Skip to content

opencode-claude ignores the host messages array when resuming, silently disabling OpenCode context plugins (e.g. DCP pruning) #5

Description

@ToppowerA

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

  1. 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.
  2. Detect that the incoming array diverges from what was already sent (messages dropped or replaced since the last turn) and rebuild instead of resuming.
  3. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions