#393·claurst

Free mode: wrong context window/max-output everywhere (catalog hardcode, footer fallback, compact heuristic) — real registry values never consulted

Author: 7jrxt42BxFZo4iAnN4CXCreated Aug 22, 2026Updated Aug 22, 2026

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

  1. 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.
  2. 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.
  3. 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

  1. /connect → Free Mode, add a Zen-capable key, pin opencode-zen/x-preview-f-free
  2. Footer shows Nk/128000; before any override, compaction sizing came from the 100k heuristic
  3. /model lists the entry as 128k / 8k output
  4. 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):

json
{
  "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.