178 lines
8.7 KiB
HTML
178 lines
8.7 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>Saturday, 2 August 2026 — Roject</title>
|
||
|
|
<link rel="stylesheet" href="../../../../_assets_/styles.css">
|
||
|
|
<link rel="stylesheet" href="../../../../_assets_/nav.css">
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<div class="page">
|
||
|
|
|
||
|
|
<header>
|
||
|
|
<p class="date">Saturday, 2 August 2026</p>
|
||
|
|
<h1>Session History</h1>
|
||
|
|
<p class="subtitle">Rojo bug fixes (local mode detection, creation, system prompt). Claude API provider and Claude Code subprocess provider for rojo-chat-panel.</p>
|
||
|
|
</header>
|
||
|
|
|
||
|
|
<section>
|
||
|
|
<h2>What we built</h2>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Rojo local-mode bug fixes (three)</h3>
|
||
|
|
<p>
|
||
|
|
All three bugs shared the same root cause: <code>rojo-chat-panel</code> only checked
|
||
|
|
<code>editor.projectId</code>, silently ignoring <code>localRoot</code> and
|
||
|
|
<code>remoteProject</code> modes.
|
||
|
|
</p>
|
||
|
|
<ul style="margin-top:0.5rem;line-height:1.9">
|
||
|
|
<li><strong>Detection</strong> — <code>_loadRojos()</code> now uses a three-way branch
|
||
|
|
matching <code>Editor.ts</code>'s three mode fields, hitting
|
||
|
|
<code>/api/local/rojos?root=…</code> for local projects.</li>
|
||
|
|
<li><strong>Creation</strong> — <code>_createRojo()</code> received the same fix,
|
||
|
|
posting to <code>/api/local/rojos/create</code> with <code>{ root, parentDir }</code>.</li>
|
||
|
|
<li><strong>System prompt</strong> — <code>_send()</code> was sending
|
||
|
|
<code>body.projectId = ''</code> (falsy), so the server skipped reading the rojo file.
|
||
|
|
Fixed by sending <code>localRoot</code> in the body and branching server-side
|
||
|
|
with a path-containment security check.</li>
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Claude API provider (<code>provider: "claude"</code>)</h3>
|
||
|
|
<p>
|
||
|
|
Installed <code>@anthropic-ai/sdk</code> and added a second LLM path to
|
||
|
|
<code>RojosAgent.ts</code>:
|
||
|
|
</p>
|
||
|
|
<ul style="margin-top:0.5rem;line-height:1.9">
|
||
|
|
<li><code>AgentConfig</code> extended with optional <code>provider?: "openai" | "claude" | "claude-code"</code>
|
||
|
|
and <code>baseURL</code> made optional.</li>
|
||
|
|
<li>Separate <code>claudeSessions</code> map (<code>Anthropic.MessageParam[]</code>)
|
||
|
|
keeps conversation history independently of the LangChain OpenAI sessions.</li>
|
||
|
|
<li>Streaming via <code>client.messages.stream()</code>; text deltas yielded in
|
||
|
|
real-time. After iteration, <code>stream.finalMessage()</code> is used to collect
|
||
|
|
tool use blocks for the agentic loop.</li>
|
||
|
|
<li>OpenAI-format tools converted to Claude format by <code>toClaudeTool()</code>
|
||
|
|
(wraps <code>parameters</code> as <code>input_schema</code>).</li>
|
||
|
|
<li>Route <code>rojos.ts</code>: <code>ep.type === "claude"</code> branch sets
|
||
|
|
<code>config.provider</code>, defaults model to <code>claude-opus-4-8</code>.</li>
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Claude Code subprocess provider (<code>provider: "claude-code"</code>)</h3>
|
||
|
|
<p>
|
||
|
|
Adds a third provider path that spawns the local <code>claude.exe</code> binary
|
||
|
|
(already installed via <code>@anthropic-ai/claude-code</code>) as a subprocess.
|
||
|
|
No API key needed — reuses the existing Claude Code login from the system keychain.
|
||
|
|
</p>
|
||
|
|
<ul style="margin-top:0.5rem;line-height:1.9">
|
||
|
|
<li>CLI flags: <code>--print --output-format stream-json --verbose
|
||
|
|
--dangerously-skip-permissions</code>.</li>
|
||
|
|
<li>First message uses <code>--session-id <conversationId></code> to create a
|
||
|
|
persistent session; subsequent messages use <code>--resume <conversationId></code>
|
||
|
|
to continue it — full multi-turn conversation.</li>
|
||
|
|
<li>System prompt passed via <code>--append-system-prompt</code> to preserve Claude
|
||
|
|
Code's own built-in instructions.</li>
|
||
|
|
<li><code>spawnJsonLines()</code> is an async generator backed by an event queue;
|
||
|
|
stdout is parsed line-by-line as newline-delimited JSON.
|
||
|
|
<code>type === "assistant"</code> events are converted to <code>AgentEvent</code>.</li>
|
||
|
|
<li>Binary located at runtime via <code>findClaudeBin()</code> (checks
|
||
|
|
<code>node_modules/@anthropic-ai/claude-code/bin/claude.exe</code> first,
|
||
|
|
falls back to <code>.bin/claude.cmd</code>).</li>
|
||
|
|
</ul>
|
||
|
|
<p style="margin-top:0.75rem">
|
||
|
|
Three bugs fixed during initial testing:
|
||
|
|
</p>
|
||
|
|
<ul style="margin-top:0.5rem;line-height:1.9">
|
||
|
|
<li><strong>Missing <code>--verbose</code></strong> — required by the CLI when
|
||
|
|
<code>stream-json</code> output format is used; without it the process exits with
|
||
|
|
an error immediately.</li>
|
||
|
|
<li><strong>Stdin not closed</strong> — process waited 3 s for piped input before
|
||
|
|
starting. Fixed by <code>stdio: ["ignore", "pipe", "pipe"]</code>.</li>
|
||
|
|
<li><strong><code>shell: true</code> on Windows</strong> — cmd.exe mangled the user
|
||
|
|
message (split on spaces, <code>?</code> treated as wildcard), so Claude received
|
||
|
|
only a fragment. Fixed by <code>shell: false</code> with the direct <code>.exe</code>
|
||
|
|
path, letting Node.js call <code>CreateProcess()</code> directly.</li>
|
||
|
|
</ul>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<h3>Rojo settings panel: <code>claude</code> and <code>claude-code</code> endpoint types</h3>
|
||
|
|
<p>
|
||
|
|
Two new options added to the endpoint type selector:
|
||
|
|
</p>
|
||
|
|
<ul style="margin-top:0.5rem;line-height:1.9">
|
||
|
|
<li><strong>Claude API (Anthropic)</strong> — shows the API key field; hides the Base URL
|
||
|
|
row (not needed). Model field stays visible with a <code><datalist></code>
|
||
|
|
suggesting <code>claude-opus-4-8</code>, <code>claude-sonnet-4-6</code>,
|
||
|
|
<code>claude-haiku-4-5</code>.</li>
|
||
|
|
<li><strong>Claude Code (local, no key)</strong> — hides the external section and model
|
||
|
|
row entirely; shows a brief info note explaining that the local Claude Code login
|
||
|
|
is used.</li>
|
||
|
|
</ul>
|
||
|
|
<p style="margin-top:0.75rem">
|
||
|
|
<code>_updateEndpointVisibility()</code> controls all show/hide logic including the
|
||
|
|
<code>.rsp-url-row</code> sub-element inside the external section.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section>
|
||
|
|
<h2>Key decisions</h2>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<p>
|
||
|
|
<strong>Separate session stores per provider.</strong>
|
||
|
|
The OpenAI path keeps <code>BaseMessage[]</code> (LangChain); the Claude API path
|
||
|
|
keeps <code>Anthropic.MessageParam[]</code>; the Claude Code path keeps only a
|
||
|
|
<code>Set<string></code> of initialised session IDs (history lives in the
|
||
|
|
claude subprocess's own storage). Mixing them would require format translation on
|
||
|
|
every turn.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<p>
|
||
|
|
<strong><code>--append-system-prompt</code> instead of <code>--system-prompt</code>.</strong>
|
||
|
|
<code>--system-prompt</code> replaces Claude Code's entire built-in prompt, which
|
||
|
|
would break the subprocess's tool-use instructions. <code>--append-system-prompt</code>
|
||
|
|
adds the rojo's persona on top without disturbing the base behaviour.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<p>
|
||
|
|
<strong><code>shell: false</code> with direct <code>.exe</code> path on Windows.</strong>
|
||
|
|
Using <code>shell: true</code> routes through <code>cmd.exe</code>, which interprets
|
||
|
|
spaces as argument separators and <code>?</code> as a wildcard — making multi-word
|
||
|
|
user messages arrive garbled. Direct process creation passes each argv element as a
|
||
|
|
properly quoted string.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="card">
|
||
|
|
<p>
|
||
|
|
<strong>Claude Code as a no-key Claude backend.</strong>
|
||
|
|
Because the subprocess inherits the OS user's existing Claude Code login (keychain /
|
||
|
|
<code>~/.claude/</code>), no Anthropic API key is required in the rojo settings.
|
||
|
|
The Claude Code subprocess can also use its own full tool set (Read, Bash, Grep, etc.),
|
||
|
|
making it more capable than the pure API path for code-aware tasks.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<footer>
|
||
|
|
Roject — session history
|
||
|
|
</footer>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
<script>var NAV_ROOT = '../../../../';</script>
|
||
|
|
<script src="../../../../_assets_/nav-data.js"></script>
|
||
|
|
<script src="../../../../_assets_/nav.js"></script>
|
||
|
|
</body>
|
||
|
|
</html>
|