Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Model provider keys and connection recovery are stored separately in model-providers.vault using the existing protection identity. Preserve this file, the account vault and the original Codex sign-in.
- ProviderEditorSession retains drafts and selection in that same encrypted store. A null draft API key retains the saved credential; an explicitly empty key is invalid. Revealing/copying a loaded key must never authorize sending it to a changed destination. Preserve drafts on provider changes, failed saves and close/reopen.
- Connection/restoration edits use the official version-checked configuration RPC. Model choices route per request; never restart the owner's Codex for verification.
- New connections publish one stable codex-switch model. The saved actual-model selection applies globally to new virtual-model requests; snapshot its destination/key/options once and retain them through streams/retries. Never retarget a duplicate turn request after selection changes, fall back from a missing route, or rewrite the catalog merely to select a model. Preserve legacy aliases and explicit history/image compatibility refusals.
- The gateway runs inside Switch. Treat an active gateway as part of ongoing Codex work when planning utility updates; use an owner-approved idle handoff.
- Provider checks use disposable storage, fictional upstreams and an explicit reviewed CLI. Keep component renders, native app-server contracts, native desktop picker confirmation, real provider acceptance and public release as separate states. [Contract](docs/MODEL_PROVIDERS.md).

Expand Down
18 changes: 10 additions & 8 deletions docs/MODEL_PROVIDERS.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# Configurable model providers

