Repository navigation
Description
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
- Add GitHub Copilot through the ACP Registry (agent
github-copilot-cli). - 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. - Start a T3 Code thread on
claude-opus-5.5and send a message. - 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:
t3code/packages/provider-acp/src/server/adapter.ts
Lines 6694 to 6709 in 960d576
| 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:
t3code/packages/provider-acp/src/server/adapter.ts
Lines 6715 to 6717 in 960d576
| 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: 200000Workaround
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
juliusmarminge commentedon Oct 29, 2026
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 cachedcurrentValuealready 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:
initializeandsession/newonly. No model set.session/promptonly.session/set_config_option model=composer-2, thensession/prompt.session/promptonly.The test patch is here and its output is here.
So the
model_changein 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'ssession/newreports a differentcurrentValue, 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:~/.t3/userdata/logs/provider/, especially thesession/new(orsession/load) response, with itsconfigOptionsandmodels, and every outgoingsession/set_config_option,session/set_modelandsession/set_moderequest before the firstsession/prompt.events.jsonllines, so thesession.model_changecan be lined up with the request that caused it.Feel free to redact anything sensitive. Only the method names,
configIds and values matter.kbehailuzt commentedon Oct 30, 2026
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:
gpt-6.1-soltoclaude-sonnet-5.5.mediumtoxhigh, followed by the static-context warning.xhightohigh. The model stayed Sonnet.The matching provider log contains three
session/set_config_optionattempts before the initial prompt, three before the first failing prompt, and one before the second. Nosession/set_modelorsession/set_moderequests appear.Unfortunately, this build’s saved protocol log replaces payloads with summaries such as
{"valueType":"object","fieldCount":3}. Thesession/newresponse similarly retains only a field count. Consequently, I can’t provide itsconfigOptions/modelsor the requests’configId/valuepairs 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?
juliusmarminge commentedon Nov 12, 2026
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 amodelselect config option with currentValueclaude-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 redundantsession/set_config_option model=claude-opus-5.5produced 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) RanT3CODE_DEV_SEED=0 vp run devwith a local ACP registry instance whose executable is acopilotwrapper 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.