Description
OpenCode silently drops token usage for dynamically loaded providers that implement AI SDK LanguageModelV2. This is severe because it does not only hide the context-usage indicator: it also disables automatic context compaction.
The affected model can have a valid positive limit.context, but every assistant message is persisted with zero input/output/cache tokens. session/overflow.ts therefore never sees the session approach the model's context limit and never schedules compaction. Long-running sessions continue growing without warning until the provider rejects the request at its hard context limit. Users lose the normal compaction safety mechanism and receive no visible indication that it has stopped working.
Observed with SAP AI Core using @jerome-benoit/sap-ai-provider-v2@4.6.7, but the bug is version-based rather than SAP-specific and can affect any dynamically loaded AI SDK V2 provider.
The model metadata was not the problem: the affected model reported limit.context = 1,000,000 correctly.
Root cause
Provider.getLanguage() is typed as returning LanguageModelV3, although dynamically loaded providers can return a runtime V2 model.
packages/opencode/src/session/llm.ts unconditionally passes that model to wrapLanguageModel() with V3 middleware. The wrapper labels the result as V3 before AI SDK can run its V2-to-V3 compatibility conversion. Finish usage is then lost.
A minimal differential probe produced:
base V2 model: finish-step usage = input 14, output 4, total 18
V3-wrapped model: finish-step usage = undefined
Plugins
None required. Reproduced with --pure.
OpenCode version
1.18.31 / dev commit 0a429ed441
Steps to reproduce
- Configure a dynamically loaded AI SDK V2 provider with a model that has a valid context limit.
- Run:
opencode run --pure --format json --model <provider>/<model> --agent build "Reply exactly OK"
- Inspect the
step_finish event.
Actual result:
{
"tokens": {
"input": 0,
"output": 0,
"reasoning": 0,
"cache": { "read": 0, "write": 0 }
}
}
- Continue the session. The context usage indicator remains absent and automatic compaction never triggers, even as the conversation approaches the model's context limit.
Expected behavior
- V2 provider usage MUST survive message transformation and AI SDK compatibility conversion.
- Assistant messages MUST persist non-zero token usage when the provider reports it.
- Automatic compaction MUST continue to trigger based on the model's context limit.
A working fix is to inspect the runtime specificationVersion:
- V2 model: transform messages before
streamText and pass the model unwrapped, allowing AI SDK's V2-to-V3 converter to preserve usage.
- V3 model: retain the existing V3 middleware path.
With that change, the same end-to-end command produced non-zero usage (total: 82061, input: 82057, output: 4), restoring both context display and auto-compaction.
Operating System
macOS 27 (arm64)
Terminal
Zed terminal
Description
OpenCode silently drops token usage for dynamically loaded providers that implement AI SDK
LanguageModelV2. This is severe because it does not only hide the context-usage indicator: it also disables automatic context compaction.The affected model can have a valid positive
limit.context, but every assistant message is persisted with zero input/output/cache tokens.session/overflow.tstherefore never sees the session approach the model's context limit and never schedules compaction. Long-running sessions continue growing without warning until the provider rejects the request at its hard context limit. Users lose the normal compaction safety mechanism and receive no visible indication that it has stopped working.Observed with SAP AI Core using
@jerome-benoit/sap-ai-provider-v2@4.6.7, but the bug is version-based rather than SAP-specific and can affect any dynamically loaded AI SDK V2 provider.The model metadata was not the problem: the affected model reported
limit.context = 1,000,000correctly.Root cause
Provider.getLanguage()is typed as returningLanguageModelV3, although dynamically loaded providers can return a runtime V2 model.packages/opencode/src/session/llm.tsunconditionally passes that model towrapLanguageModel()with V3 middleware. The wrapper labels the result as V3 before AI SDK can run its V2-to-V3 compatibility conversion. Finish usage is then lost.A minimal differential probe produced:
Plugins
None required. Reproduced with
--pure.OpenCode version
1.18.31 / dev commit
0a429ed441Steps to reproduce
step_finishevent.Actual result:
{ "tokens": { "input": 0, "output": 0, "reasoning": 0, "cache": { "read": 0, "write": 0 } } }Expected behavior
A working fix is to inspect the runtime
specificationVersion:streamTextand pass the model unwrapped, allowing AI SDK's V2-to-V3 converter to preserve usage.With that change, the same end-to-end command produced non-zero usage (
total: 82061,input: 82057,output: 4), restoring both context display and auto-compaction.Operating System
macOS 27 (arm64)
Terminal
Zed terminal