Available in the 1.43.3 release. [Issue138](https://github.com/Gaebabi/codex-switch/issues/138) tracks remaining actual desktop picker and external-provider compatibility acceptance.
Provider support is available in the1.43.3release. Fixed-model routing below is a source change after1.44.0; the existing release downloads retain the earlier picker. [Issue138](https://github.com/Gaebabi/codex-switch/issues/138) tracks remaining actual desktop picker and external-provider compatibility acceptance.

Keep eligible OpenAI models and custom Responses models in the existing Codex model picker. OpenAI models retain their original display names; custom models include their provider name in parentheses. Selecting a model changes that chat's next request; an in-flight response keeps the route and key it started with.
New connections expose one stable **Codex Switch** model (`codex-switch`) in Codex. Choose the actual OpenAI or custom Responses model in Switch's **Model** dropdown on Home or in provider setup. The selection is global to all new requests using the virtual model. Each admitted request retains its selected model, destination, key and reasoning default through its response and bounded retries; changing the dropdown never retargets an in-flight stream. A duplicate native turn request is refused rather than replayed against a newly selected backend.

## Set up a provider

1. In1.44.0, open Model providers in the titlebar Settings gear. Older1.43.3 Windows packages use Models in the toolbar.
2. In the first step, select Add a provider or a saved provider, then enter its display name, API base URL and API key. Choose Save and continue to discover and save its models.
3. For a large catalog, expand the optional model-ID field and enter one ID per line. Leave it empty to discover all models within the supported bounds. Reasoning and conversation-handoff controls stay under Advanced in the second step.
4. In the second step, choose Connect Codex, then Restart Codex when ready. Saving alone does not connect or restart Codex. The restart button uses the existing normal restart and chat-recovery flow with the same login.
5. Keep Switch running, including in its tray. Choose OpenAI or a custom model inside each Codex chat. New connections use an explicit local model catalog; after changing its models or reasoning settings, restart Codex when ready to load the updated catalog.
4. In the second step, choose the actual model, then Connect Codex. Restart Codex once when no work is running to load the fixed model and gateway. Saving or selecting alone does not connect or restart Codex. The restart button uses the existing normal restart and chat-recovery flow with the same login.
5. Keep Switch running, including in its tray. Select **Codex Switch** in Codex for an existing chat; new chats default to it. Change the actual model in Switch whenever needed. Later selections, provider additions and saved reasoning defaults apply to new requests without changing the fixed catalog or restarting Codex.

The dropdown preserves its exact selection in the existing encrypted provider store. An unavailable/deleted selection is shown explicitly and requests fail before inference; Switch never silently selects another destination. OpenAI choices come from the current account catalog and are revalidated for its sign-in before inference. The first connection/upgrade still needs a Codex restart: changing an already running client's catalog/endpoint is not supported. Existing OpenAI models and custom aliases remain accepted by the gateway for older connected chats; a legacy installation upgrades only through an explicit Connect action. The built-in OpenAI provider identity is retained throughout.

Saved providers reopen with their fields and a masked key. The eye button shows or hides the current key, and Copy copies it only after an explicit click. Neither action changes the key. Changing the destination URL still requires an explicitly entered replacement key; displaying a saved key does not authorize sending it elsewhere.

Expand All @@ -30,9 +32,9 @@ A provider's native Codex catalog retains its model instructions and capabilitie

### GPT reasoning controls

Custom GPT models can use the ordinary Codex reasoning control. Switch passes the selected `reasoning.effort` to the custom Responses endpoint. A native Codex catalog supplies the supported levels; a plain list uses the exact-ID OpenAI match described above. The destination must support the same effort values; matching IDs do not certify external provider behavior.
For the fixed model, configure reasoning in Switch. Each request uses the actual selected model's saved default, or its provider/exact-ID OpenAI metadata default. The virtual Codex entry keeps no per-backend reasoning menu, so changing models does not require a catalog refresh. Older custom aliases retain their ordinary Codex reasoning control and pass its explicit effort. The destination must support the declared values; matching IDs do not certify external provider behavior.

For a model absent from that catalog or a route with different support, expand **GPT reasoning in Codex**, select the saved model, enable **Set supported reasoning levels**, select its documented levels and a default, then save. Disable the override and save to return to automatic metadata. These settings apply to one model and remain after rediscovery at the same destination. A changed destination resets them. Refresh the Codex model list after saving; its cache may take a few minutes to update.
For a model absent from that catalog or a route with different support, expand **Model reasoning**, select the saved model, enable **Set supported reasoning levels**, select its documented levels and a default, then save. Disable the override and save to return to automatic metadata. These settings apply to one model and remain after rediscovery at the same destination. A changed destination resets them. Fixed-model requests use the saved default from the next request; older per-model catalogs require their original refresh/restart behavior.

Supported control values are `none`, `minimal`, `low`, `medium`, `high`, `xhigh` and `max`, subject to the installed Codex version and the model's API. Unsupported selections fail before inference rather than silently using another strength. A plain list with no exact match or explicit settings keeps provider-default behavior. No additional reasoning API or token-budget conversion is used.

Expand All @@ -56,7 +58,7 @@ While waiting, Switch sends only SSE comment keep-alives to the original caller.

A later hotfix observes confirmed desktop turn failures of every error type, including HTTP507exhaustion, invalid event streams, authentication, quota and context errors. Eligibility uses the persisted failed status, without matching error words or requiring an error message. After the original turn has stopped, Switch can send one identified `Continue` through that chat's existing desktop owner, inheriting its settings. It does not replay the original Responses input after tools or partial output may already have run. Consecutive failed automatic continuation turns share at most three attempts, with3/10/30second backoff; successful completion closes that recovery chain. This is separate from the pre-response HTTP507request budget, so each new continuation has its own bounded HTTP507retries. A persistent error can still require user action after all three continuations fail.

The observer requires a recent persisted `task_complete` failure, an observed stopped main chat with no approval/input/queued follow-up, matching current model/provider, a connected original OpenAI/Switch provider and unchanged current sign-in/route during backoff. Custom model aliases must still be registered. An earlier successful request is not required: the first request can fail before successful-route history exists. This never fabricates route history or bypasses encrypted-compaction and portable-history checks in the gateway. It skips active, completed, interrupted, paused/limited-goal and unsupported-provider chats. The bounded read-only rollout tail comes from the existing SQLite thread record; a newer `task_started` invalidates a historical failure. No original transcript is rewritten, no account or model is changed and no window is opened or activated.
The observer requires a recent persisted `task_complete` failure, an observed stopped main chat with no approval/input/queued follow-up, matching current model/provider, a connected original OpenAI/Switch provider and unchanged current sign-in/route during backoff. Custom model aliases and the virtual model's selected actual route must still be registered. An earlier successful request is not required: the first request can fail before successful-route history exists. This never fabricates route history or bypasses encrypted-compaction and portable-history checks in the gateway. It skips active, completed, interrupted, paused/limited-goal and unsupported-provider chats. The bounded read-only rollout tail comes from the existing SQLite thread record; a newer `task_started` invalidates a historical failure. No original transcript is rewritten, no account or model is changed and no window is opened or activated.

Encrypted recovery receipts persist attempt count and message/turn identities before submission. A lost/ambiguous IPC response or persistence error stops that recovery rather than resending. Exhaustion survives a Switch restart. Reauthentication, route edits, new user turns, queues and actual user interruptions prevent resubmission. Generic idle/EOF alone does not establish a failed turn. The observer applies only to chats observed in this local desktop with a connected OpenAI/Switch provider; it does not revive archived or unloaded historical chats. The local `stream-recovery-state.json` also retains up to128deduplicated decisions with fixed reason codes, hashed chat/turn references and attempt counts. It omits chat text, raw errors, account identities and provider secrets.

Expand All @@ -72,7 +74,7 @@ The large-upload admission slot and request buffers are released when final upst

Unregistered model names fail explicitly. OpenAI routes are checked against the current sign-in's catalog, while custom routes use their registered aliases. Managed provider/catalog requirements are refused before preparing a local connection.

New connections preserve the existing built-in `openai` provider identity and set `openai_base_url` to the loopback listener. An explicit private `model_catalog_json` file supplies the combined native and custom model list to Codex and its desktop picker. The catalog contains model metadata, not API keys. It is refreshed on provider edits, account changes and the existing refresh cycle; Codex loads the file at startup. WebSockets and request compression are disabled for portable full-input HTTP replay. Existing provider definitions are preserved.
New connections preserve the existing built-in `openai` provider identity and set `openai_base_url` to the loopback listener. An explicit private `model_catalog_json` supplies the one stable virtual model, and `model` defaults to `codex-switch`. It contains no API keys. Selections and provider/reasoning edits do not rewrite this fixed catalog. Codex loads it at startup; WebSockets and request compression are disabled for portable full-input HTTP replay. Existing provider definitions are preserved. The wrapper uses generic client instructions, no asserted context-window size, no hosted-tool extensions and image-envelope support; the gateway refuses image input for a selected backend that does not declare it. Actual upstream context/tool/streaming limits still apply; history is never silently truncated to fit.

Older connections that use `codex_switch_router` remain untouched. Recent Codex versions reject a chat when its saved provider identity differs from the current provider, even if both use the same gateway. Restoring/reconnecting an older installation can therefore affect which provider-pinned chats it can reopen. Switch does not rewrite chat history or bypass that check. Keep the older connection for its existing router-pinned chats; new built-in OpenAI connections retain compatibility with original OpenAI chats.

Expand Down
2 changes: 2 additions & 0 deletions docs/development/OPERATIONS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Operations

Fixed-model routing after1.44.0 keeps one `codex-switch` model/default and moves actual model selection to a direct Home/setup dropdown. Selection is global for new virtual-model requests and persists in the existing protected provider vault. Snapshot at authenticated HTTP admission, before body upload, and retain destination/model/key/options through streaming and507retries. Native duplicate receipts use the fixed logical request identity so a selection change cannot replay it to a different backend. Do not rewrite the catalog for route/provider/reasoning edits; keep legacy aliases and history/image refusals. Recovery also pins the saved selection during backoff. Initial connection/legacy upgrade needs one owner-idle Codex restart; no owner restart is part of verification. [Contract](../MODEL_PROVIDERS.md) / [fictional native/component evidence](../evidence/fixed-model-router/README.md).

Failed-turn recovery uses `DesktopStreamRecoveryMonitor` and a separately encrypted `stream-recovery.vault`, preserving the original vault/key identities. It reads a bounded original rollout tail through exact read-only SQLite metadata and waits for a confirmed failed stopped main turn of any error type. HTTP507, quota, authentication, context, compaction and malformed-stream errors all qualify; missing error prose does not imply success. It sends one inherited-settings identified Continue via the existing desktop owner, without deep-link activation, model changes, account application or original HTTP replay. Three consecutive automatic attempts share persisted3/10/30second backoff and do not reset on app restart; success closes the chain. Ambiguous send/persistence failures stop that chat's recovery. Existing cancellation, queue, approval, route/authentication and completed/active guards stay enforced. An earlier successful-route receipt is not required, and gateway history checks remain enforced. Inspect bounded `decisions` in local `stream-recovery-state.json` for hashed references and reason codes such as `continued`, `attempt-limit`, `selected-model-unregistered` or `state-changed`; no raw error/chat/provider data is logged. [Behavior and limitations](../MODEL_PROVIDERS.md#continue-after-a-failed-response-stream).

The Home Errors counter reports currently stopped main chats from the same authenticated desktop observation as Run. Count a main chat once when its runtime status is systemError, or idle and its latest turn failed. Active, interrupted, recovered and child chats are excluded; unavailable or incomplete discovery displays an em dash. Counts update on state patches and clear on recovery/removal. It is not a cumulative gateway request/error log. Windows and shared desktop use the same protocol count and localized warning-colored presentation.
Expand Down
2 changes: 2 additions & 0 deletions docs/development/PLAN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Current plan

One stable `codex-switch` model and direct Windows/shared-desktop actual-model dropdown are implemented. Global selection stays in the existing encrypted store; each admitted route/key/options snapshot survives upload/streaming/retries, and native duplicates cannot retarget after a selection change. Current-native-CLI/provider292, WPF124, account/recovery350and language API42checks pass with both desktop builds. Complete final-head CI/review/normal merge and independently verify installation while preserving personal Codex work. First connection/legacy upgrade still needs one owner-idle Codex restart; later provider additions and route/reasoning selections do not rewrite its fixed catalog or restart Codex.

Custom-provider setup now keeps connection state visible after every editor action and preserves the original OpenAI provider identity. New connections supply an explicit private combined model catalog. Native visible-model discovery, same-chat provider selection, catalog refresh/restoration and bounded retry receipts are verified with disposable inputs. Require final-head repository/native CI before promotion; owner restarts, actual desktop picker confirmation and binary releases remain separate actions.

The two-step onboarding follow-up preserves encrypted drafts, selected provider and saved keys through errors and reopening. Explicit eye/copy/reuse/load and user-requested restart actions are implemented. Provider, account/recovery, language and narrow Windows-component checks pass alongside both desktop builds. Complete final-head CI and review before promotion; owner installation and binary release remain separate.
Expand Down
2 changes: 2 additions & 0 deletions docs/development/STATUS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Current status

Fixed-model routing passes292provider/current-native-CLI contracts,124offscreen WPF checks,350account/recovery checks and42language API checks, with both desktop builds and9catalogs of535messages. One `codex-switch` catalog entry/default and the persisted actual-model dropdown keep the native model stable while routes change. Same-process/same-chat A/B/OpenAI, a provider added after startup, saved reasoning defaults, upload/stream/retry snapshots, duplicate-client refusal, deletion/image/history guards and malformed-cache preservation are verified with fictional upstreams. [Component images and scoped receipts](../evidence/fixed-model-router/README.md). Initial connection/upgrade still requires one Codex restart; final-head CI, owner restart and personal picker/external-provider acceptance remain separate. See [model routing](../MODEL_PROVIDERS.md).

Provider setup and onboarding pass255gateway/current-native-CLI checks,350account/recovery checks,116offscreen WPF checks and42language API checks. The two-step editor retains encrypted drafts and selection through failed saves, provider changes and close/reopen. Saved keys remain masked, with explicit eye/copy/reuse/load actions; a displayed key never authorizes a changed destination. New connections retain OpenAI identity and use a private combined catalog, with bounded native-request receipts. Windows/shared desktop builds and all9language catalogs pass. Existing accounts, router-pinned chats and owner installations remain unchanged; actual Codex picker interaction and a binary release are separate.

Unreleased: the chat date button independently updates active root chat titles with the existing KST prefix and verified metadata path. It does not switch accounts or restart Codex. Errors stop the operation and report the number already verified. Native UI screenshots and real-user acceptance remain pending.
Expand Down
Loading
Loading