anomalyco / anomalyco/opencode

session.summarize with a non-session model makes the synthetic post-compaction continue (and the following turns) run on that model

Open
#49,386 0 comments 0 reactions 1 assignee View on GitHub

@jlongster is already working on this.

Since Sep 16, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

What happens

session.summarize accepts a model for the compaction request. That model is stored on the synthetic compaction trigger message, and OpenCode's post-compaction autocontinue then reuses it for the synthetic continue turn:

packages/opencode/src/session/compaction.ts (v1.18.31, lines ~497–548):

const continueMsg = yield* session.updateMessage({
  id: MessageID.ascending(),
  role: "user",
  sessionID: input.sessionID,
  time: { created: Date.now() },
  agent: userMessage.agent,
  model: userMessage.model,   // <-- the summarize request's model
})
const text = /* ... */ "Continue if you have next steps, or stop and ask for clarification if you are unsure how to proceed."

and SessionCompaction.create (same file, ~559–582) stores the summarize request's model on the trigger message. So when a caller summarizes with a model different from the session's working model, every turn after the auto-continue runs on the summarize model instead of the model the user/session is actually using — and the session only returns to the working model when the next real user message arrives.

This is not reachable through the built-in auto-compact path (which passes lastUser.model), but it is reachable through the public session.summarize API/SDK, which is exactly what orchestration plugins use to summarize on a cheaper/faster model while the session keeps running on its selected model.

Reproduction

  1. Session on model A (providerID/modelID of your choice), a few turns of context.
  2. POST /session/{id}/summarize (or SDK client.session.summarize) with { providerID, modelID } of model B ≠ A, auto: true.
  3. Observe the recorded messages:
role model notes
user B trigger message, carries a compaction part with auto: true
assistant B summary, agent: "compaction", summary: true — expected
user B synthetic continue: "Continue if you have next steps, or stop and ask for clarification if you are unsure how to proceed."
assistant ×N B the task continuation runs on B
user (next real prompt) A session returns to its working model

Message rows from a live session (opencode.db) showing exactly this (B = zai-coding-plan/glm-5.3-flash, A = opencode-go/deepseek-v4.1-flash), 2026-09-16:

17:23:18 user      zai-coding-plan/glm-5.3-flash   [part: compaction, auto:true]
17:24:20 assistant zai-coding-plan/glm-5.3-flash   agent=compaction summary=true
17:31:38 user      zai-coding-plan/glm-5.3-flash   "Continue if you have next steps, ..."
17:31:38 assistant zai-coding-plan/glm-5.3-flash   agent=<working agent>   <- wrong model
17:33:25 assistant zai-coding-plan/glm-5.3-flash   agent=<working agent>
17:36:11 user      opencode-go/deepseek-v4.1-flash (next real prompt — everything after is back on A)

Expected

The synthetic continue turn (and the turns it triggers) should run on the session's working model — i.e. the model of the last non-compaction user/assistant message — regardless of which model was used to produce the summary. Summaries can legitimately run on a different model; continuations should not silently switch the session's model.

Suggested fix

Resolve the continue message's model from the last non-compaction user message instead of the trigger message, e.g.:

const continueModel = lastNonCompactionUserMessage?.model ?? userMessage.model

or pass the working model explicitly through SessionCompaction.create/processCompaction separately from the summarize model.

Note on the plugin workaround (why the hook is not enough)

The experimental.compaction.autocontinue plugin hook can only enable/disable the synthetic continue — the payload contains the model but there is no way to correct it. Downstream plugins therefore have to disable the synthetic continue and re-issue the continue prompt themselves via session.promptAsync with the working model. That workaround has been implemented for the oh-my-openagent plugin in code-yeongyu/oh-my-openagent#8397; a core-side fix would make the workaround unnecessary and would also cover every non-plugin caller of session.summarize.

Environment: opencode-ai 1.18.31 (Windows, x64); source references are from tag v1.18.31.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.