Repository navigation
docs(cursor): document exec policy and catalog troubleshooting #321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -153,6 +153,33 @@ reapplies the configured name. Genuine upstream native names (e.g. `gpt-5.6-sol` | |
| "GPT-5.6-Sol") come from the pinned upstream snapshot and are never overridden by a custom display | ||
| name. | ||
|
|
||
| ### Catalog troubleshooting | ||
|
|
||
| If a model is missing from Codex, or the catalog order/visibility looks wrong, check in order: | ||
|
|
||
| 1. **`selectedModels`** on the provider — a non-empty allowlist exposes only those ids to Codex; | ||
| empty or omitted exposes all discovered models. An id not in the allowlist never reaches the | ||
| catalog. | ||
| 2. **`disabledModels`** (top level) — hides models from both the catalog and `/v1/models`, and flips | ||
| bare native GPT slugs to `visibility: "hide"`. | ||
| 3. **`liveModels: false` with empty `models`** — when live discovery is off and `models` is empty or | ||
| omitted, opencodex exposes no routed models for that provider. | ||
|
Comment on lines
+165
to
+166
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is not true for every provider: when a built-in provider such as Useful? React with 👍 / 👎. |
||
| 4. **Cursor `GetUsableModels`** — the Cursor adapter discovers models through its protobuf | ||
| `GetUsableModels` RPC, not `/models`, so a Cursor-side change can alter which ids are visible | ||
| independently of other providers. | ||
| 5. **Cache and `ocx sync`** — live catalogs are cached for about five minutes (`modelCacheTtlMs`, | ||
| default `300000`). Run `ocx sync` to force a fresh fetch and rewrite the catalog immediately. | ||
|
Comment on lines
+170
to
+171
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 6 \
'modelCacheTtlMs|ocx sync|invalidate|force.?fetch|cache' .Repository: lidge-jun/opencodex Length of output: 50378 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== docs snippet ==\n'
sed -n '160,180p' docs-site/src/content/docs/guides/codex-integration.md
printf '\n== sync references ==\n'
rg -n -C 3 '\bocx sync\b|/api/sync|modelCacheTtlMs|clearModelCache|invalidate.*cache|stale the Codex model cache|rewrite the catalog' \
docs-site src tests --glob '!**/dist/**' --glob '!**/build/**'
printf '\n== model cache implementation ==\n'
rg -n -C 4 'modelCacheTtlMs|clearModelCache|getStaleCached|setCached|cache.*ttl|ttl.*cache|invalidate.*model cache' src tests --glob '!**/dist/**' --glob '!**/build/**'Repository: lidge-jun/opencodex Length of output: 50378 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== sync command files ==\n'
fd -a 'sync' src docs-site tests | sed -n '1,120p'
printf '\n== direct references to ocx sync command ==\n'
rg -n -C 4 'ocx sync|sync command|command.*sync|/api/sync|stale the Codex model cache' src docs-site tests
printf '\n== model-cache file outline ==\n'
fd -a 'model-cache' src testsRepository: lidge-jun/opencodex Length of output: 50377 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== candidate model-cache file ==\n'
fd -a 'model-cache.ts' src
printf '\n== candidate sync implementation files ==\n'
fd -a 'sync' src | sed -n '1,120p'Repository: lidge-jun/opencodex Length of output: 315 🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 5 '/api/sync|stale the Codex model cache|clearModelCache\(|modelCacheTtlMs: 0|sync.*model cache|refresh.*catalog' \
src docs-site testsRepository: lidge-jun/opencodex Length of output: 50377 🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 4 'clearModelCache|setCached\(|getStaleCached\(|modelCacheTtlMs|sync' src/codex src/cli src | head -n 200Repository: lidge-jun/opencodex Length of output: 12880 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== src/codex/sync.ts ==\n'
sed -n '1,140p' src/codex/sync.ts
printf '\n== src/codex/model-cache.ts ==\n'
sed -n '1,180p' src/codex/model-cache.ts
printf '\n== refreshCodexModelCatalog references ==\n'
rg -n -C 6 'refreshCodexModelCatalog|syncModelsToCodex|clearModelCache\(' src/codex src/server src/cliRepository: lidge-jun/opencodex Length of output: 31145 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== src/codex/refresh.ts ==\n'
sed -n '1,220p' src/codex/refresh.ts
printf '\n== sync/catalog call sites ==\n'
rg -n -C 4 'syncCatalogModels|refreshCodexModelCatalog|gatherRoutedModels|modelCacheTtlMs' src/codex src/server src/cli
printf '\n== model-cache usage in refresh path ==\n'
rg -n -C 4 'clearModelCache\(|setCached\(|getFreshCached\(|getStaleCached\(' src/codex src/serverRepository: lidge-jun/opencodex Length of output: 23832 Clarify 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| :::caution[Other local writers] | ||
| Catalog writes (`opencodex-catalog.json`, `config.toml`) are atomic **inside** opencodex, which only | ||
| prevents half-written files when two opencodex-owned writers race. That does **not** stop another | ||
| local process, file watcher, or sync agent from rewriting catalog visibility or order after opencodex | ||
| has written. Codex keeps its separate `models_cache.json` and can refresh it independently, changing | ||
| the visible list without rewriting `opencodex-catalog.json`. If models flip unexpectedly while the | ||
| proxy is running, stop or reconfigure the competing writers, then run `ocx sync` — this is an | ||
| external-writer hazard, not a confirmed opencodex defect. | ||
| ::: | ||
|
|
||
| ## The subagent picker | ||
|
|
||
| Codex's `spawn_agent` advertises the first **5 picker-visible catalog models** after sorting by | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -134,8 +134,9 @@ advertised effort control on those models as proof of upstream-native reasoning | |
| `cursor/auto-balance`, and `cursor/auto-intelligence` entries. Explicit levels are encoded in | ||
| `requested_model.parameters` while the legacy `cursor/auto` entry retains the account/team default. | ||
| - Cursor-native local filesystem/shell/network execution is denied by default. Explicit `mcpServers` | ||
| and `desktopExecutor` integrations have separate opt-ins; `unsafeAllowNativeLocalExec` enables the | ||
| broader built-in executor and bypasses Codex approval/sandbox semantics. | ||
| and `desktopExecutor` integrations have separate opt-ins; `nativeLocalExec: "on"` enables the | ||
| broader built-in executor and bypasses Codex approval/sandbox semantics, and legacy | ||
| `unsafeAllowNativeLocalExec: true` remains equivalent only when `nativeLocalExec` is unset. | ||
|
Comment on lines
+137
to
+139
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Make legacy precedence fail closed for invalid selectors. The documentation says the legacy boolean applies only when
📍 Affects 2 files
🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| ## `azure-openai` (alias: `azure`) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -187,7 +187,8 @@ network. Only do this on trusted networks, and always set a strong `OPENCODEX_AP | |
| | `location?` | `string` | Vertex location; environment fallback is `GOOGLE_CLOUD_LOCATION`. | | ||
| | `mcpServers?` | `Record<string,CursorMcpServerConfig>` | **Cursor only.** MCP servers started over stdio or reached over Streamable HTTP; fields are listed below. | | ||
| | `desktopExecutor?` | `DesktopExecutorConfig` | **Cursor only.** External computer-use/record-screen commands; fields are listed below. | | ||
| | `unsafeAllowNativeLocalExec?` | `boolean` | **Cursor adapter only.** Opt-in escape hatch for Cursor server-driven local `read` / `write` / `delete` / `ls` / `grep` / `shell` / `fetch` execution. Defaults to `false` so remote Cursor messages cannot bypass Codex approval and sandbox enforcement. See [Cursor provider](#cursor-provider-adapter-cursor) below. | | ||
| | `unsafeAllowNativeLocalExec?` | `boolean` | **Cursor adapter only.** Legacy compatibility boolean for the Cursor server-driven local `read` / `write` / `delete` / `ls` / `grep` / `shell` / `fetch` executor. Equivalent to `nativeLocalExec: "on"` when `nativeLocalExec` is unset; an explicit `nativeLocalExec` value always wins. Defaults to `false`. Prefer `nativeLocalExec` for new configs. See [Cursor provider](#cursor-provider-adapter-cursor) below. | | ||
| | `nativeLocalExec?` | `"off" \| "codex-sandbox" \| "on"` | **Cursor adapter only.** Native local exec policy for the Cursor server-driven executor. `"off"` (default) rejects it; `"on"` is the trusted-local opt-in; `"codex-sandbox"` is accepted for backwards compatibility but is fail-closed like `"off"`. See [Cursor provider](#cursor-provider-adapter-cursor) below. | | ||
|
|
||
| For broken `openai-responses` compatibility gateways, `responsesItemIdRepair` belongs on the | ||
| provider object itself, for example: | ||
|
|
@@ -232,9 +233,17 @@ the selection is preserved on every request. They remain available when live mod | |
| `default`, just like the original `cursor/auto` entry. | ||
|
|
||
| By default, Cursor's server-driven native local tools stay **disabled**. Codex keeps using its own | ||
| tools (`apply_patch`, `exec_command`, and so on) with approval and sandbox policy. Set | ||
| `unsafeAllowNativeLocalExec` only for trusted local experiments where you accept that Cursor may | ||
| read, write, delete, list, grep, shell, or fetch on your machine **without** Codex's approval path. | ||
| tools (`apply_patch`, `exec_command`, and so on) with approval and sandbox policy. Use the | ||
| `nativeLocalExec` field to choose a policy: | ||
|
|
||
| - **`"off"` (default, safest)** — rejects all Cursor server-driven local `read`, `write`, `delete`, | ||
| `ls`, `grep`, `shell`, and `fetch` execution. Use this unless you have deliberately opted in. | ||
| - **`"on"` (trusted-local opt-in)** — always allows Cursor-native local execution for this provider. | ||
| Use it only for a trusted local experiment on a host where every data-plane caller is trusted. | ||
|
Comment on lines
+241
to
+242
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In normal Codex requests that advertise Useful? React with 👍 / 👎. |
||
| - **`"codex-sandbox"` (accepted, fail-closed)** — recognized for backwards compatibility, but | ||
| currently behaves like `"off"`. Responses `instructions` / `system` / `developer` text is | ||
| caller-controlled prose, and opencodex has no trustworthy per-request attestation that it reflects | ||
| a real Codex sandbox state, so it never authorizes native local exec. | ||
|
|
||
| ```json | ||
| { | ||
|
|
@@ -244,21 +253,25 @@ read, write, delete, list, grep, shell, or fetch on your machine **without** Cod | |
| "baseUrl": "https://api2.cursor.sh", | ||
| "authMode": "oauth", | ||
| "defaultModel": "auto", | ||
| "unsafeAllowNativeLocalExec": true | ||
| "nativeLocalExec": "off" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| The flag belongs on the **provider object** (`providers.cursor`), not at the top level of | ||
| The field belongs on the **provider object** (`providers.cursor`), not at the top level of | ||
| `config.json`. | ||
|
|
||
| You can also set it from the [web dashboard](/opencodex/guides/web-dashboard/): **Providers → | ||
| Cursor → Edit JSON**, add `"unsafeAllowNativeLocalExec": true`, save, then restart the proxy | ||
| (`ocx restart` or `ocx stop` + `ocx start`). | ||
| Cursor → Edit JSON**, set `"nativeLocalExec"` to `"off"`, `"on"`, or `"codex-sandbox"`, save, then | ||
| restart the proxy (`ocx restart` or `ocx stop` + `ocx start`). | ||
|
|
||
| The legacy `unsafeAllowNativeLocalExec: true` boolean is still accepted and is equivalent to | ||
| `nativeLocalExec: "on"` when `nativeLocalExec` is unset; an explicit `nativeLocalExec` value always | ||
| wins. Prefer `nativeLocalExec` for new configs. | ||
|
Comment on lines
+269
to
+271
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The newly documented precedence makes the existing provider guide incorrect for users who first copy this page's example with AGENTS.md reference: AGENTS.md:L78-L79 Useful? React with 👍 / 👎. |
||
|
|
||
| MCP, screen recording, and computer-use use separate `mcpServers` / `desktopExecutor` config and are | ||
| not controlled by this flag. | ||
| not controlled by this field. | ||
|
|
||
| ### Cursor integration records | ||
|
|
||
|
|
@@ -272,8 +285,11 @@ accept `headers?: Record<string,string>`. Both forms support `enabled?: boolean` | |
| one JSON request from stdin, and must write one JSON result to stdout. | ||
|
|
||
| :::caution[Security] | ||
| Leave `unsafeAllowNativeLocalExec` unset or `false` unless you explicitly want Cursor-native local | ||
| execution that bypasses Codex approval and sandbox semantics. | ||
| `"off"` is the safest default. The default loopback bind admits **any** local process without auth | ||
| (including other local users on multi-user machines), and request text such as Codex sandbox | ||
| markers never authorizes native local exec. Leave `nativeLocalExec` unset or set it to `"off"` | ||
| unless you explicitly want Cursor-native local execution that bypasses Codex approval and sandbox | ||
| semantics. | ||
| ::: | ||
|
|
||
| ## OpenRouter provider routing | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A provider-level
disabled: trueis another direct cause of every model from that provider disappearing:gatherRoutedModels()removes disabled providers before discovery, filtering, and catalog generation. Because this new section tells users with missing models to check these items in order but never mentions the provider'sdisabledswitch, they can exhaust the checklist without finding the actual cause. Add the provider-level switch beforeselectedModelsin the sequence.Useful? React with 👍 / 👎.