* docs: document provider model refresh fix Document the verified live-model refresh path for stale provider catalogs, record the current Pollinations anonymous-access limitation, and sync the provider-count references after regenerating the provider reference. Co-Authored-By: Oz <oz-agent@warp.dev> * docs: note codex local env and mac path Co-Authored-By: Oz <oz-agent@warp.dev> --------- Co-authored-by: Oz <oz-agent@warp.dev>
28 KiB
title, version, lastUpdated
| title | version | lastUpdated |
|---|---|---|
| Codex CLI — Configuration with OmniRoute | 3.8.49 | 2026-08-01 |
Codex CLI — Configuration with OmniRoute
Complete guide for using the Codex CLI pointed at OmniRoute as an OpenAI-compatible backend.
Ready-to-paste config.toml
Replace <YOUR_HOST> and <YOUR_KEY> with your values:
# ~/.codex/config.toml
model = "cx/gpt-5.5"
model_provider = "omniroute"
model_reasoning_effort = "xhigh"
model_context_window = 400000
model_auto_compact_token_limit = 350000
tool_output_token_limit = 32768 # history storage cap per tool call
[model_providers.omniroute]
name = "OmniRoute"
base_url = "http://<YOUR_HOST>:20128/v1"
env_key = "OMNIROUTE_API_KEY"
requires_openai_auth = false
wire_api = "responses"
# ~/.bashrc or ~/.zshrc — actual key value, never in config.toml
export OMNIROUTE_API_KEY="<YOUR_KEY>"
macOS: Codex bundled inside the ChatGPT app
If you installed Codex through the ChatGPT desktop app, the codex binary may
exist only inside the app bundle and not yet be on your shell PATH. Add the
resources directory to your shell startup file:
export PATH="/Applications/ChatGPT.app/Contents/Resources:$PATH"
Open a new shell, then verify:
command -v codex
codex --version
Local unauthenticated OmniRoute: placeholder key is enough
Codex validates that the environment variable named by env_key exists
before the first request leaves the CLI. If your local OmniRoute
instance does not require auth, any non-empty placeholder works:
export OMNIROUTE_API_KEY="${OMNIROUTE_API_KEY:-local}"
Use a real key instead when your OmniRoute server is protected or remote.
Common host options
Access URL Local network http://192.168.0.1:20128/v1Tailscale http://100.x.x.x:20128/v1Loopback http://localhost:20128/v1
wire_api = "responses" — why it works for all models
Codex CLI deprecated wire_api = "chat" (Chat Completions) in February 2026 and now requires wire_api = "responses" (OpenAI Responses API). Setting wire_api = "chat" causes an immediate startup crash since v0.138.
DeepSeek, GLM, Kimi and others only expose a Chat Completions endpoint — not the Responses API. If you pointed Codex directly at them, it would fail.
OmniRoute solves this transparently:
Codex CLI
→ wire_api = "responses"
→ POST /v1/responses (OmniRoute)
→ OmniRoute Responses ↔ Chat Completions transformer
→ POST /chat/completions (DeepSeek / Mistral / GLM / Kimi / any provider)
You never need a separate translation proxy when using OmniRoute. All models use wire_api = "responses" — OmniRoute handles the rest.
wire_apiis the default — the field defaults to"responses"and can be omitted entirely fromconfig.toml. Only ever set it explicitly if you're documenting intent.
Context window and compaction
Token configuration fields
| Field | Description |
|---|---|
model_context_window |
Total token budget for the active model. Set to the model's advertised limit. |
model_auto_compact_token_limit |
Threshold that triggers automatic history compaction. Maximum: 90% of model_context_window — values above 90% are silently ignored. |
tool_output_token_limit |
Cap on tokens stored per tool call output in history. Prevents a single large tool response from filling the window. This is not the max output — it is a history storage cap. |
compact_prompt |
Inline override for the system prompt used during compaction (v0.138+). |
Note on
model_max_output_tokens: This field is not part of the Codex CLI config schema (absent from the Codex Rust codebase). It is silently ignored if set. Do not rely on it — usetool_output_token_limitto control how much tool output is stored in history.
Context windows by model
| Model | OmniRoute ID | Context window | auto_compact |
tool_output_limit |
|---|---|---|---|---|
| GPT-5.5 | cx/gpt-5.5 |
400k reliable (1M max) | 350,000 | 32,768 |
| Kimi K2.7 (thinking) | kmc/kimi-k2.7 |
131,072 | 112,000 | 32,768 |
| Kimi K2.6 | kmc/kimi-k2.6 |
131,072 | 112,000 | 32,768 |
| GLM-5.2 / 5.2-max (thinking) | glm/glm-5.2 |
131,072 | 112,000 | 32,768 |
| MiMo V2.5 Pro (thinking) | opencode-go/mimo-v2.5-pro |
131,072 | 112,000 | 32,768 |
| Qwen 3.7 Plus (thinking) | opencode-go/qwen3.7-plus |
32,768 | 28,000 | 16,384 |
| DeepSeek V4 Pro (OllamaCloud) | ollamacloud/deepseek-v4-pro |
131,072 | 112,000 | 32,768 |
| DeepSeek V4 Pro | ds/deepseek-v4-pro |
1,000,000 | 900,000 | 65,536 |
| MiMo V2.5 | opencode-go/mimo-v2.5 |
131,072 | 112,000 | 32,768 |
| Gemma 4 31B (OllamaCloud) | ollamacloud/gemma4:31b |
32,768 | 28,000 | 16,384 |
| Nemotron 3 Super (OllamaCloud) | ollamacloud/nemotron-3-super |
32,768 | 28,000 | 16,384 |
| GPT-OSS 20B (OllamaCloud) | ollamacloud/gpt-oss:20b |
32,768 | 28,000 | 16,384 |
| DeepSeek V4 Flash (OllamaCloud) | ollamacloud/deepseek-v4-flash |
65,536 | 56,000 | 16,384 |
| Gemini 3 Flash Preview (OllamaCloud) | ollamacloud/gemini-3-flash-preview |
1,000,000 | 850,000 | 32,768 |
| GLM-5 Turbo | glm/glm-5-turbo |
131,072 | 112,000 | 16,384 |
| GLM-4.7 Flash | glm/glm-4.7-flash |
131,072 | 112,000 | 16,384 |
| Mistral Large Latest | mistral/mistral-large-latest |
262,144 | 220,000 | 16,384 |
Compaction formula:
effective_window = model_context_window - min(tool_output_token_limit, 20000). Values above 20k do not change the compaction trigger.
Rule of thumb: set
model_auto_compact_token_limitto 85–88% ofmodel_context_window. Never go above 90% — silently ignored.
Model prefix: cx/
All Codex models in OmniRoute use the cx/ prefix:
| Codex CLI name | OmniRoute model |
|---|---|
cx/gpt-5.5 |
GPT-5.5 standard |
cx/gpt-5.4 |
GPT-5.4 standard |
cx/gpt-5.4-mini |
GPT-5.4 mini |
cx/gpt-5.1-codex-mini |
GPT-5.1 Codex mini |
Other providers use their own prefix (kmc/, glm/, ds/, ollamacloud/, opencode-go/, mistral/) — the prefix matches the OmniRoute provider alias.
Reasoning Effort
Controls how much the model "thinks" before responding.
| Value | Use for |
|---|---|
none |
No reasoning — direct response |
low |
Trivial tasks (rename, format) |
medium |
Server default when not specified |
high |
Intermediate tasks (refactoring, debug) |
xhigh |
Architecture, deep analysis, complex problems |
# Per invocation override
codex -c model_reasoning_effort=low "rename variable x to count"
codex -c model_reasoning_effort=xhigh "design the auth module"
Profiles — named configurations per model/workflow
Profiles let you switch model + context window with a single flag. Each profile is a flat
~/.codex/<name>.config.toml that overlays on top of the base config.toml.
Naming rule (Codex CLI v0.137+): file must be
~/.codex/<name>.config.toml— noprofile-prefix. The CLI resolves-p kimi-k27→~/.codex/kimi-k27.config.toml. If the file is not found, the default applies silently.
codex --profile kimi-k27 "analyze 10k lines of this codebase"
codex -p glm52 "architecture review"
codex --profile deepseek-flash "rename variable" # fast, cheap
Effort profiles (same model, different effort)
codex -p low # cx/gpt-5.5, effort=low
codex -p medium # cx/gpt-5.5, effort=medium
codex -p high # cx/gpt-5.5, effort=high
codex -p xhigh # cx/gpt-5.5, effort=xhigh (default)
codex -p chat # cx/gpt-5.5, no effort set (server default)
Thinking models (alto pensamento) — xhigh + detailed summary
| Profile | Model | Context | Use for |
|---|---|---|---|
kimi-k27 |
kmc/kimi-k2.7 |
128k | Best thinking quality (Kimi) |
glm52 |
glm/glm-5.2 |
128k | GLM thinking |
glm52max |
glm/glm-5.2-max |
128k | GLM thinking max |
mimo-pro |
opencode-go/mimo-v2.5-pro |
128k | MiMo thinking |
qwen37plus |
opencode-go/qwen3.7-plus |
32k | Qwen thinking |
Good models (bons) — high effort
| Profile | Model | Context | Use for |
|---|---|---|---|
kimi-k26 |
kmc/kimi-k2.6 |
128k | General purpose (Kimi) |
deepseek-pro |
ollamacloud/deepseek-v4-pro |
128k | DeepSeek Pro via OllamaCloud |
deepseek |
ds/deepseek-v4-pro |
1M | DeepSeek Pro direct, huge context |
mimo |
opencode-go/mimo-v2.5 |
128k | MiMo general |
Simple models (simples) — no reasoning effort
| Profile | Model | Context | Use for |
|---|---|---|---|
gemma4 |
ollamacloud/gemma4:31b |
32k | Cost-effective, capable |
nemotron |
ollamacloud/nemotron-3-super |
32k | NVIDIA Nemotron |
gptoss |
ollamacloud/gpt-oss:20b |
32k | Open-source GPT |
Fast models — low effort
| Profile | Model | Context | Use for |
|---|---|---|---|
deepseek-flash |
ollamacloud/deepseek-v4-flash |
64k | Quick tasks |
gemini-flash |
ollamacloud/gemini-3-flash-preview |
1M | Very fast, huge context |
glm5turbo |
glm/glm-5-turbo |
128k | GLM Turbo |
glm47flash |
glm/glm-4.7-flash |
128k | GLM Flash |
mistral |
mistral/mistral-large-latest |
256k | Mistral Large |
Quick decision table
| Task | Recommended profile |
|---|---|
| Rename, format, boilerplate | --profile deepseek-flash or -p low |
| Explain, light review | -p chat or -p gemini-flash |
| Debug, moderate refactor | -p medium or -p kimi-k26 |
| New feature, complex tests | -p high or -p mimo |
| Architecture, deep analysis | -p kimi-k27 or -p glm52 or -p xhigh |
| Codebase analysis (needs 1M ctx) | --profile deepseek or --profile gemini-flash |
| Maximum thinking quality | -p glm52max or -p mimo-pro |
| Cost-conscious | -p gemma4 or -p gptoss |
Generating profiles automatically with omniroute setup-codex
If you run OmniRoute on a VPS, you can auto-generate profile files from the live model catalog:
# From a VPS (uses local OmniRoute on port 20128)
omniroute setup-codex
# From any machine — point at your VPS
omniroute setup-codex --remote http://100.x.x.x:20128 --api-key sk-xxx
# Preview without writing files
omniroute setup-codex --remote http://100.x.x.x:20128 --dry-run
# Only generate GLM and Kimi profiles
omniroute setup-codex --only glm,kimi
# Write to a custom directory
omniroute setup-codex --codex-home /path/to/.codex
The command fetches /v1/models, uses tuned profiles for known models, falls back to catalog metadata for other compatible text models, and writes ~/.codex/<name>.config.toml for each. Idempotent — safe to re-run.
OmniRoute can also auto-sync these same profile files after a successful provider model discovery/import changes the live catalog. This is opt-in and off by default: toggle it from the CLI Code dashboard ("CLI profile auto-sync" → Codex), or set OMNIROUTE_AUTO_SYNC_CODEX_PROFILES=true (it also honors CLI_ALLOW_CONFIG_WRITES, on by default). When enabled it only writes separate ~/.codex/*.config.toml profile files; it never changes the active/default ~/.codex/config.toml, Codex-lb settings, auth, or provider selection.
Launching Codex with omniroute launch-codex
Health-checks your OmniRoute instance before launching Codex:
# Launch against local OmniRoute (default port 20128)
omniroute launch-codex
# Launch with a specific profile
omniroute launch-codex --profile kimi-k27
# Launch against a remote VPS
omniroute launch-codex --remote http://100.x.x.x:20128/v1 --api-key sk-xxx
# Pass extra args to codex
omniroute launch-codex --profile glm52 -- --yolo "fix this bug"
New Codex CLI features (v0.138–v0.141)
| Version | Feature |
|---|---|
| v0.138 | Desktop app handoff (/app), v2 personal access tokens, --profile as the exclusive profile selector (legacy in-file [profiles] tables crash on startup) |
| v0.139 | web_search = "live" — native web search from code mode; oneOf/allOf in MCP tool schemas; codex doctor env diagnostics |
| v0.140 | /usage token view in-session; /import from Claude Code sessions; codex delete <SESSION_ID> subcommand; Amazon Bedrock auth via aws object in provider config |
| v0.141 | E2E encrypted Noise relay for remote executors; SQLite WAL fix; P-521 TLS support |
New config.toml fields (post-v0.137)
# Native web search (v0.139)
web_search = "live" # "disabled" | "cached" | "live"
# Separate developer system prompt (v0.138)
developer_instructions = "Always prefer functional style."
# Custom compaction prompt
compact_prompt = "Summarise the above as bullet points."
# Route /review to a cheaper model
review_model = "glm/glm-5-turbo"
# OpenAI service tier
service_tier = "fast" # "fast" | "flex"
New [model_providers.<id>] fields
[model_providers.omniroute]
base_url = "http://100.x.x.x:20128/v1"
env_key = "OMNIROUTE_API_KEY"
requires_openai_auth = false
# Static extra headers on every request
[model_providers.omniroute.http_headers]
"X-Custom-Header" = "value"
# Headers read from env vars
[model_providers.omniroute.env_http_headers]
"X-Trace-Id" = "TRACE_ID"
# Extra URL query params (useful for Azure api-version)
[model_providers.omniroute.query_params]
"api-version" = "2024-12-01-preview"
Amazon Bedrock auth (v0.140)
[model_providers.bedrock]
base_url = "https://bedrock-runtime.us-east-1.amazonaws.com"
[model_providers.bedrock.aws]
profile = "default" # ~/.aws/credentials profile
region = "us-east-1"
Multiple servers
[model_providers.omniroute-main]
base_url = "http://192.168.0.1:20128/v1"
env_key = "OMNIROUTE_API_KEY"
[model_providers.omniroute-tailscale]
base_url = "http://100.x.x.x:20128/v1"
env_key = "OMNIROUTE_API_KEY"
Claude Code — equivalent configuration
Codex CLI (config.toml) |
Claude Code (env var) | Effect |
|---|---|---|
tool_output_token_limit = 32768 |
(not directly exposed) | Per-tool history cap |
model_context_window = 400000 |
(determined by the model) | Context window |
| — | CLAUDE_CODE_MAX_OUTPUT_TOKENS=65536 |
Max tokens per response |
# ~/.bashrc — Claude Code token cap
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=65536
Quick reference — CLI flags
| Flag | Short | Effect |
|---|---|---|
--model <id> |
-m |
Overrides model for this invocation |
--profile <name> |
-p |
Loads ~/.codex/<name>.config.toml |
--config key=value |
-c |
Overrides any config.toml field (repeatable) |
--enable <feature> |
— | Force-enables a feature flag |
--disable <feature> |
— | Force-disables a feature flag |
--search |
— | Enable live web search for this invocation |
New in v0.140:
codex delete <SESSION_ID> # delete a session
codex delete <SESSION_ID> --force # skip confirmation
codex debug models --bundled # list bundled model catalog as JSON
Inside an interactive session:
| Command | Effect |
|---|---|
/model |
Opens the model picker |
/usage |
Shows token usage for this session (v0.140) |
/app |
Hands off to the desktop app (v0.138) |
/import |
Import a Claude Code session (v0.140) |
/help |
Lists all slash commands |
Long-running tasks
Two OmniRoute defaults can silently sabotage multi-hour Codex CLI sessions. Neither is a Codex CLI setting — both live on the OmniRoute side. Users migrating a config from upstream proxies that pin accounts and disable idle cutoffs often hit both and conclude OmniRoute “cannot sustain a long session.”
| Symptom | Likely cause | Knob |
|---|---|---|
| Session keeps switching accounts / prompt-cache continuity is lost between turns | Session affinity TTL is 0 (disabled) |
sessionAffinityTtlMs |
| Connection dies mid-reasoning with no client-facing prompt | Stream idle watchdog fired after 10 minutes with no upstream chunk | STREAM_IDLE_TIMEOUT_MS |
Related discussions: #7126 (long task drops), #5718 (why affinity defaults off). Tracking: #7287.
1. Session affinity — pin one conversation to one account
Default: sessionAffinityTtlMs = 0 (disabled).
Where to set it
- Dashboard → Settings → Routing → Session affinity → Affinity TTL (seconds) (
ComboDefaultsTab) - Or PATCH settings with
sessionAffinityTtlMsin milliseconds (Zod range0–86_400_000, i.e. up to 24 hours)
Renamed in #7274 from the Codex-only
codexSessionAffinityTtlMs. The legacy key is still accepted as a read-only alias; new configs should usesessionAffinityTtlMs. Affinity now applies to any provider once the TTL is above0, not only Codex — seedocs/architecture/RESILIENCE_GUIDE.md→ Session affinity.
What breaks when it stays at 0
Every turn of a multi-turn Codex conversation is routed independently by the active combo strategy and can land on a different account per turn. That breaks upstream session / prompt-cache continuity. OmniRoute only consults Codex session headers (x-codex-session-id / x-session-id / x-omniroute-session) and body fields such as prompt_cache_key / session_id when the TTL is greater than 0 (extractSessionAffinityKey in src/sse/services/auth.ts).
Recommended for a multi-hour single task
Set the TTL above the expected wall-clock length of the task (UI max is 86400 seconds = 24 hours):
| Expected task length | Affinity TTL (UI, seconds) | sessionAffinityTtlMs |
|---|---|---|
| A few hours | 14400 (4h) |
14400000 |
| Overnight / ~12h | 43200 (12h) |
43200000 |
| Full day | 86400 (24h, maximum) |
86400000 |
Opt-in is deliberate: disabling affinity favors load-balancing across accounts; enabling it favors continuity for one long agent session. This guide does not change the default — operators running long Codex tasks must opt in.
2. Stream idle timeout — do not kill quiet reasoning turns
Default: STREAM_IDLE_TIMEOUT_MS = 600000 (10 minutes). It inherits from REQUEST_TIMEOUT_MS when unset; the shared baseline is also 600000. See docs/guides/SETUP_GUIDE.md → Timeouts.
What breaks at the default
A Codex reasoning / tool turn that stays silent for more than 10 minutes with no real upstream chunk is force-closed by the SSE idle watchdog (open-sse/utils/stream.ts). The client often sees a bare connection drop — matching “stopped automatically without any notification.”
Critical detail: OmniRoute’s synthetic SSE heartbeat does not reset the idle clock. Only a real upstream body chunk updates lastChunkTime. A quiet model that is still “thinking” looks identical to a stalled upstream from the watchdog’s point of view.
Related Undici body inactivity: FETCH_BODY_TIMEOUT_MS (also defaults to the same 10-minute baseline; 0 disables it). For streaming, FETCH_TIMEOUT_MS only covers connection setup / first headers — once the stream is active, stalls are governed by STREAM_IDLE_TIMEOUT_MS and FETCH_BODY_TIMEOUT_MS.
Recommended for a multi-hour single task
In the OmniRoute process environment (.env / compose / systemd):
# Disable stream idle + body inactivity cutoffs for long reasoning turns
STREAM_IDLE_TIMEOUT_MS=0
FETCH_BODY_TIMEOUT_MS=0
Or raise them above the longest quiet gap you expect (values are milliseconds):
# Example: allow up to 2 hours of silence between upstream chunks
STREAM_IDLE_TIMEOUT_MS=7200000
FETCH_BODY_TIMEOUT_MS=7200000
Restart OmniRoute after changing these env vars.
Concrete recipe — multi-hour Codex task
- Pin the account: Dashboard → Settings → Routing → Session affinity → Affinity TTL =
43200(12h) or86400(24h max). - Raise / disable idle cutoffs in OmniRoute’s environment:
STREAM_IDLE_TIMEOUT_MS=0
FETCH_BODY_TIMEOUT_MS=0
- Keep the usual Codex
config.toml(wire_api = "responses", correctbase_url,OMNIROUTE_API_KEY) — no Codex-side affinity/idle knobs exist for these two behaviors. - Restart OmniRoute, then start the long Codex task.
Defaults decision (#7287)
| Knob | Ship default | Change in this guide? |
|---|---|---|
sessionAffinityTtlMs |
0 (off) |
No — remains opt-in (load-balancing vs continuity; see Discussion #5718) |
STREAM_IDLE_TIMEOUT_MS |
600000 (10 min) |
No — remains 10 minutes for general traffic; long Codex operators raise or disable it |
Flipping either default globally would change behavior for every client of an instance, not only Codex. Document the knobs; leave the defaults alone until an explicit operator decision says otherwise.
Diagnosing idle cuts
When the idle watchdog fires, OmniRoute logs a line shaped like:
[STREAM] Idle timeout: no data from codex for 600000ms (model: cx/gpt-5.5)
Grep for Idle timeout: no data from (or the code stream_idle_timeout / error name StreamIdleTimeoutError). The provider segment is whatever OmniRoute used for that request (codex, another provider id, or provider if unknown) — it is not always the literal string codex.
Troubleshooting
Error: wire_api = "chat" is no longer supported
Remove wire_api = "chat" from your config. Set wire_api = "responses" or omit the field (defaults to "responses" since v0.138).
Error: model not found
Verify the model exists in OmniRoute with the correct prefix. Use omniroute models list or open /dashboard/providers/<provider>.
Authentication error
Confirm OMNIROUTE_API_KEY is exported: echo $OMNIROUTE_API_KEY.
ERROR: Missing environment variable: OMNIROUTE_API_KEY
Codex validates that the env var exists before making the first request. Export
a real key for protected servers, or a non-empty placeholder such as
OMNIROUTE_API_KEY=local when your local OmniRoute instance does not
require auth. Restart the shell if you added it to ~/.bashrc or ~/.zshrc.
Connection refused
Verify OmniRoute is running and the base_url host/port is correct for your network (local vs Tailscale vs VPS).
Session crashes near context limit
Set model_context_window and model_auto_compact_token_limit explicitly. See the context window table above.
Compaction fires too late
Lower model_auto_compact_token_limit to 80–85% of the window. Never set above 90%.
Profile not loading (-p <name> silently ignored)
Confirm the file exists at ~/.codex/<name>.config.toml (no profile- prefix). Run ls ~/.codex/*.config.toml.
Long Codex task drops mid-run / switches accounts between turns
See Long-running tasks. Enable session affinity (TTL above task length) and raise or disable STREAM_IDLE_TIMEOUT_MS / FETCH_BODY_TIMEOUT_MS. Grep OmniRoute logs for Idle timeout: no data from.