route OpenCode chat through the officer-opencode sidecar
Turns now run in the sidecar via `opencode run --dir <cwd> --format json
--dangerously-skip-permissions [-s <ses_>]` instead of the serve's
`POST /session/{id}/message` path. That path was unreliable at reporting
tool completion — tools finished but the turn stayed status=running,
wedging the UI at "Working…". `run` re-anchors tools to the chat cwd via
--dir, reports completion faithfully, and exits when done.
- runner.ts (new): spawn `run`, map its JSON events (text/tool_use/
step_finish) to ChatEvent, report the `ses_` id for resume, accumulate
cost; inactivity (120s) + hard-cap (10min) watchdogs kill a hung turn
and emit a clean error instead of hanging forever.
- protocol.ts: opencode:run-streaming/kill commands; opencode:spawned/
event/session events; OpenCodeRunParams.
- sidecar index.ts: wire run/kill; sweepStaleServes() on startup kills
only an `opencode serve` whose resolved /proc/<pid>/cwd == SERVE_CWD,
so an unclean prior exit can't leave two.
- sidecar-registry.ts: spawnOpenCodeStreaming/killOpenCode/onOpenCodeEvent/
onOpenCodeSession helpers.
- send-opencode.ts: rewritten to mirror send-claude-code (subscribe →
resolve resume id → spawn → kill handle).
- sidecar-server.ts: persist reported ses_ id into state for resume.
- list-models/server-manager: route to the sidecar's reported serve URL.
The serve stays up only for read-only calls that never hung (model
listing, session history).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,9 @@ export type SidecarCommand =
|
||||
| { type: 'claude:spawn-streaming'; id: string; params: ClaudeSpawnStreamingParams }
|
||||
| { type: 'claude:kill'; id: string; sessionKey: string }
|
||||
| { type: 'claude:clear-session'; id: string; sessionKey: string }
|
||||
// OpenCode — drive a turn via `opencode run … --format json` (tools re-anchored to cwd via --dir)
|
||||
| { type: 'opencode:run-streaming'; id: string; params: OpenCodeRunParams }
|
||||
| { type: 'opencode:kill'; id: string; sessionKey: string }
|
||||
// VNC
|
||||
| { type: 'vnc:start'; id: string; params: VncStartParams }
|
||||
| { type: 'vnc:stop'; id: string; email: string }
|
||||
@@ -43,6 +46,11 @@ export type SidecarEvent =
|
||||
| { type: 'email:new'; userEmail: string }
|
||||
// OpenCode — the sidecar reports where its `opencode serve` is listening (random port) on connect
|
||||
| { type: 'opencode:server'; port: number }
|
||||
// OpenCode turn streaming (analog of claude:*): spawned ack, per-event stream, session id report
|
||||
| { type: 'opencode:spawned'; id: string; sessionKey: string }
|
||||
| { type: 'opencode:event'; sessionKey: string; event: ChatEvent }
|
||||
| { type: 'opencode:session'; sessionKey: string; sessionId: string }
|
||||
| { type: 'opencode:error'; id: string; error: string }
|
||||
// Generic
|
||||
| { type: 'error'; id?: string; error: string };
|
||||
|
||||
@@ -85,6 +93,16 @@ export type ClaudeCodeResult = {
|
||||
cost: MessageCost;
|
||||
};
|
||||
|
||||
// ── OpenCode turn params ──
|
||||
|
||||
export type OpenCodeRunParams = {
|
||||
sessionKey: string;
|
||||
prompt: string;
|
||||
cwd?: string; // passed to `opencode run --dir` — hard-re-anchors tools to this directory
|
||||
model?: string; // `providerID/modelID` (e.g. opencode/claude-haiku-4-5); passed to --model verbatim
|
||||
resumeSessionId?: string; // OpenCode `ses_…` id to continue (`--session`)
|
||||
};
|
||||
|
||||
// ── VNC types ──
|
||||
|
||||
export type VncStartParams = {
|
||||
|
||||
Reference in New Issue
Block a user