Skip to content

[Bug]: ACP adapter re-sends an unchanged model, which resets Copilot's 1M context tier #27022

Closed as not planned

Description

@uzairansaruzi

Note

🤖 Claude Opus 5.5 responding on behalf of uzairansaruzi

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Add GitHub Copilot through the ACP Registry (agent github-copilot-cli).
  2. Start Copilot on a long-context-eligible model with the 1M tier: copilot --acp --model claude-opus-5.5 --context long_context, or set "contextTier": "long_context" in ~/.copilot/settings.json.
  3. Start a T3 Code thread on claude-opus-5.5 and send a message.
  4. Read the session journal at ~/.copilot/session-state/<session-id>/events.jsonl.

Expected behavior

The model the thread wants is already the session's current model, so T3 Code leaves it alone and the session keeps the tier Copilot started with.

Actual behavior

T3 Code sends session/set_config_option {configId: "model", value: "claude-opus-5.5"} even though currentValue is already claude-opus-5.5. Copilot treats every model change as clearing the context tier, so the session drops to the 200K default.

On the config-option path, the adapter sets the model without checking the current value:

const modelOption = findModelConfigOption(yield* runtime.getConfigOptions);
const modelState = startResult.sessionSetupResult.models;
if (modelOption?.type === "select") {
if (
isCustomModel ||
modelOption.options.some((entry) =>
"value" in entry
? entry.value === requestedModel
: entry.options.some((choice) => choice.value === requestedModel),
)
) {
yield* runtime
.setConfigOption(modelOption.id, requestedModel)
.pipe(Effect.mapError(rejectedModel));
appliedModel = requestedModel;
}

The session/set_model path just below skips a model that already matches:

if (modelState.currentModelId !== requestedModel) {
yield* runtime.setSessionModel(requestedModel).pipe(Effect.mapError(rejectedModel));
}

The real fix belongs in Copilot CLI (github/copilot-cli#4275). Its ACP model switch drops contextTier, and the tier isn't offered as a config option. Skipping a model set that changes nothing still matters on our side: it is the only thing resetting the tier when the thread model matches Copilot's startup model. It also brings the two paths in line.

Once Copilot advertises a context-tier config option, T3 Code should show it with no changes (sessionConfig.ts passes through everything outside the model and collaboration_mode categories).

Impact

Major degradation or frequent failure

Version or commit

0.1.4-nightly.20261028.3841 (code references at main @ 960d576)

Environment

macOS 26, GitHub Copilot CLI 1.0.104 over ACP, claude-opus-5.5

Logs or stack traces

# Copilot started with --model claude-opus-5.5 --context long_context, then T3 Code's model set
1 session.start         {"selectedModel":"claude-opus-5.5","contextTier":"long_context"}
2 session.model_change  {"newModel":"claude-opus-5.5","previousModel":"claude-opus-5.5","contextTier":null,"source":"sdk"}
6 session.usage_record  maxPromptTokens: 200000

# Every recent T3 Code Copilot session (Copilot 1.0.97 to 1.0.104) shows the same pattern
2 session.model_change  {"newModel":"claude-opus-5.5","contextTier":null,"source":"sdk"}
8 session.usage_record  maxPromptTokens: 200000

Workaround

None in T3 Code today. The launch flag and the settings.json tier are both cleared by the model set. Over ACP, /model -s <id> takes no tier argument and doesn't restore the saved tier. The interactive copilot CLI is the only way to get the 1M window.

Activity

  1. juliusmarminge commented on Oct 29, 2026

    @juliusmarminge
    Member

    Thanks for the detailed report. I tried to reproduce this on current main (960d576) and couldn't get T3 Code to re-send an unchanged model.

    The adapter line you linked calls runtime.setConfigOption, which returns early without sending anything when the cached currentValue already equals the requested value: AcpSessionRuntime.ts#L2012. That guard has been in place since ACP support first landed.

    I ran the adapter against the mock ACP agent, with the thread model equal to the session's current model, and recorded every outgoing request:

    • Session open: initialize and session/new only. No model set.
    • Turn on the same model: session/prompt only.
    • Turn on a different model (control): session/set_config_option model=composer-2, then session/prompt.
    • A second turn on that model: session/prompt only.

    The test patch is here and its output is here.

    So the model_change in your journal comes from a request whose value did not match what T3 Code had cached for Copilot's model option. That can happen when Copilot's session/new reports a different currentValue, or a model option shaped differently than expected, or when the change comes from another request entirely. To narrow it down, could you share, from one affected thread:

    1. The ACP protocol log T3 Code writes for that thread under ~/.t3/userdata/logs/provider/, especially the session/new (or session/load) response, with its configOptions and models, and every outgoing session/set_config_option, session/set_model and session/set_mode request before the first session/prompt.
    2. The matching events.jsonl lines, so the session.model_change can be lined up with the request that caused it.
    3. Whether the thread had a reasoning effort or any other model option selected in T3 Code.

    Feel free to redact anything sensitive. Only the method names, configIds and values matter.

  2. kbehailuzt commented on Oct 30, 2026

    @kbehailuzt

    Note

    🤖 GPT-6.1 Sol responding on behalf of kbehailuzt

    @juliusmarminge thanks for checking. I inspected the affected Windows thread, and these records don’t establish a redundant unchanged-model request.

    t3-copilot-maintainer-evidence.txt

    Environment: T3 Code desktop 0.1.5, Copilot CLI 1.0.105, Windows.

    The journal shows:

    • A genuine switch from gpt-6.1-sol to claude-sonnet-5.5.
    • An effort change from medium to xhigh, followed by the static-context warning.
    • Before the next failure, effort changed from xhigh to high. The model stayed Sonnet.

    The matching provider log contains three session/set_config_option attempts before the initial prompt, three before the first failing prompt, and one before the second. No session/set_model or session/set_mode requests appear.

    Unfortunately, this build’s saved protocol log replaces payloads with summaries such as {"valueType":"object","fieldCount":3}. The session/new response similarly retains only a field count. Consequently, I can’t provide its configOptions/models or the requests’ configId/value pairs from these records.

    Attached are the timestamped protocol summaries and matching journal excerpts, without prompts or credentials. What’s the supported way to capture the requested option fields without logging private prompt content?

  3. juliusmarminge commented on Nov 12, 2026

    @juliusmarminge
    Member

    I tried to reproduce this on the current build and could not.

    Commit 6ba3de7 (origin/main). (1) Ran GitHub Copilot CLI 1.0.109 over raw ACP with --acp --model claude-opus-5.5 --context long_context: session/new returns a model select config option with currentValue claude-opus-5.5 (plus mode, reasoning_effort=medium, allow_all). After a prompt the journal shows session.start {selectedModel: claude-opus-5.5, contextTier: long_context} and usage_record maxPromptTokens 952000. Forcing a redundant session/set_config_option model=claude-opus-5.5 produced session.model_change {previousModel=newModel=claude-opus-5.5, contextTier: long_context} and maxPromptTokens stayed 952000. Setting reasoning_effort=high also kept the tier (952000). (2) Ran T3CODE_DEV_SEED=0 vp run dev with a local ACP registry instance whose executable is a copilot wrapper adding --acp --model claude-opus-5.5 --context long_context. Paired a browser, picked Copilot local / Claude Opus 5.5 (default effort) in the composer and sent 'Reply with just: ok'. T3's provider protocol log contains only initialize, session/new and session/prompt: no session/set_config_option or session/set_model. The Copilot journal for that session has no session.model_change and its usage_record shows maxPromptTokens 952000. T3 does not re-send an unchanged model (the runtime's setConfigOption skips a value equal to the cached currentValue), and current Copilot no longer drops the tier even when a same-model set arrives.

    Closing as not planned. If it still happens for you, reply with the exact steps and the version you are on and it will be looked at again.

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