phase D: delete the subprocess path

The serve is now the only way an opencode turn runs. runner.ts and its tests are gone, and
so is the OPENCODE_TURNS switch — there is no fallback engine any more, and the recovery for
a bad day is git rather than a config flag. Deliberate, and cheap right now precisely because
nothing depends on opencode yet.

What goes with it: mapRunLine and its NDJSON fixtures, the temp-file spill for --file image
attachments, the supersede-and-kill dance, the process watchdogs, the pidfile-adjacent child
tracking, and stopAllOpenCodeTurns. All of it existed to work around stdin being /dev/null.

Verified after deletion, with no env var set at all: tool call, tool result, 5 streaming
deltas, text and cost, through the real chat socket.

Also corrected the comments the deletion falsified rather than leaving them to mislead — the
module header, the wire contract description of opencode:run-streaming, and serve-runner own
header, which still announced itself as off by default.

One difference worth stating: shutdown no longer kills anything. Turns run inside the serve,
which is a separate process that survives us, so officer stops routing them and says so in
the transcript. When a subprocess ran the turn, failing to kill it orphaned it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 19:33:58 +01:00
co-authored by Claude Opus 5
parent b79eca45fb
commit a3dbda7d3b
5 changed files with 44 additions and 818 deletions
+5 -4
View File
@@ -26,8 +26,8 @@ export type LiveClaudeSession = {
};
/**
* An OpenCode turn in flight. Only ever the generating ones — see `listRunningOpenCodeTurns` for why
* this carries neither `isGenerating` (it is always true) nor `pendingTasks` (no such concept).
* An OpenCode turn in flight. Only ever the generating ones, which is why it carries neither
* `isGenerating` (always true) nor `pendingTasks` (no such concept on this harness).
*
* It deliberately carries no id for OpenCode's own `ses_…` session, and does not need to: the sidecar
* only ever knows its own `sessionKey`, while the `ses_…` is reported separately over `opencode:session`
@@ -63,12 +63,13 @@ export type SidecarCommand =
// records are in memory and die with `pm2 restart officer`, while the agent keeps running. Without it
// a live session is invisible until a browser happens to reconnect to it by id.
| { type: 'claude:list'; id: string }
// OpenCode — drive a turn via `opencode run … --format json` (tools re-anchored to cwd via --dir)
// OpenCode — drive a turn through the serve (POST /api/session/{id}/prompt), anchored to the chat cwd
// by a per-request location header. Was an `opencode run` subprocess until 2026-08-10.
| { type: 'opencode:run-streaming'; id: string; params: OpenCodeRunParams }
| { type: 'opencode:kill'; id: string; sessionKey: string }
// Which OpenCode turns are running right now. The counterpart of `claude:list`, and thinner for a
// reason: OpenCode has no warm session between turns, so there is nothing to report but the running
// ones. See `listRunningOpenCodeTurns`.
// ones — see `listRunningServeTurns`.
| { type: 'opencode:list'; id: string }
// VNC
| { type: 'vnc:start'; id: string; params: VncStartParams }