Saturday, 2 August 2026

Session History

Rojo bug fixes (local mode detection, creation, system prompt). Claude API provider and Claude Code subprocess provider for rojo-chat-panel.

What we built

Rojo local-mode bug fixes (three)

All three bugs shared the same root cause: rojo-chat-panel only checked editor.projectId, silently ignoring localRoot and remoteProject modes.

  • Detection_loadRojos() now uses a three-way branch matching Editor.ts's three mode fields, hitting /api/local/rojos?root=… for local projects.
  • Creation_createRojo() received the same fix, posting to /api/local/rojos/create with { root, parentDir }.
  • System prompt_send() was sending body.projectId = '' (falsy), so the server skipped reading the rojo file. Fixed by sending localRoot in the body and branching server-side with a path-containment security check.

Claude API provider (provider: "claude")

Installed @anthropic-ai/sdk and added a second LLM path to RojosAgent.ts:

  • AgentConfig extended with optional provider?: "openai" | "claude" | "claude-code" and baseURL made optional.
  • Separate claudeSessions map (Anthropic.MessageParam[]) keeps conversation history independently of the LangChain OpenAI sessions.
  • Streaming via client.messages.stream(); text deltas yielded in real-time. After iteration, stream.finalMessage() is used to collect tool use blocks for the agentic loop.
  • OpenAI-format tools converted to Claude format by toClaudeTool() (wraps parameters as input_schema).
  • Route rojos.ts: ep.type === "claude" branch sets config.provider, defaults model to claude-opus-4-8.

Claude Code subprocess provider (provider: "claude-code")

Adds a third provider path that spawns the local claude.exe binary (already installed via @anthropic-ai/claude-code) as a subprocess. No API key needed — reuses the existing Claude Code login from the system keychain.

  • CLI flags: --print --output-format stream-json --verbose --dangerously-skip-permissions.
  • First message uses --session-id <conversationId> to create a persistent session; subsequent messages use --resume <conversationId> to continue it — full multi-turn conversation.
  • System prompt passed via --append-system-prompt to preserve Claude Code's own built-in instructions.
  • spawnJsonLines() is an async generator backed by an event queue; stdout is parsed line-by-line as newline-delimited JSON. type === "assistant" events are converted to AgentEvent.
  • Binary located at runtime via findClaudeBin() (checks node_modules/@anthropic-ai/claude-code/bin/claude.exe first, falls back to .bin/claude.cmd).

Three bugs fixed during initial testing:

  • Missing --verbose — required by the CLI when stream-json output format is used; without it the process exits with an error immediately.
  • Stdin not closed — process waited 3 s for piped input before starting. Fixed by stdio: ["ignore", "pipe", "pipe"].
  • shell: true on Windows — cmd.exe mangled the user message (split on spaces, ? treated as wildcard), so Claude received only a fragment. Fixed by shell: false with the direct .exe path, letting Node.js call CreateProcess() directly.

Rojo settings panel: claude and claude-code endpoint types

Two new options added to the endpoint type selector:

  • Claude API (Anthropic) — shows the API key field; hides the Base URL row (not needed). Model field stays visible with a <datalist> suggesting claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5.
  • Claude Code (local, no key) — hides the external section and model row entirely; shows a brief info note explaining that the local Claude Code login is used.

_updateEndpointVisibility() controls all show/hide logic including the .rsp-url-row sub-element inside the external section.

Key decisions

Separate session stores per provider. The OpenAI path keeps BaseMessage[] (LangChain); the Claude API path keeps Anthropic.MessageParam[]; the Claude Code path keeps only a Set<string> of initialised session IDs (history lives in the claude subprocess's own storage). Mixing them would require format translation on every turn.

--append-system-prompt instead of --system-prompt. --system-prompt replaces Claude Code's entire built-in prompt, which would break the subprocess's tool-use instructions. --append-system-prompt adds the rojo's persona on top without disturbing the base behaviour.

shell: false with direct .exe path on Windows. Using shell: true routes through cmd.exe, which interprets spaces as argument separators and ? as a wildcard — making multi-word user messages arrive garbled. Direct process creation passes each argv element as a properly quoted string.

Claude Code as a no-key Claude backend. Because the subprocess inherits the OS user's existing Claude Code login (keychain / ~/.claude/), 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.