From 492509ae5212ca7e56c8490847ec9801d2f954fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Mon, 10 Aug 2026 02:22:47 +0000 Subject: [PATCH] route a resumed opencode session to opencode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resuming an OpenCode conversation dispatched it to the Claude CLI: a `ses_…` id handed to `claude --resume`. Wrong harness, not degraded output. The whole six-hop chain, confirmed rather than inferred, because the endpoints alone do not show which hop drops the value: /chat/:id fetches detail and sets `selected.model` = `opencode/big-pickle` ← the value exists ChatDetailPanel renders without a `model` prop ← dropped here NewChat reads `initialModel={locationState?.model}` ← unrelated source nothing in the tree ever writes `location.state.model` ← so always undefined useChat therefore holds no model and the socket sends none websocket.ts falls back to the user default, `isClaudeModel` is true So the model was resolved correctly at the top and read from somewhere else at the bottom. `selected` has carried `model` all along. `locationState.model` stays as a fallback rather than being deleted: it is declared on ChatLocationState and costs nothing to keep for a caller that navigates with one deliberately. docs/opencode-parity.md B2, which flagged this as the one to verify hop by hop. Its account is accurate; the added detail is that the value is produced and then dropped, not never produced. Co-Authored-By: Claude Opus 5 --- .../src/apps/ChatHistory/ChatDetailPanel.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/workspaces/officerdev/src/apps/ChatHistory/ChatDetailPanel.tsx b/src/workspaces/officerdev/src/apps/ChatHistory/ChatDetailPanel.tsx index 791257fc..105a447b 100644 --- a/src/workspaces/officerdev/src/apps/ChatHistory/ChatDetailPanel.tsx +++ b/src/workspaces/officerdev/src/apps/ChatHistory/ChatDetailPanel.tsx @@ -123,6 +123,15 @@ function DetailBar({ sessionTitle, cwd, partCount, isConnected, isGenerating, on } type NewChatProps = { + /** + * The harness that owns this conversation, as `harness/model` — e.g. `opencode/big-pickle`. + * + * Load-bearing on resume, not cosmetic. The server routes by model: anything starting `claude-code` + * goes to the Claude sidecar, everything else to OpenCode. Send nothing and it falls back to the + * user's default, which is how a `ses_…` id ended up being handed to `claude --resume` — a + * wrong-harness dispatch rather than a degradation. + */ + model?: string | null; resumeSummary?: string; resumeSessionId?: string; initialMessages?: ChatMessage[]; @@ -191,7 +200,10 @@ function NewChat(props: NewChatProps) { { initialMessages={selected.initialMessages} total={selected.total} initialOffset={selected.initialOffset} + model={selected.model} sessionCwd={selected.cwd} sessionTitle={title} partCount={selected.partCount}