# Conductor Session Streaming API Proposal ## Problem When Claude controls your real browser via CDP (gstack `$B connect`), you look at two windows: **Conductor** (to see Claude's thinking) and **Chrome** (to see Claude's actions). gstack's Chrome extension Side Panel shows browse activity — every command, result, and error. But for *full* session mirroring (Claude's thinking, tool calls, code edits), the Side Panel needs Conductor to expose the conversation stream. ## What this enables A "Session" tab in the gstack Chrome extension Side Panel that shows: - Claude's thinking/content (truncated for performance) - Tool call names + icons (Edit, Bash, Read, etc.) - Turn boundaries with cost estimates - Real-time updates as the conversation progresses The user sees everything in one place — Claude's actions in their browser + Claude's thinking in the Side Panel — without switching windows. ## Proposed API ### `GET http://127.0.0.1:{PORT}/workspace/{ID}/session/stream` Server-Sent Events endpoint that re-emits Claude Code's conversation as NDJSON events. **Event types** (reuse Claude Code's `--output-format stream-json` format): ``` event: assistant data: {"type":"assistant","content":"Let me check that page...","truncated":true} event: tool_use data: {"type":"tool_use","name":"Bash","input":"$B snapshot","truncated_input":true} event: tool_result data: {"type":"tool_result","name":"Bash","output":"[snapshot output...]","truncated_output":true} event: turn_complete data: {"type":"turn_complete","input_tokens":1234,"output_tokens":567,"cost_usd":0.02} ``` **Content truncation:** Tool inputs/outputs capped at 500 chars in the stream. Full data stays in Conductor's UI. The Side Panel is a summary view, not a replacement. ### `GET http://127.0.0.1:{PORT}/api/workspaces` Discovery endpoint listing active workspaces. ```json { "workspaces": [ { "id": "abc123", "name": "gstack", "branch": "garrytan/chrome-extension-ctrl", "directory": "/Users/garry/gstack", "pid": 12345, "active": true } ] } ``` The Chrome extension auto-selects a workspace by matching the browse server's git repo (from `/health` response) to a workspace's directory or name. ## Security - **Localhost-only.** Same trust model as Claude Code's own debug output. - **No auth required.** If Conductor wants auth, include a Bearer token in the workspace listing that the extension passes on SSE requests. - **Content truncation** is a privacy feature — long code outputs, file contents, and sensitive tool results never leave Conductor's full UI. ## What gstack builds (extension side) Already scaffolded in the Side Panel "Session" tab (currently shows placeholder). When Conductor's API is available: 1. Side Panel discovers Conductor via port probe or manual entry 2. Fetches `/api/workspaces`, matches to browse server's repo 3. Opens `EventSource` to `/workspace/{id}/session/stream` 4. Renders: assistant messages, tool names + icons, turn boundaries, cost 5. Falls back gracefully: "Connect Conductor for full session view" Estimated effort: ~200 LOC in `sidepanel.js`. ## What Conductor builds (server side) 1. SSE endpoint that re-emits Claude Code's stream-json per workspace 2. `/api/workspaces` discovery endpoint with active workspace list 3. Content truncation (500 char cap on tool inputs/outputs) Estimated effort: ~100-200 LOC if Conductor already captures the Claude Code stream internally (which it does for its own UI rendering). ## Design decisions | Decision | Choice | Rationale | |----------|--------|-----------| | Transport | SSE (not WebSocket) | Unidirectional, auto-reconnect, simpler | | Format | Claude's stream-json | Conductor already parses this; no new schema | | Discovery | HTTP endpoint (not file) | Chrome extensions can't read filesystem | | Auth | None (localhost) | Same as browse server, CDP port, Claude Code | | Truncation | 500 chars | Side Panel is ~300px wide; long content useless |