Skip to content

Bare gpt-reserve gets no catalog row outside codexDesktopAuthless: tool_mode falls back to direct, ~250k tokens of inlined tool schemas at boot #3844

Closed as not planned

Description

@alanxchen85

Summary

The bare gpt-reserve wire id gets no catalog row outside codexDesktopAuthless mode. Codex then falls back to default model settings, which means tool_mode is unset (i.e. direct) instead of code_mode_only. Every MCP/plugin tool schema is inlined into the request instead of being deferred behind the exec namespace.

On a Codex Desktop install with the normal plugin set this is ~250k input tokens on turn 1, against a fallback model_context_window of 258400. The session compacts after nearly every action.

Environment

  • opencodex 2.45.0 (also reproduced on 2.42.0)
  • Codex 0.153.4, Codex Desktop 26.901.51231, macOS 15 (arm64)
  • Normal loopback routing: root openai_base_url = http://127.0.0.1:10100/v1, codexDesktopAuthless unset
  • model_catalog_json = ~/.codex/opencodex-catalog.json (39 models, no gpt-reserve)

Evidence

Turn 1 of a fresh Desktop thread, prompt "hi", from the rollout token_count event:

{"input_tokens": 252450, "cached_input_tokens": 3328, "output_tokens": 94,
 "model_context_window": 258400}

~/.opencodex/usage.jsonl confirms the wire id resolves fine upstream — the request itself succeeds:

{"model": "gpt-reserve", "resolvedModel": "gpt-5.6-luna",
 "requestedEffort": "max->xhigh", "status": 200, "totalTokens": 252544}

Minimal repro

Point Codex at a request sink and compare a catalogued slug against gpt-reserve:

codex exec --config openai_base_url='"http://127.0.0.1:10199/v1"' -m <slug> "hi"
-m slug in catalog request body inlined tools[] additional_tools deferral
gpt-reserve no 1,321,509 chars 56 no
zzz-fake-model no 1,321,512 chars 56 no
gpt-5.6-luna yes 120,914 chars 0 yes
gpt-5.6-terra yes 120,915 chars 0 yes

gpt-reserve behaves identically to a nonsense slug. Breakdown of the 56 inlined tools under gpt-reserve (CLI only — Desktop is larger):

source tools ~tokens
codex_apps 35 292,716
builtin 15 6,817
dataAnalyticsWidgets 1 6,203
playwright 1 4,029
others 4 3,110

Note that [features] tool_search = true and tool_search_always_defer_mcp_tools = true are set in config.toml and do not help — the per-model tool_mode field is the gate.

Cause

createReserveCatalogProjection() in src/codex/catalog/reserve.ts returns early:

if (!isEffectiveCodexDesktopAuthless(config) || mainSelectors.length === 0) return undefined;

Two consequences for a user on normal openai_base_url routing:

  1. The projection never fires, so no Reserve row is emitted at all.
  2. Even when it does fire, the row is account-bound (<selector>/gpt-reserve per isReserveCatalogProjection), while the Desktop model picker sends the bare gpt-reserve.

native-models.ts is explicit that this is intended — "Reserve wire identity, not a globally available native catalog registration." But the account still serves the bare slug upstream (200 above), so a user can select it and silently lose both tool deferral and the correct context window with no warning anywhere.

Sync does recognise a hand-added bare row: observedReserveCatalogSource() captures it into the root opencodex_reserve_source field. It just never emits a model row from it outside the authless gate, so ocx sync strips the row on every run (including the auto-sync on ocx start).

Workaround

Clone the gpt-5.6-luna row with slug: "gpt-reserve" into opencodex-catalog.json. That restores tool_mode: code_mode_only, context_window: 300000, effective_context_window_percent: 95, auto_compact_token_limit: 270000, and the request drops to 120,912 chars with 0 inlined tools — identical to Luna. It has to be re-applied after every sync.

Suggested fix

Either:

  • Emit a bare gpt-reserve row whenever a genuine reserve observation exists (opencodex_reserve_source is populated), independent of codexDesktopAuthless; or
  • If Reserve is genuinely unsupported outside authless mode, warn on sync when the persisted Codex model selection is a slug with no catalog row — silently degrading to tool_mode: direct and a smaller window is very hard to attribute.

More generally: any slug the backend accepts but the catalog omits will hit this. A sync-time warning for "selected model has no catalog row" would catch the whole class.

Activity

  1. added
    catalogModel catalog, slugs, visibility, routed entries
    toolstool_calls, MCP, web-search / sidecar tools
    on Sep 7, 2026
  2. github-actions commented on Sep 7, 2026

    @github-actions
    Contributor

    Issue closed: use an issue template

    Thanks for taking the time to submit this issue. It was closed automatically because it was not opened with a recognized issue template (Bug report, Feature request, Documentation, or Provider compatibility). Freeform and API-opened issues cannot skip this check.

    • This issue does not use a recognized issue template.

    Please edit the issue to add:

    • Open a new issue with the Bug report, Feature request, Documentation, or Provider compatibility template.
    • Or edit this issue so the body uses the template section headings for the kind of report you are filing.

    See the Contributing guide for details.

    Once the report passes the automated checks, it will be reopened automatically unless a maintainer has changed its state.

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

    catalogModel catalog, slugs, visibility, routed entriestoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions