Summary
For any turn that resumes a Claude Code session, the plugin ignores the incoming OpenCode messages array except for the latest user message — history comes from the Claude-side session transcript that resume points at. As a result, any OpenCode plugin that manipulates the conversation through experimental.chat.messages.transform has no effect on claude-code/* models: the transform runs, the host believes the context changed, and the model still sees the untouched Claude-side transcript.
Concrete case: @tarquinen/opencode-dcp (dynamic context pruning) 3.1.15. It prunes and compresses tool outputs and history in that hook. On every other provider this reduces what the model sees; on claude-code it is a no-op — while DCP's own accounting and notifications still report the pruning as done. Users read this as "DCP is broken on claude-code".
Where
// dist/proxy.js:307
const prompt = latestUserPrompt(messages);
// dist/proxy.js:361,377-387
let resume = getForeignSessionId(conversationKey);
const transcript = resume ? "" : buildConversationTranscript(priorMessagesOf(messages));
const contextualPrompt = withConversationContext(prompt, transcript);
The host array is consulted only when resume is absent — first claude-code turn of a chat, a model switch, or a missing session file. From turn 2 onward, whatever a host-side transform did is discarded.
Why it's worth addressing
- The turn-2 cliff makes it non-obvious: the same session behaves differently on its first turn than on all the rest, so users see such features "sometimes work".
- Being an OpenCode provider carries the expectation that host-side context management applies. opencode-claude silently opts out of all of it, and nothing in the logs or the UI says so.
- The effect isn't cosmetic: context plugins exist to keep long sessions inside the window. Under opencode-claude the only thing doing that is CC's own autoCompact (
autoCompactEnabled, proxy.js:456), and the user's configured strategy is inert.
Possible directions
- An opt-in "host owns the transcript" mode: skip
resume and rebuild the prompt from the (transformed) host array every turn. More expensive, but it makes the plugin behave like a normal provider.
- Detect that the incoming array diverges from what was already sent (messages dropped or replaced since the last turn) and rebuild instead of resuming.
- At minimum: document the behavior, and log a warning once per session when the incoming array is smaller than what has already been sent, so plugin authors and users can see why their transform did nothing.
Note: I'm aware system messages are dropped deliberately (prompt.js:serializeHistoryMessage documents why) — not asking to change that. Mentioning it only because it means experimental.chat.system.transform is inert here for the same reason.
Happy to test any of these on Windows.
Environment
@openchamber/opencode-claude 0.14.0
- OpenCode 1.18.21
@tarquinen/opencode-dcp 3.1.15
- Claude Code CLI 2.1.239,
@anthropic-ai/claude-agent-sdk 0.3.237
- Windows 11
Summary
For any turn that resumes a Claude Code session, the plugin ignores the incoming OpenCode
messagesarray except for the latest user message — history comes from the Claude-side session transcript thatresumepoints at. As a result, any OpenCode plugin that manipulates the conversation throughexperimental.chat.messages.transformhas no effect onclaude-code/*models: the transform runs, the host believes the context changed, and the model still sees the untouched Claude-side transcript.Concrete case:
@tarquinen/opencode-dcp(dynamic context pruning) 3.1.15. It prunes and compresses tool outputs and history in that hook. On every other provider this reduces what the model sees; on claude-code it is a no-op — while DCP's own accounting and notifications still report the pruning as done. Users read this as "DCP is broken on claude-code".Where
The host array is consulted only when
resumeis absent — first claude-code turn of a chat, a model switch, or a missing session file. From turn 2 onward, whatever a host-side transform did is discarded.Why it's worth addressing
autoCompactEnabled,proxy.js:456), and the user's configured strategy is inert.Possible directions
resumeand rebuild the prompt from the (transformed) host array every turn. More expensive, but it makes the plugin behave like a normal provider.Note: I'm aware
systemmessages are dropped deliberately (prompt.js:serializeHistoryMessagedocuments why) — not asking to change that. Mentioning it only because it meansexperimental.chat.system.transformis inert here for the same reason.Happy to test any of these on Windows.
Environment
@openchamber/opencode-claude0.14.0@tarquinen/opencode-dcp3.1.15@anthropic-ai/claude-agent-sdk0.3.237