Skip to content

core: compaction trusts a measured prompt from a different model behind the same provider #61372

Open

Description

@pjiang000-eng

Description

estimatePrompt chooses its measured anchor with hasMeasuredPrompt, which only requires the provider to match (packages/core/src/session/compaction.ts:1164-1169). measuredPrompt in the same file additionally requires model.id, variant and agent to match, and says why: "they shape the request" (:1171-1181).

due — the decision whether automatic compaction runs — sizes the context through estimatePrompt / estimateContext (:422-430, :1101). So after switching model within one provider it still treats the previous model's token count as the size of the current prompt, even though measuredPrompt refuses that same anchor. The two paths disagree about one number: due compares a stale count against the new model's ceiling, which delays compaction until the provider rejects the request, or fires it early and throws away the prompt cache for nothing.

Steps to reproduce

In packages/core/test/session-compaction.test.ts, beside the existing "Another provider's usage is not trusted either" assertion (:493), swap the model id instead of the provider:

const swapped = { ...assistant, model: { ...assistant.model, id: Model.ID.make("other-model") } }
expect(SessionCompaction.estimateContext({ ...grown, messages: [swapped] })).toBe(21)
// Expected: 21, Received: 89000
cd packages/core && bun run script/test.ts test/session-compaction.test.ts -t "auto compaction estimates current content"

Plugins

none

OpenCode version

dev

Screenshot

No response

Activity

  1. opencodejr commented on Nov 3, 2026

    @opencodejr
    Contributor

    Thanks for the precise report. I reproduced this on the latest dev (3ba597f). With your assertion added next to the other-provider case in session-compaction.test.ts, SessionCompaction.estimateContext returns 89000 instead of 21. So a token count measured for the previous model behind the same provider is still used as the current prompt size. due decides automatic compaction on that estimate, while measuredPrompt refuses the same count because the model differs.

    The script below adds the assertion, runs the test and reverts the file.

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Reproduces #61372 against an opencode checkout (default /workspace/repo, dev branch, deps installed).
    set -euo pipefail
    REPO="${1:-/workspace/repo}"
    cd "$REPO/packages/core"
    python3 - <<'PY'
    p = "test/session-compaction.test.ts"
    s = open(p).read()
    a = '    expect(SessionCompaction.estimateContext({ ...grown, messages: [foreign] })).toBe(21)\n'
    b = a + ('    const swapped = { ...assistant, model: { ...assistant.model, id: Model.ID.make("other-model") } }\n'
             '    expect(SessionCompaction.estimateContext({ ...grown, messages: [swapped] })).toBe(21)\n')
    assert a in s, "anchor assertion not found"
    if "other-model" not in s:
        open(p, "w").write(s.replace(a, b))
    PY
    trap 'git checkout -- test/session-compaction.test.ts' EXIT
    # Expected: 21 (same as the other-provider case); actual: 89000 (previous model's count reused)
    bun run script/test.ts test/session-compaction.test.ts -t "auto compaction estimates current content"

    Run it: bash repro.sh

  2. jimmyallinai commented on Nov 3, 2026

    @jimmyallinai
    Contributor

    I'll take a look at this one. Plan: first reproduce with the swapped-model assertion in packages/core/test/session-compaction.test.ts, then align the estimatePrompt anchor check with the stricter measuredPrompt criteria (model id, variant, agent) so a count measured for a different model behind the same provider is no longer trusted, and run the session-compaction tests plus the neighbouring core tests and typecheck before opening a PR.

  3. r266-tech commented on Nov 3, 2026

    @r266-tech
    Contributor

    I have an implementation in #61379 and updated it to match usage by provider, model, variant, and agent, including a native-window regression. This covers the anchor cases from your plan, so we can avoid parallel implementation. The visible-text estimate may still undercount dense content until the provider reports overflow.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions