Environment
- Claurst master @
595b0eb (built as 0.1.7.r85.g595b0eb); the same code paths exist in v0.1.7
- OS: Arch Linux, kitty/tmux
- Provider/model: Free Mode, pinned
opencode-zen/x-preview-f-free
(real limits per models.dev opencode/x-preview-f-free: context 1,000,000 / output 131,072)
Summary
In Free mode the effective context window ends up wrong in three independent
places, because none of them consult ModelRegistry/models.dev for the actual
(upstream, model) pair, and models.dev has no opencode-zen entry at all:
| Consumer |
Source of the window |
Value seen |
Footer / token warnings — App::refresh_context_window_size() (crates/tui/src/app.rs:1921) |
registry miss → hardcoded fallback table |
128_000 |
Auto-compact sizing — compact::resolve_context_window() (crates/query/src/compact.rs:851) |
registry miss → Claude-only name heuristic |
100_000 |
/model picker — Free provider catalog() (crates/api/src/providers/free.rs:374,389) |
constants for every upstream model |
128_000 / max_output 8_192 |
So for a 1M-context model the footer claims 128k, auto-compact was sized from a
~100k heuristic, and the picker lists every free upstream model as 128k/8k —
including e.g. google/gemini-2.5-flash (real: 1M) or the Zen default
minimax-m2.5-free (real: 204,800).
Root cause
free.rs catalog() passes literal 128_000 / 8_192 for all chained
upstream models instead of resolving them per (upstream_id, model) from the
registry.
refresh_context_window_size() only strips the active provider prefix and
then does registry.get("free", "<model-id>"). It does not try the embedded
"provider/model" split the way resolve_context_window() does
(compact.rs:867-873), so the two consumers derive different registry keys
for the same model.
- models.dev has no
opencode-zen provider entry; Zen gateway models are
listed under provider id opencode. Registry lookups keyed by
opencode-zen/<model> therefore always miss.
Steps to reproduce
/connect → Free Mode, add a Zen-capable key, pin opencode-zen/x-preview-f-free
- Footer shows
Nk/128000; before any override, compaction sizing came from the
100k heuristic
/model lists the entry as 128k / 8k output
- Cross-check models.dev:
opencode/x-preview-f-free = 1,000,000 / 131,072
Workaround (verified working)
Duplicate the metadata override under both key schemas in settings.json
(the registry synthesizes entries for unknown keys):
{
"modelOverrides": {
"opencode-zen/x-preview-f-free": { "contextWindow": 1000000, "maxOutputTokens": 131072 },
"free/opencode-zen/x-preview-f-free": { "contextWindow": 1000000, "maxOutputTokens": 131072 }
}
}
##Suggested fix
- In the Free catalog, resolve context_window / max_output_tokens per
(upstream_id, default_model) from ModelRegistry, keeping the current
constants only as last-resort defaults (reuse the existing
MIN_PLAUSIBLE_REGISTRY_WINDOW plausibility guard).
- Make App::refresh_context_window_size() delegate to
compact::resolve_context_window() so both consumers share one key-derivation
path instead of maintaining parallel lookup logic.
- (Upstream data) file against models.dev: missing opencode-zen entry.
Happy to test a patch.
Environment
595b0eb(built as0.1.7.r85.g595b0eb); the same code paths exist in v0.1.7opencode-zen/x-preview-f-free(real limits per models.dev
opencode/x-preview-f-free: context 1,000,000 / output 131,072)Summary
In Free mode the effective context window ends up wrong in three independent
places, because none of them consult ModelRegistry/models.dev for the actual
(upstream, model) pair, and models.dev has no
opencode-zenentry at all:App::refresh_context_window_size()(crates/tui/src/app.rs:1921)128_000compact::resolve_context_window()(crates/query/src/compact.rs:851)100_000/modelpicker — Free providercatalog()(crates/api/src/providers/free.rs:374,389)128_000/max_output 8_192So for a 1M-context model the footer claims 128k, auto-compact was sized from a
~100k heuristic, and the picker lists every free upstream model as 128k/8k —
including e.g.
google/gemini-2.5-flash(real: 1M) or the Zen defaultminimax-m2.5-free(real: 204,800).Root cause
free.rscatalog()passes literal128_000/8_192for all chainedupstream models instead of resolving them per
(upstream_id, model)from theregistry.
refresh_context_window_size()only strips the active provider prefix andthen does
registry.get("free", "<model-id>"). It does not try the embedded"provider/model"split the wayresolve_context_window()does(
compact.rs:867-873), so the two consumers derive different registry keysfor the same model.
opencode-zenprovider entry; Zen gateway models arelisted under provider id
opencode. Registry lookups keyed byopencode-zen/<model>therefore always miss.Steps to reproduce
/connect→ Free Mode, add a Zen-capable key, pinopencode-zen/x-preview-f-freeNk/128000; before any override, compaction sizing came from the100k heuristic
/modellists the entry as 128k / 8k outputopencode/x-preview-f-free= 1,000,000 / 131,072Workaround (verified working)
Duplicate the metadata override under both key schemas in
settings.json(the registry synthesizes entries for unknown keys):
{ "modelOverrides": { "opencode-zen/x-preview-f-free": { "contextWindow": 1000000, "maxOutputTokens": 131072 }, "free/opencode-zen/x-preview-f-free": { "contextWindow": 1000000, "maxOutputTokens": 131072 } } }##Suggested fix
(upstream_id, default_model) from ModelRegistry, keeping the current
constants only as last-resort defaults (reuse the existing
MIN_PLAUSIBLE_REGISTRY_WINDOW plausibility guard).
compact::resolve_context_window() so both consumers share one key-derivation
path instead of maintaining parallel lookup logic.
Happy to test a patch.