Skip to content

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

Closed as not planned

Description

@mradwankhalil

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.

Activity

  1. mradwankhalil commented on Oct 10, 2026

    @mradwankhalil
    ContributorAuthor

    This remains reproducible at current v1 source: the synthetic continuation is constructed with model: userMessage.model, so a public session.summarize request using B carries B into continuation instead of the prior working model A. Could maintainers confirm whether this API contract is intended? If not, a regression test should distinguish summary-model B from continuation-model A; the built-in auto-compact path should be tested separately.

  2. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

  3. mradwankhalil commented on Oct 23, 2026

    @mradwankhalil
    ContributorAuthor

    Thanks for clarifying the V1 triage boundary. The old report alone does not establish a V2 regression; a V2-specific reproduction would be needed before pursuing it again.

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

Metadata

Metadata

Assignees

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