Integrations
The Connect page writes opencodex’s provider block into a client’s own config file, and removes it again. Each client below has its own switch:
| Client | Config file | Format | When the change takes effect | Credential |
|---|---|---|---|---|
| OpenCode | ~/.config/opencode/opencode.json |
JSON | next direct launch | OPENCODEX_OPENCODE_API_KEY |
| Pi | ~/.pi/agent/models.json |
JSON | new sessions | loopback placeholder |
| Gentle Shell | ~/.gentle-shell/agent/models.json (persistent selected home) |
JSON | after restarting Gentle Shell | loopback placeholder |
| OMP | ~/.omp/agent/models.yml |
YAML | after restarting OMP | opencodex-loopback placeholder |
| Hermes | ~/.hermes/config.yaml |
YAML | new sessions | OPENCODEX_HERMES_API_KEY |
| OpenClaw | ~/.openclaw/openclaw.json |
JSON5 | immediately, on a running gateway | OPENCODEX_OPENCLAW_API_KEY |
| Kimi Code | ~/.kimi-code/config.toml |
TOML | on restart, or /reload |
loopback placeholder |
| gjc | ~/.gjc/agent/models.yml |
YAML | new sessions, or when you open /model |
non-secret loopback placeholder |
| DeepSeek Harness (DSH) | $DSH_HOME/profiles/desktop/cordis.patch.yml (default ~/.dsh/profiles/desktop/cordis.patch.yml); $DSH_HOME/settings.yaml until DSH Desktop creates that profile |
YAML | hot reload | non-secret loopback bearer placeholder |
| MiniMax Code | ~/.minimax/config.yaml |
YAML | new sessions, or after opening the model picker | loopback placeholder |
| Prime Agent | ~/.prime/agent/models.json |
JSON | new sessions | loopback placeholder |
| ZCode | ~/.zcode/v2/provider_config.json (schemaVersion 1); legacy fallback: ~/.zcode/v2/config.json |
JSON | on restart | loopback placeholder |
| Aside | ~/.aside/u/<account>/models.json |
JSON | after fully quitting and reopening Aside | loopback placeholder |
| Raycast | ~/.config/raycast/ai/providers.yaml |
YAML | immediately on save — Raycast watches the file | none — loopback only |
| omo (Pi / senpi) | ~/.omo/agent/models.json |
JSON | new sessions | loopback placeholder |
| Qoder | settings.json in $QODER_CONFIG_DIR, default ~/.qoder |
JSON | new sessions | loopback placeholder |
| Qoder CN | settings.json in $QODERCN_CONFIG_DIR, default ~/.qoder-cn |
JSON | new sessions | loopback placeholder |
| WorkBuddy (CN) | ~/.workbuddy/models.json |
JSON | auto reload / new sessions | loopback placeholder |
| WorkBuddy AI | ~/.workbuddy-ai/models.json |
JSON | auto reload / new sessions | loopback placeholder |
| Cline CLI | ~/.cline/data/settings/providers.json and sibling models.json |
JSON pair | after stopping and restarting Cline | loopback placeholder |
| Kilo | first existing kilo.jsonc, kilo.json, opencode.jsonc, opencode.json, or config.json under ~/.config/kilo |
JSONC | new sessions | OPENCODEX_KILO_API_KEY |
| Command Code | ~/.commandcode/providers.json (on Windows with the default home: use HOME ?? USERPROFILE only if nonblank and Windows-absolute, otherwise homedir(); a separately supplied home is preserved; append .commandcode\providers.json) |
JSON | next launch of Command Code | none — keyless loopback (apiKey: false) |
| Factory Droid | ~/.factory/settings.json (%USERPROFILE%\.factory\settings.json on Windows) |
JSON | immediately via file watching | none — keyless loopback |
| T3 Code | $T3CODE_HOME/userdata/settings.json (default ~/.t3/userdata/settings.json) |
JSON | immediately — T3 Code watches the file | non-secret loopback marker |
Gentle Shell
Section titled “Gentle Shell”Choose Connect → Gentle Shell, or use the same managed-integration commands:
ocx export --client gentle-shell --jsonocx integration client enable --client gentle-shellocx integration client disable --client gentle-shellGentle Shell has a separate destination and rollback history from Pi. Only
providers.opencodex is managed; other providers and preferences stay intact.
Catalog changes refresh an already connected, unchanged block. An existing manually
copied block is a conflict, not permission to overwrite it: inspect the preview and
use the explicit overwrite confirmation only if you intend to replace it.
The saved home in ~/.gentle-shell/config.json selects the destination. A saved
custom path wins; isolated/default mode uses GENTLE_SHELL_HOME or
~/.gentle-shell/agent. GENTLE_SHELL_CONFIG selects an alternate launcher config.
These environment variables must be set on the OpenCodex process, not just in a
later shell. Selectors must be absolute paths; literal ~ and relative paths refuse.
One-off gentle-shell --home flags cannot be discovered by a background proxy.
Link mode or a home aliasing Pi refuses with a Pi-integration remedy; use Connect → Pi for that shared file instead. OpenCodex does not change launcher preferences. An unreadable/unsafe launcher config refuses; malformed JSON falls back to isolated, matching the launcher. If you move a connected home, disable it using its original selectors before reconnecting the new one. Restart Gentle Shell after a catalog update. This integration retains Pi’s loopback-only export policy and stores no real API key.
“omo” names three products that share the ~/.omo folder. The omo tab manages Pi-based omo
(the senpi engine) through ~/.omo/agent/models.json, as in the table above. Codex-based omo
(LazyCodex) gets its own section on the same tab, shown only when LazyCodex is detected, described in
omo (Codex / LazyCodex) role models. OpenCode-based omo
(oh-my-opencode) keeps its own config under OpenCode; opencodex does not read or write it.
The managed Pi-based omo block uses the Chat dialect (api: "openai-completions") by default. Chat
drops the previous step’s reasoning across tool calls, which long omo tool loops notice. To have
enable and every refresh write the Responses dialect instead, set this config-file-only key in
your opencodex config:
{ "omoClientExport": { "api": "openai-responses" } }Restart the proxy to load the edited config (ocx service restart for a service install, or ocx stop, then ocx start), then refresh the omo integration from Connect → omo in the dashboard.
Refreshing alone uses the running proxy’s config and does not reload the config file.
The setting applies only to omo; Pi, Gentle Shell and the other Pi-format clients keep
openai-completions. To return to Chat, remove the key, restart the proxy in the same way, then
refresh Connect → omo. An unknown value is ignored.
opencodex still owns only providers.opencodex in ~/.omo/agent/models.json, so other providers
in that file are left as they are.
Qoder CN and WorkBuddy editions
Section titled “Qoder CN and WorkBuddy editions”In Connect, enable Qoder CN, WorkBuddy (CN) or WorkBuddy AI, then choose an OpenCodex model in the client’s custom-model picker. The proxy must stay running. The first enable saves a snapshot; disable removes only the generated contribution, and Undo restores the snapshot. Existing models and unrelated settings remain intact. These are separate connections from Global Qoder and from each other.
The same operations are available through the CLI (replace workbuddy with
workbuddy-ai or qoder-cn for the other editions):
ocx integration client enable --client workbuddyocx export --client workbuddy --jsonocx integration client disable --client workbuddyQoder CN honors QODERCN_CONFIG_DIR. Both WorkBuddy editions honor their native
WORKBUDDY_CONFIG_DIR, then CODEBUDDY_CONFIG_DIR; unset overrides keep the regional
roots in the table. Overrides must be absolute, ~, or start with ~/ or ~\. WorkBuddy’s legacy
array configuration and its object-form models list are both supported. If an existing
availableModels restriction excludes OpenCodex models, remove that restriction in
WorkBuddy before enabling. Project-level settings can override user-level models.
Connections require an unauthenticated loopback listener; no upstream credentials are copied into these clients. WorkBuddy receives the full Chat Completions URL and the capabilities declared by the OpenCodex catalog. Restart the client or start a new session if its model picker has not reloaded yet. Configuration/lifecycle tests do not establish end-to-end compatibility with every upstream model or every client release.
Doubao Desktop and Devin currently have no verified arbitrary-provider configuration contract in this integration. They have no enable switch; adding a local JSON file alone would not make their model pickers consume it.
Qoder IDE / qodercli supports export and managed configuration through the Dashboard and CLI:
ocx export --client qoder --jsonocx integration client enable --client qoderocx integration client disable --client qoderQoder uses settings.json in QODER_CONFIG_DIR when that variable is set, and
~/.qoder/settings.json otherwise; a relative QODER_CONFIG_DIR is refused. Enable merges only providers.opencodex, preserving other providers, custom models,
and settings. Models appear once in the provider catalog; duplicating them in
modelConfigs.customModels causes Qoder to reject the provider. Refreshing an older managed
integration removes its recorded duplicate rows; user-edited rows remain conflicts. Disable removes the recorded OpenCodex entries; restore uses the existing
snapshot workflow. The provider’s default model is the first model in the sorted export.
If modelConfigs.customModels already holds an opencodex row for an exported model that
OpenCodex did not write, enable, refresh, overwrite and disable report a conflict and leave the
file unchanged; remove or rename that row in Qoder’s settings, then try again. Rows for other
providers or for models outside the export are left alone.
Provider models use their catalog contextWindow, falling back to 128000 input tokens.
Restart Qoder after refreshing an existing integration to reload the provider registry.
The generated API key is a non-secret placeholder: Qoder integration requires loopback
access and refuses an authenticated non-loopback bind. When the dedicated unauthenticated
loopback listener is enabled, exports point Qoder at that listener. ocx sync and catalog
changes refresh an already enabled Qoder integration like the other managed clients.
Model IDs containing , or ] (for example kimi/k3[1m]) are exported like any other model.
T3 Code
Section titled “T3 Code”T3 Code drives the Codex and Claude Code CLIs already
installed on the machine. Its Codex threads route through opencodex once the
Codex integration is enabled, because T3 Code lists Codex models
through codex app-server, which reads $CODEX_HOME/config.toml like every Codex client. Routed
models then appear under Codex with no T3 Code switch, as long as T3 Code runs Codex with the same
Codex home opencodex manages (CODEX_HOME, default ~/.codex). A T3 Code Codex instance pointed at
another home does not see the routed catalog.
The T3 Code switch adds the Claude side. It needs the Messages API, which every Claude turn uses: while Messages is off on the API page, enable and refresh are refused, and disable still works.
ocx export --client t3code --jsonocx integration client enable --client t3codeocx integration client disable --client t3codeEnable writes one provider instance, providerInstances.opencodex, into T3 Code’s server settings
and leaves every other instance and setting alone. It is a Claude instance named opencodex that
runs your own claude binary with ANTHROPIC_BASE_URL set to the proxy, the non-secret
opencodex-proxy marker as ANTHROPIC_AUTH_TOKEN, an explicitly empty ANTHROPIC_API_KEY, and
CLAUDE_CODE_PROVIDER_MANAGED_BY_HOST=1 so a settings.json env block in your Claude config cannot
re-point it. Each routed model is a custom model keyed by its opencodex selector
(provider/model), which T3 Code sends verbatim. It appears in T3 Code’s model picker under
opencodex. T3 Code reloads the file on its own, so no restart is needed; catalog changes and
ocx sync refresh the instance.
T3 Code keeps its data in T3CODE_HOME when set, otherwise ~/.t3. Set the same variable on the
opencodex process if you relocate it; a relative value is refused. On Windows, use a fully qualified
drive path (C:\shared\t3) or UNC path (\\server\share\t3); paths without a drive, such as
\shared\t3 or /shared/t3, are refused. ~ expands against the effective user home.
The marker is not a credential, so the integration needs loopback access and refuses an authenticated non-loopback bind unless the
unauthenticated loopback listener is enabled.
Treat the instance as opencodex’s. Marking one of its variables Sensitive in T3 Code, adding a custom model to it, or toggling it off rewrites the block. The switch then shows a conflict instead of overwriting your edit, and Replace restores the generated block. To use different settings, add your own Claude instance in T3 Code beside it.
Generated catalogs include only enabled models from each provider selection. This applies to both downloads and managed integrations, including Pi and Aside. The management model list still shows the full roster so you can enable additional models.
ocx uninstall disables recorded integrations, including all owned Aside profiles, before deleting
OpenCodex’s recovery state. Unreadable ownership, missing profile registration or a conflicting edit
stops that deletion. Cleanup is sequential: earlier successful disables are not undone when a later
one fails. If compensation also fails, a client file may be left in an intermediate state. Inspect
the reported client files and retained recovery snapshots before retrying; retained state does not
mean every client was restored or left unchanged.
For Gajae built-in presets, keep the routing choice in ~/.gjc/agent/config.yml:
modelProfile: proxyProvider: opencodex proxyMode: alwaysKeep your chosen modelProfile.default to apply it when plain gjc starts. The managed integration owns only providers.opencodex in models.yml; refreshing or disabling that provider does not rewrite your preset choice. Refresh the integration after changing the exported model selection.
GJC models with a supported reasoning-effort ladder export reasoning: true, thinking.levels, and compat.supportsReasoningEffort, so GJC can offer an effort choice. Native Codex models receive their standard ladder even when the catalog omits it. Models without a known ladder omit these fields; none and ultra are not offered because none sends no effort and ultra folds to max on the wire. Refresh the integration to update these model options.
The managed OpenCode integration owns two fragments: provider.opencodex (opencode V1) and
providers.opencodex (opencode V2). Both carry the same declared per-model reasoning choices and
defaults — the legacy block spells a model’s default in its options and its choices as a variant
map, the V2 block uses the model’s settings and a native variant array — so both are written and
kept in sync; they name the same provider and model ids, and opencode V2 merges them into one
provider entry. Apply, Refresh, Disable, and Restore act on both fragments, and your other
providers, agents, keybinds, and MCP entries stay untouched.
Both fragments are built from the effective canonical model metadata — as are the Kilo export and
the ocx opencode launcher. An explicit custom override always wins, and a ladder you cleared to
empty stays empty; a reasoning ladder or default is never invented for a model that declares none.
An authoritative context limit is carried with an output budget beside it: a known output value —
explicit or from catalog metadata — wins, otherwise the schema-required 32000 fallback clamped to
the context, and without an authoritative context the whole limit block is omitted. Capabilities,
effort ladders, defaults, and an optional input limit appear only when known; the V2 block’s
native capabilities object requires a known tools value, so when tool support is unknown it is
omitted entirely rather than written partially, and the legacy modality declarations stand.
“No ladder invented” still writes the controls, never choices: the V2 block always carries an
explicit variant array — empty when nothing is declared, because omitting it would make OpenCode
synthesize low/medium/high — and a model with known fixed reasoning but no adjustable ladder gets
disabled-only suppression instead of an effort: the legacy OpenCode block and Kilo both disable
every rung id the client would otherwise generate. Neither adds a selectable effort. The
selectable variants override the per-model
default — none included, offered only when the model’s own declared ladder contains it. Whatever
the client selects, the proxy’s pinned upstream reasoning policy still governs the request that
leaves it.
Managed DSH support has a compatibility floor of DSH 0.1.0-rc.6. DSH 0.1.7+ reads provider
routes from [id=llm-pi-ai].config.providers.opencodex in the Desktop profile patch,
$DSH_HOME/profiles/desktop/cordis.patch.yml, which DSH hot reloads. OpenCodex writes that row when the Desktop profile and its patch exist. If
$DSH_HOME/profiles/desktop/package.json exists but cordis.patch.yml is missing, Apply refuses:
create cordis.patch.yml containing [] (the empty patch DSH writes for a new profile), then
enable the integration again. Only when there is no Desktop profile does OpenCodex use
llm-pi-ai.providers.opencodex in $DSH_HOME/settings.yaml. OpenCodex owns only that provider
fragment: Apply and Refresh replace it, Disable removes it, and Restore puts back a recorded
snapshot. These operations do not change the user’s default model or the native
deepseek-official provider.
The managed DSH integration is currently loopback-only and never writes a real credential.
MiniMax Code follows MINIMAX_DATA_DIR, then MAVIS_DATA_DIR, before falling
back to ~/.minimax. Its managed block owns only custom_provider.opencodex.
It does not change defaultModel, the selected MiniMax credential source, or
the user’s MiniMax login. Choose a custom_provider:opencodex/<provider/model>
entry in MCode after connecting it. Refreshing the integration also refreshes
authoritative per-model context windows and reasoning-effort choices; unknown
capabilities are omitted, and MCode’s session-owned current effort is preserved.
Prime Agent follows PRIME_AGENT_CODING_AGENT_DIR before falling back to
~/.prime/agent; a relative value is refused so the proxy and the agent cannot
disagree about which file is meant. Its managed block owns only
providers.opencodex, so other providers and any modelOverrides you have set
stay untouched. Prime Agent reads models.json when a session starts, so start
a new session after connecting it.
Aside keeps a separate model catalog for each registered profile, including local profiles. OpenCodex lists all registered profiles, including local profiles, and can synchronize them together or control one profile at a time. Switching an integration never changes Aside’s active account. A prior Aside connection enables all profiles by default; individual exclusions survive later syncs.
One caveat specific to Aside: the running app rewrites models.json itself, so
fully quit and reopen Aside after applying, the same way Claude Desktop needs a
restart. Aside’s block is loopback-only and never carries a real credential.
The managed Raycast integration supports macOS and Windows. Custom Providers
is a Raycast Pro feature: on a free plan the file is still written, but
ocx integration client status --client raycast and the Connect page report
a warning, because Raycast will not read it. On macOS or Windows, open Raycast →
Settings → AI → Reveal Providers Config once so the ai folder exists.
On these supported platforms, opencodex uses that folder as its install signal
and reports the client as not installed until it exists. Linux is unsupported,
even if the folder exists.
The status field aiDirPresent reports only whether ~/.config/raycast/ai exists,
independently of whether the Raycast app is installed or the platform is supported.
It does not prove that Raycast is installed or usable. The CLI prints plan on a
separate line and adds the macOS/Windows setup instruction when aiDirPresent is
false; --json preserves the raw status, including the nested raycast block.
Raycast reads ~/.config/raycast/ai/providers.yaml on macOS and Windows alike and
does not honor XDG_CONFIG_HOME, so that path is not relocatable.
The managed block is one element, id: opencodex, in the file’s providers
sequence: name: OpenCodex, base_url: http://<host>:<port>/v1, and every
routed model with its abilities — the exporter sets tools and system_message to
true as a client-export convention, vision follows the catalog’s input modalities, reasoning_effort
is set when the model has an effort ladder, and temperature is turned off for
reasoning models. Other providers in the file are preserved, and disable removes
only the OpenCodex element. Raycast picks up the change as soon as the file is
saved, no restart needed; the models appear in Raycast’s model picker grouped
under OpenCodex. Raycast supports optional api_keys, but OpenCodex intentionally
omits them and refuses non-loopback or admission-authenticated targets; this integration
cannot supply OpenCodex’s required admission header.
The macOS private preference is only an advisory Pro hint; Windows never reads it and
reports the plan as unknown. Plan detection does not authorize or block a write.
The export metadata has no authoritative tool-support flag, so tools: true does not
prove every routed model supports tools. Vision and effort flags follow catalog metadata;
turning temperature off for an effort ladder is conservative export behavior.
Provider values are preserved; YAML formatting and comments are not guaranteed to survive.
The format is documented at
manual.raycast.com/ai/custom-providers.
Raycast CLI exports and dashboard downloads use the running server’s destination and
admission policy, including a configured unauthenticated loopback listener. ocx ensure
does not refresh Raycast from its saved configuration snapshot: that can differ from the
running server. Server startup and explicit sync remain the catalog refresh paths.
Cursor has a tab but is not one of these switches. Regular Cursor calls custom endpoints from its own backend, so a loopback proxy is unreachable without a public tunnel, and Cursor’s separate Private Inference build is configured inside Cursor. The Cursor tab is read-only: it detects which build is installed, shows the Base URL and API Key to paste into Cursor, and reports the last request Cursor made to the proxy. See Cursor Private Inference.
Paths honor each client’s own environment override where it has one. For OMP,
OMP_PROFILE wins over PI_PROFILE by presence, even when explicitly empty. A named profile
uses PI_CONFIG_DIR as a directory name relative to the user’s home and ignores PI_CODING_AGENT_DIR; without a named profile,
PI_CODING_AGENT_DIR wins. OMP supports provider-level headers, but this initial integration
is deliberately loopback-only; remote x-opencodex-api-key wiring is deferred. Relocated
HERMES_HOME, KIMI_CODE_HOME, and XDG_CONFIG_HOME paths are likewise followed rather than
guessed at. The table lists each client’s default.
For native OpenAI models, the generated OMP block selects its model-level Responses API, preserving image input and reasoning-effort controls. Routed models retain the provider’s Chat Completions dialect so their existing adapters remain compatible.
OpenClaw has several, and they do different jobs. OPENCLAW_CONFIG_PATH selects the
file; OPENCLAW_STATE_DIR, OPENCLAW_PROFILE and OPENCLAW_HOME select the state
directory, which is also what detection looks at — so a profile or relocated home
still reads as installed, while a config-path override moves only the file. If you
are still on the older .clawdbot layout, that is found too: the modern directory
wins when it exists, and the legacy one is used when it is the only one there.
On Windows, overrides requiring absolute paths must include a drive letter
(C:\Users\me\client) or a UNC share (\\server\share\client). Drive-less
rooted forms such as \Users\me\client or /client/config are refused because
they depend on the current drive. A selector documented to support ~ still expands
it against the effective home; Gentle Shell does not expand ~.
These must be absolute paths or start with ~. A relative one is refused rather
than resolved, because it would mean whatever directory each process happened to
start in — and that path is stored with the backup, so it has to name the same file
tomorrow as it did today.
opencodex reads these from its own environment. If your gateway runs with a profile or a relocated home, start opencodex with the same variables set, or it will correctly follow a different installation.
The other five surfaces are not switches
Section titled “The other five surfaces are not switches”API Keys manages opencodex’s own credentials and is not a client at all. Codex CLI is wired by the proxy service itself — starting opencodex applies it, stopping it restores native routing — so there is nothing to toggle per-file. Claude keeps its own enable flag and Desktop’s Save/Apply flow, and Grok Build keeps its select-then-apply model fence. Those semantics predate this feature and are unchanged. Cursor writes nothing at all: its tab shows detection, the gateway values, and the last request seen, and the rest happens inside Cursor Private Inference.
Rollback
Section titled “Rollback”Every successful write takes a snapshot of your file first, so the state you had is always recoverable:
- Undo appears on the newest operation when your file still matches what we wrote.
- Restore this point… appears on older operations, or when the file changed after that operation. Restoring across such a change asks a second time before replacing your newer edits — and backs them up too, so that restore is itself undoable.
- Ten backups are kept per client. Beyond that, the oldest snapshot files are removed and their history rows read Backup expired.
Disable removes only the entries opencodex recorded as its own. If your file changed
after we wrote it, what happens depends on whether our own entries are still intact
and on the file’s format. For strict-JSON configs (OpenCode, Pi), an edit next to
our block — adding an MCP server, a provider of your own — shows as Update needed:
refreshing merges around your entries and keeps them, though formatting may be
normalized. The exception is something JSON cannot rewrite exactly — a non-finite
number like 1e999, a number a rewrite would round (a very large integer, or one
so small it collapses to zero), -0, the same key written twice in one object, or nesting deeper
than 1000 levels — which locks the switch instead, so nothing is silently changed or dropped.
OMP, DSH and Hermes are unaffected by sibling edits too, for a different reason: their writers
patch only their own managed provider ranges byte-wise, so the rest of the
file is never rewritten. For the remaining formats that can carry comments
(OpenClaw, Kimi Code, gjc, MiniMax Code, Raycast — JSON5 and TOML
written as whole documents, or generic YAML without source preservation), or
whenever our own entries were edited, the switch locks and disable refuses rather
than guessing which edits were yours.
That lock is no longer a dead end. A conflicted client shows Replace next to its switch, on both the overview card and the client’s own page. It replaces whatever holds our settings with the block opencodex would write, and it asks first: the dialog names the file, says what is lost, and points at the snapshot that makes it undoable. The switch itself stays locked, because the switch cannot know which edits you meant to keep — only you can say so. Nothing else is relaxed: a file we cannot parse, or one whose structure we cannot reason about, still refuses.
Hermes session affinity
Section titled “Hermes session affinity”The generated providers.opencodex block includes session_affinity_header: session-id for all
models. Retain this generated default unless you deliberately choose log-only correlation:
changing session_affinity_header from session-id to x-hermes-conversation-id replaces the
header sent by Hermes. Because OpenCodex treats x-hermes-conversation-id strictly as a request-log
correlation fallback and not as a native routing or turn-termination identity, switching to
x-hermes-conversation-id removes native session affinity and lane assignment while correlating
agent loops in request logs. This names a header; Hermes supplies its dynamic conversation identifier. OpenCodex does
not write a shared static identifier or change api_mode to enable affinity.
Use a Hermes version supporting per-provider request options. Older versions may ignore or discard the option; a valid configuration alone does not prove that Hermes sends the header. Conversation isolation, compaction lineage and auxiliary/child requests follow Hermes’ affinity semantics. This setting does not guarantee a particular cache-hit rate.
For an existing managed integration, open Connect → Hermes, review Apply, and confirm the update. Until then, it shows Update needed and implicit catalog refresh leaves it unchanged, including its model list. Reading the page does not upgrade the configuration. After Apply, normal catalog refresh resumes and retains the setting; Replace also includes it.
If you already added exactly session_affinity_header: session-id inside the managed block, Apply
can adopt it when all other managed settings still match the ownership record. This is the narrow
exception to the conflict rule above: other edits, a different header name, or a block without a
matching ownership record still require conflict resolution. Unrelated YAML settings and comments
remain untouched, and the existing snapshot and Restore workflow applies to the upgrade.
Hermes profiles
Section titled “Hermes profiles”Hermes keeps a separate home for each bot profile. The default profile uses
~/.hermes/config.yaml (%LOCALAPPDATA%\hermes\config.yaml on Windows, or $HERMES_HOME/config.yaml),
and each named profile created with hermes profile create <name> uses
<Hermes home>/profiles/<name>/config.yaml. See Hermes’
profiles guide.
When named profiles exist, Connect → Hermes lists every profile with its own status. Select a
profile to apply, disable, replace, or restore OpenCodex for that profile alone; each profile keeps its
own backups and rollback history, and the other profiles stay unchanged. Every profile keeps its own
model.default and other providers: OpenCodex adds only providers.opencodex. With no named profiles,
the page works as before and changes the default profile.
Catalog changes and ocx sync automatically refresh only the default profile resolved from the
running proxy’s environment (including HERMES_HOME). Named profiles keep their previous catalog.
After changing the model selection, open Connect → Hermes, select each named profile showing
Update needed, and review and confirm Apply. The CLI has no named-profile selector.
With an unchanged catalog, refresh stays Current after edits to comments or settings outside
providers.opencodex; it changes neither the file nor the recorded baseline. When a catalog update
is pending, automatic refresh refuses any Hermes file changed since the last Apply, including those
unrelated edits. When the catalog shows Update needed, review and Apply
the pending catalog change to resume automatic updates. Apply with an unchanged catalog is a no-op
and does not accept a new file baseline.
The management API selects a profile with profile=<name> on /api/client-integrations/hermes and with
client=hermes&profile=<name> on the preview, journal, and restore endpoints;
GET /api/client-integrations/hermes/profiles lists the profiles. A directory under profiles/ counts
only when Hermes would list it: a valid name, a Hermes identity file such as config.yaml or SOUL.md,
and no deletion marker. A profile reached through a symbolic link or junction is not offered. If
HERMES_HOME later points at a different Hermes home, a profile with the same name there is refused
until you disconnect the original one, so OpenCodex never loses track of a file it changed.
Preview and confirm changes
Section titled “Preview and confirm changes”In the dashboard, Apply, Replace, Disable, and Restore begin with a preview. The dialog shows exactly which managed settings will change, including the bounded change paths and whether each change adds, updates, or removes a value. Review that plan before confirming.
When a plan reports no changes, it means the managed client document already has the requested state. For a selected Aside profile, confirming can still save that profile’s sync preference even though the managed document does not change.
If the file changes after you review it, the write is refused as stale. The dialog replaces the old plan with the updated one and asks you to confirm again; it never retries the write automatically. If a preview is temporarily unavailable, reload the page normally and start the action again.
Aside uses the same preview and confirmation flow for one selected profile at a time. Sync all profiles remains a separate bulk action and is not bound to one combined preview.
What to expect, honestly
Section titled “What to expect, honestly”If a generated managed path cannot be represented safely, that client reports an unsafe status and its preview refuses the operation. Attempts to apply, overwrite or disable return HTTP 409 without changing the configuration. Other clients remain available.
Managed configuration files are limited to 4 MiB (4,194,304 UTF-8 bytes).
The shared integration reader refuses larger files as unsafe to read, and the writer
refuses a merged configuration that exceeds the same limit with write_failed, leaving
the original configuration unchanged. Cline’s recovery marker also has this limit;
it includes both files’ before-and-after contents plus metadata, so Cline can hit the
limit even when each individual file is smaller than 4 MiB.
Back up the client configuration yourself, then remove unneeded entries or reduce the exported model list before retrying. Replace does not bypass the limit. If the configuration must stay larger, manage it manually instead of using the Connect switch.
Formatting is generally not preserved. Applying parses a config and writes it back
out, so JSON, JSON5 and TOML may be reformatted and comments in JSON5 or TOML are lost.
OMP, DSH and Hermes are the exceptions: OMP and Hermes patch only providers.opencodex.
DSH patches only [id=llm-pi-ai].config.providers.opencodex in the Desktop profile patch,
or llm-pi-ai.providers.opencodex in settings.yaml when there is no Desktop profile.
Their YAML writers preserve unrelated provider comments and formatting byte-for-byte. If that exact
source range cannot be identified safely, the operation refuses instead. For other clients, use
Restore when you need the previous file bytes: the snapshot is a verbatim copy.
If a value cannot be rewritten faithfully, the switch refuses instead. The round
trip covers the value kinds these formats use in practice, and where it does not —
a TOML file using inf or nan, for instance, which the parser available to us
cannot read back accurately — applying stops and says so rather than writing a
changed value and calling it success. You will see the file named and nothing on
disk will have moved. Editing that file by hand still works; it is only our
automatic rewrite that declines.
TOML dates and times also refuse managed rewrites: the merge step would turn these typed values into quoted strings. This includes values inside arrays and inline tables. Quoted date strings remain supported; an unquoted date must be preserved by editing the configuration manually.
Pi, Kimi Code, gjc, MiniMax Code, Prime Agent, Aside, Raycast, omo and the managed DSH integration only work against a loopback bind.
The first four have no config field for the x-opencodex-api-key header a non-loopback bind
requires. DSH has a generic headers map, but rc.6 does not document that dedicated admission
header as a supported integration contract, so the managed writer fails closed instead of
guessing. Prime Agent’s provider block does accept headers, but remote credential wiring is
deferred from its initial integration. Give them loopback access through an SSH tunnel or a local forwarder that adds the header.
The generated OMP integration is also deliberately loopback-only. OMP does support
provider-level headers, but this initial integration does not emit remote
x-opencodex-api-key credential wiring. Manual remote OMP configuration is outside the
managed integration for now.
Kimi Code cannot hold an environment reference, so its config carries an
opencodex-loopback placeholder rather than a key. No real credential is ever written
into any client config.
For ocx opencode, the launcher’s provider blocks win. That launcher injects
provider.opencodex and providers.opencodex through OPENCODE_CONFIG_CONTENT, which
outranks the same entries on disk — the rest of your opencode config still applies as
usual. Those blocks are built from the same effective canonical model metadata as the
exports, so the launcher and the managed integration describe the same models, limits, and
reasoning choices. The switch here is what matters when you launch opencode directly.
From the terminal
Section titled “From the terminal”The same operations are available headlessly:
ocx integration client statusocx integration client enable --client hermesocx integration client disable --client hermesocx integration client history --client hermesocx integration client restore --op <opId> [--confirm-drift]Opt-in alias model names
Section titled “Opt-in alias model names”Hermes and Command Code accept --model-names alias|routed. The default is
routed, independent of preferModelAliasIds. For example:
ocx export --client hermes --model-names alias --jsonocx integration client preview --client hermes --operation apply --model-names alias --jsonocx integration client enable --client hermes --model-names aliasocx integration client enable --client commandcode --model-names aliasExport is a preview: it uses an already loaded, current catalog and writes no
client configuration or integration state. If that catalog is unavailable, load
the model catalog separately and retry. --out explicitly writes an export
artifact; it does not connect the client.
The connection stores the mode. Omitting the flag on later Apply (including the
dashboard) preserves it; explicit --model-names routed resets it. A qualified
alias such as AC/AC-model-x replaces acme/vendor-model-x only when the router
proves the same provider and model. Missing or unsafe aliases and collisions
fall back to the routed key. Combos retain their existing keys.
After an alias rename or removal, run ocx sync or explicitly Apply the connection:
the next successful managed refresh replaces the old key with the newly validated
alias or routed fallback. Alias edits do not atomically update client files.
Refresh remains idempotent and refuses foreign edits. If an alias changes while
Apply or refresh waits, the write is refused; retry against the current catalog.
Each named Hermes profile
stores its own mode; automatic catalog refresh covers only the default profile,
so named profiles need explicit Apply. Other exporters retain routed names.
Preview and bind a terminal write
Section titled “Preview and bind a terminal write”The existing direct commands remain supported. To bind a write to a reviewed observation, inspect status and save a preview first:
ocx integration client preview --helpocx integration client status --client hermes --jsonocx integration client preview --client hermes --operation apply --json > hermes-preview.jsonReview state, foreignEdit, structural changes, canApply and willChange.
Valid refused and no-op plans exit 0 because inspection succeeded; nothing was
applied. The plan contains no secret values or full file diff. It uses existing
passive catalog evidence and never secretly refreshes providers. If unavailable,
resolve catalog availability separately before requesting a new preview.
Only after reviewing an applicable plan and choosing the write:
PLAN_FINGERPRINT="$(jq -er 'select(.canApply == true) | .fingerprint' hermes-preview.json)"ocx integration client enable --client hermes --plan-fingerprint "$PLAN_FINGERPRINT" --jsonocx integration client status --client hermes --jsonFingerprint is concurrency evidence, not authorization. Commit accepts pN:
followed by 32 lowercase hex digits; a refused pN:unbound cannot commit. Repeat
exact client/action/profile/options. Preview overwrite maps to enable with
--overwrite-conflict; preview disable maps to disable. Stale commit exits 5,
prints re-preview guidance on stderr and leaves stdout empty. It never adopts a
replacement fingerprint or retries. Other failures use usage 2, not-found 4,
runtime/malformed 1, with safe stderr rather than a JSON error envelope.
Inspect restoration drift before replacing it
Section titled “Inspect restoration drift before replacing it”Use a real history opId; op-example below is fictional:
ocx integration client history --client aside --profile 1 --jsonocx integration client restore --op op-example --client aside --profile 1 --preview --jsonIf drift is refused, inspect the edits. When replacing them is explicitly intended,
preview with --confirm-drift, review, then bind that same intent:
ocx integration client restore --op op-example --client aside --profile 1 --preview --confirm-drift --json > restore-preview.jsonAfter review:
RESTORE_FINGERPRINT="$(jq -er 'select(.canApply == true) | .fingerprint' restore-preview.json)"ocx integration client restore --op op-example --client aside --profile 1 --confirm-drift --plan-fingerprint "$RESTORE_FINGERPRINT" --jsonocx integration client status --client aside --profile 1 --jsonAside preview/bound writes require one profile, not an aggregate target. Generic
restore omits client/profile; the server derives the non-Aside client from opId.
The returned plan does not contain opId or all original command inputs, so retain
that context. --preview cannot combine with --plan-fingerprint.
Retire an old recovery record
Section titled “Retire an old recovery record”This permanently retires the record and backup; it does not restore or disable the integration. After inspecting history and choosing to lose that recovery point:
ocx integration client history remove --op op-example --client aside --profile 1 --yes --jsonocx integration client history --client aside --profile 1 --jsonUse the actual selected opId. No selector addresses the global journal; --client aside
addresses aggregate Aside history and optional --profile narrows it. Other client
selectors are refused for deletion. The newest row is protected. A receipt with
snapshotRemoved:false is committed retirement with incomplete cleanup and exit 1;
it is not rollback and must not trigger repeated deletion. journal remove is an alias.
--overwrite-conflict is the terminal form of Replace:
ocx integration client enable --client zcode --overwrite-conflictLike --confirm-drift, it is never assumed — without it a conflict is still refused.
It applies only to enable; forcing a disable over a conflict would delete a block
we never wrote, so that combination is rejected.
For MiniMax Code, connect the provider once and launch through the checked wrapper:
ocx integration client enable --client mcodeocx mcodeOnce connected, ocx sync and POST /api/sync refresh owned MCode, Pi, Aside,
Raycast, omo, OpenCode, Kilo, and Hermes catalogs with the current model selection, context windows, and
reasoning-effort ladders. Background catalog changes also refresh the connected Hermes default profile unattended;
an edited Hermes file with a pending catalog update is refused and reported; a never-connected or disabled integration stays untouched.
Named profiles require explicit Apply. Proxy startup refreshes an owned Raycast catalog. Changes to
model visibility, provider selection, or presets also refresh connected Pi, Aside,
Raycast, omo, OpenCode, Kilo, and Hermes catalogs.
Missing, foreign-edited, or unsafe blocks stay untouched, as do previously owned blocks
you removed manually.
An enabled Aside profile is an exception to the usual owned-only refresh: if its account
directory exists and it has never had an owned block, sync may create its first block when
that slot is empty. A prior Aside connection enables this behavior for all registered
profiles by default. Sync does not create missing account directories or replace manual blocks.
A refused or overlapping refresh is reported separately for each client. Start a new Pi,
OpenCode, or Kilo session or fully quit and reopen Aside to load the updated file.
Aside refresh requires a compatible running proxy.
If Models reports “Model selection saved” together with a client-refresh warning, the
selection is already saved; one or more client files could not be updated. The warning names
the affected client and Aside profile, when applicable, and explains the refusal. Open
Connect to inspect that client or profile before starting a new session. Resolve the
reported issue, then retry ocx sync; an overlapping operation must finish first. If the
warning includes a backup path or says recovery did not finish, inspect that recovery state
before retrying. A successful selection save alone does not confirm client-file recovery.
The separate MiniMax platform CLI (mmx) is not a file-toggle integration. Its text
commands use MiniMax’s Anthropic-compatible endpoint, so OpenCodex provides a
credential-isolated, loopback-only launcher:
ocx mmx text chat --model anthropic/claude-opus-5 --message "Hello"ocx mmx text repl --model openai/gpt-5.6-solOnly mmx text chat and mmx text repl are proxied. Run plain mmx for
MiniMax-native image, video, speech, music, vision, search, quota, auth, config, file
and update commands. The wrapper uses a temporary config containing only a non-secret
loopback placeholder; it never loads your ~/.mmx OAuth or API-key credentials, and
it refuses --api-key, --base-url and --region overrides. See
MiniMax clients for the complete workflow and limits.
--confirm-drift is never assumed. If the file changed after the operation you are
restoring, the command refuses and tells you, because replacing your newer edits is your
decision to make.
Client details were verified against each project’s own configuration format; see the
research notes in devlog/_fin/260802_client_toggle_api/002_client_toggle_matrix.md
for what was checked and when.
Aside profile controls
Section titled “Aside profile controls”Aside profile controls and the Aside refresh performed by ocx sync require a running
ocx proxy that supports the Aside profile APIs. Updating the CLI alone does not update an
already-running proxy. If the proxy is unavailable or too old, the Aside operation cannot
complete; the CLI never falls back to writing Aside profile files locally.
Upgrade the ocx installation used by the proxy, then restart the proxy (or start it if it
is stopped). Retry ocx sync or the profile command. After the profile files update
successfully, fully quit and reopen Aside so it loads the new catalogs.
ocx integration client status --client aside --jsonocx integration client enable --client asideocx integration client disable --client aside --profile 1ocx integration client history --client aside --profile 1ocx integration client restore --client aside --profile 1 --op <opId>The profile number is the account ID shown by the status command. Omitting --profile on an
Aside toggle applies the desired state to every registered profile. A per-profile change leaves
siblings unchanged. Desired sync settings are saved before file changes; actual state and any
refusal are reported for each profile. A partial bulk result is not an all-applied success and
the CLI exits nonzero. Undo restores the selected profile’s synchronization intent as well as
its file, so a later sync does not silently reverse Undo.
The profile API returns HTTP 200 for a
successful bulk operation and HTTP 207 with ok: false if any profile refuses. Inspect every
entry in results: successful profiles are not rolled back when another fails. Desired
settings remain saved, so retry after addressing the affected profile rather than assuming
the entire change failed. If saving those settings fails, no profile files are changed.
For a targeted refresh of eligible Aside profiles, rather than broad catalog sync:
ocx integration client sync --client aside --jsonocx integration client status --client aside --jsonThis uses the existing attested Aside owner and has no profile selector or local
fallback. {results:[]} exits 0 and means no eligible profiles, not applied writes.
Any failed row returns exit 1 while successful profiles remain updated. Preserve
per-profile refusal, residual and separately labeled redacted backup information;
inspect the affected profile before retrying. This refresh is separate from each
profile’s preview/bound mutation workflow.
Sync refreshes only profiles whose sync preference is on, so an empty result is not a failure.
If ocx integration client status --client aside shows a profile as off (stale), status
prints the commands to reconnect it: review ocx integration client preview --client aside --operation apply --profile N, and run ocx integration client enable --client aside --profile N
only if the preview permits the change and you accept it. Nothing is re-enabled automatically.
A malformed asideProfileSync block in config.json turns every profile off and logs a
warning when the config loads.
Each profile has separate ownership and history. Existing user edits, unsafe paths and linked catalogs are refused; the existing explicit overwrite and drift-confirmation controls remain available. Fully quit and reopen Aside to load changed model files.
ZCode 3.14 and later
Section titled “ZCode 3.14 and later”By default, ZCode 3.14 stores its custom providers in ~/.zcode/v2/provider_config.json and left
~/.zcode/v2/config.json reachable only through a one-shot import that runs when the new file is
missing. ZCode creates the new file the first time it runs, so on any install that has ever been
launched the import is already spent and a write to config.json reaches nothing.
ZCode can relocate its data root with an absolute dataBaseDir in
~/.zcode/v2/setting.json. This bootstrap file always stays under the default home;
the provider store moves to <dataBaseDir>/.zcode/v2/provider_config.json.
ZCODE_DATA_DIR takes precedence and names the .zcode root directly, while
ZCODE_PERSONAL_PROVIDER_CONFIG_FILE takes precedence for the provider file.
ZCODE_DATA_BASE_DIR is a separate native selector and is not an alias for ZCODE_DATA_DIR.
Unreadable, malformed or relative bootstrap settings leave the active root unknown.
The bootstrap must be a regular file of at most 4 MiB; symbolic links and special files
are refused during export and integration path discovery. Repair the bootstrap file or
supply an explicit ZCODE_DATA_DIR to select the intended data root.
Apply, overwrite and refresh refuse with a repair message instead of claiming a successful
legacy write. An existing explicit provider file can still establish the active store.
Ownership recorded at the original default config or store keeps its path, fingerprints
and undo history; disable removes that original block before a later enable can use the
relocated store. An older undo also refuses while another path owns the integration.
If an earlier explicit selector no longer identifies the owned file, restore that selector
before disabling; opencodex retains the record and refuses to abandon the block.
opencodex writes provider_config.json directly where it can. Enabling the integration adds the
opencodex provider rule to that file, a catalog refresh updates it, and disabling removes exactly
what opencodex put there. Every other rule in the file is left alone, including a rule another
provider keeps for a model id that also appears under ours. A rule carrying the opencodex id that
opencodex did not write is a conflict rather than something to take over; resolve it in ZCode, or
use the explicit overwrite.
An unreadable or non-file provider store also refuses writes; it is not treated as an absent store that permits the legacy import.
Two other situations still refuse rather than write. A block opencodex applied before ZCode moved its
store keeps the integration on config.json: disable it there first, then enable it again to write
the new store. And a provider_config.json whose schemaVersion is not one opencodex has observed
is reported rather than merged into, because that file holds every provider ZCode has and asserting
a shape into it would trade a silent no-op for a silent loss. Status names the file ZCode reads
whenever the integration is not writing it.
In that second case, add the provider in ZCode’s own settings: base URL
http://127.0.0.1:10100/v1 (adjust the port to your bind), any non-empty key, and the model ids
from ocx export --client zcode. Deleting provider_config.json to re-trigger ZCode’s import is
not supported — it discards every provider ZCode keeps there.
Cline CLI
Section titled “Cline CLI”This integration targets Cline’s current CLI/shared SDK provider store, whose native schema has
version: 1. Legacy VS Code extension globalState/secret storage is not migrated or detected
as this integration. Run Cline once to initialize its settings directory.
Stop Cline before enabling, syncing, disabling or restoring the integration. OpenCodex writes
providers.opencodex into both providers.json and sibling models.json. The first file holds
the OpenAI Responses connection with a non-secret loopback placeholder; the second holds the
filtered routed model catalog, including available context and image metadata. Existing provider
entries and the default provider selection remain unchanged.
ocx integration client list --jsonocx integration client enable --client clineocx integration client history --client clineocx integration client restore --op <operation-id>After enabling, restart Cline and select OpenCodex, or launch with
cline --provider opencodex --model <provider/model>. External catalog changes are read when
Cline restarts. Cline is excluded from unattended catalog refresh; after changing the routed
model selection, stop Cline and run ocx sync or enable the integration again to refresh it.
A selected model is preserved while still routed and cleared if removed from the exported catalog.
CLINE_PROVIDER_SETTINGS_PATH overrides the primary file. Otherwise CLINE_DATA_DIR selects the
data directory, then CLINE_DIR selects the root, then ~/.cline is used. The model file is always
models.json beside the selected provider file. Overrides must be absolute or start with ~.
Mirror command-local Cline --config paths with CLINE_PROVIDER_SETTINGS_PATH when starting
OpenCodex. A primary path named models.json is refused because the files must be distinct.
Each file replacement is atomic, but no filesystem operation replaces both simultaneously. One journal operation snapshots both original files; a write or bookkeeping failure compensates both. An interrupted operation retains a private recovery record. Status reports incomplete recovery as unsafe, and the next explicit mutation recovers only if neither file nor its ownership has an unrelated edit. If recovery refuses, preserve the files and the recovery path reported by the operation; resolve the conflict before retrying.
Undo restores both original byte strings, including a file that originally did not exist.
Edits after the operation require the existing explicit --confirm-drift; the edited pair is
backed up first. An occupied OpenCodex entry requires the existing --overwrite-conflict opt-in.
Disable removes the two managed entries; it does not restore a prior foreign entry. Use Undo
for that. Snapshot retention and expiration follow the same rules as other integrations.
The download cline-config-bundle.json contains two native document members: settings for
providers.json, and catalog for models.json. It is not itself a Cline settings file. Prefer
the integration command for a journaled merge and rollback. Remote admission wiring is not
supported by this generated integration; it requires unauthenticated loopback access.
GitHub Copilot App
Section titled “GitHub Copilot App”The GitHub Copilot desktop app can use opencodex as an OpenAI-compatible model provider. This is a
manual client setup with no switch on Connect, and it is separate from the upstream
github-copilot provider, which uses a Copilot subscription as a backend for opencodex.
-
Start opencodex and confirm it answers:
Terminal window curl http://127.0.0.1:10100/healthzcurl http://127.0.0.1:10100/v1/models -
In the Copilot app, open Settings → Model providers → Add provider and enter:
Field Value Name any label, for example OpenCodexBase URL http://127.0.0.1:10100/v1(adjust the port to your bind)API key leave blank on loopback -
Sync models from the endpoint, or add one by its
provider/modelid, and select it.
The app uses GET /v1/models for discovery and POST /v1/chat/completions for turns. Those turns
go through opencodex’s normal model routing, so provider credentials, OAuth accounts and combos apply
as they do for any other client. The accepted request fields are listed in the
proxy formats reference.
If the app reports no models, check that the base URL ends in /v1 rather than
/v1/chat/completions and that /v1/models returns a non-empty data array. When opencodex
listens on a non-loopback address, put a data-admission key (the token described under
remote access, or a dashboard-generated ocx_…
key) in the app’s API key field. The app sends it as Authorization: Bearer, which
/v1/chat/completions accepts as proxy admission and never forwards upstream; see the
authentication matrix.
omo (Codex / LazyCodex) role models
Section titled “omo (Codex / LazyCodex) role models”When LazyCodex is installed, the omo tab shows an omo (Codex / LazyCodex) section below the Pi-based omo controls, listing
every Codex agent role found in $CODEX_HOME/agents/*.toml, with the model each one is pinned
to. Codex runs a role on that pin no matter which model the parent asks for, so this is where a
role’s model is actually decided. LazyCodex counts as installed when the omo@sisyphuslabs
Codex plugin is enabled in $CODEX_HOME/config.toml and an installed copy under
$CODEX_HOME/plugins/cache/sisyphuslabs/omo/ carries its plugin manifest or lazycodex-install.json. A ~/.omo
folder on its own does not count, because Pi-based omo creates it too. Without LazyCodex the
section is hidden and the command line reports it as not installed. Pick a model and a reasoning
effort on a row and press Save. A row whose role has no model pin can save an effort on its own;
the role keeps inheriting its model:
- opencodex updates only the root
model = "..."andmodel_reasoning_effort = "..."keys of that role’s file. Missing pins are inserted near the top. Choosing Inherit effort, available on every row, sends"effort": nullto clear the effort pin, removing its line and trailing comment. The role’s instructions, other comments, and other keys are left exactly as they were. - The model is written to
[codex].agents.<role>.modelin~/.omo/omo.jsonc, which LazyCodex 5.1.1 and later reads, and a changed effort LazyCodex supports goes to[codex].agents.<role>.reasoningin the same entry. If that file does not exist it is not created. If it contains comments it is left untouched, because saving would remove them; the tab says so, and you can set the value there by hand. Symlinks and non-regular files are rejected; on macOS and Linux, a FIFO is rejected without waiting for a writer. A skipped mirror does not undo the role-file save.
If the role-file save succeeds but the omo.jsonc write fails, the row’s button becomes
Retry omo.jsonc, which resends the same effort choice, including an Inherit effort clear
(null). Until that retry succeeds, a later Save or Auto-assign Apply for the role that leaves the
effort alone carries the pending choice with it; an explicitly picked effort takes its place.
Nothing happens until you press Save; syncing or restarting opencodex never changes a role file. New Codex sessions pick up the change. The same controls exist on the command line:
ocx agent rolesocx agent roles set explorer xai/grok-4.5ocx agent roles set <role> <model> --effort <level> also sets the role’s reasoning effort: it
inserts or replaces the role file’s model_reasoning_effort value and writes [codex].agents.<role>.reasoning
in omo.jsonc when LazyCodex has that level (ultra stays in the role file only, and an older
reasoning is removed). On PUT /api/codex-agent-roles/{role}, omitting effort keeps the pin,
a level sets it, and null removes the role file’s model_reasoning_effort line, its trailing
comment, and the omo.jsonc reasoning. A Save that changes only the model leaves both effort values as they were.
Omitting model leaves the role file’s model pin and the omo.jsonc model exactly as they were,
so a role without a pin keeps inheriting its model while its effort changes or clears; the dashboard
sends this effort-only request for such a row. When omo.jsonc can be written, a role without an entry there
gets one holding only reasoning if the saved effort is one LazyCodex supports (none is written as
off); for such a role, clearing the effort or saving ultra adds no entry. A null model is refused with 400 invalid_model, because a save never removes a model
pin, and a request with neither model nor effort is refused with 400 empty_update before the role file or omo.jsonc is
read. ocx agent roles set always takes a model.
Only an existing one-line string value can be removed; a multiline or non-string value is refused
with 409 unsupported_model_value, leaving both files unchanged. Clearing removes only the
role’s own override: Codex then resolves the effort as it does for any role without one (an explicit
spawn value, the [agents] default, or the parent session), not a fixed model default.
Auto-assign
Section titled “Auto-assign”Auto-assign is part of omo (Codex / LazyCodex): it sits above the role table in that section and
exists only while LazyCodex is detected. Without it the dashboard shows neither, the API answers
409 lazycodex_not_detected, and ocx agent roles suggest is refused.
Auto-assign proposes a model for every role at once. opencodex asks your
default Codex model (the root model in Codex config.toml) one question: for each role, given its
description and the start of its instructions, which capability tier (fast, standard or frontier),
how much reasoning (glance, measured, thorough or exhaustive) and which task domain (coding, agentic,
reasoning, writing, knowledge or general) does it need? That model never picks a model. opencodex
then picks the cheapest model from your picker list that reaches the tier:
- Models listed under
codexRoleTiersin the opencodex config ({ "fast": [...], "standard": [...], "frontier": [...] }) have that tier. - When two or more of the other models have a published score in the role’s domain, those models are
tiered by distinct score values, without requiring a price; equal scores share a tier, and unscored models are left out. Scores come from
Artificial Analysis when you add your own API key
(below). Artificial Analysis no longer publishes a Coding or Agentic Index, so every domain is scored by
its Intelligence Index. The response’s
capabilityblock names the source it used. - Other models with a known price are ranked by price and split evenly across the three tiers. With only one or two priced models, the dearest is frontier and the other, if any, is standard.
- When tiering falls back to price, models with no price and no listed tier are not proposed. List them to include them.
To use Artificial Analysis, create a free API key at artificialanalysis.ai
and set it as a literal, an ${ENV} reference or a keychain reference:
{ "codexRoleCapability": { "artificialAnalysis": { "apiKey": "${ARTIFICIAL_ANALYSIS_API_KEY}" } }}opencodex reads the free language model list (/api/v2/language/models/free) once per day, one request
per page, which stays inside the free tier’s daily request limit. Its terms allow
using the data for your own decisions and forbid passing it on, so auto-assign keeps the scores
internal: the response carries the tier each model got and the attribution, without the scores. If the
key is absent or the read fails, auto-assign uses configured tiers and prices; capability.error reports
why and the source attribution fields are null. A retired huggingFace setting is ignored.
Artificial Analysis refuses HTTP redirects, reads at most ten pages and 16 MiB in total, and gives each request a 30-second timeout; a longer list counts as a failed read. Credentials are sent only to its fixed API endpoint.
Each proposal shows the model, the tier, the reasoning effort, a one-line reason, and what would move
it up or down. A role the model could not size clearly is shown as not sized, with the reason, and
cannot be applied. Nothing is written until you press Apply on a row or Apply all. Applying uses the
same save as picking by hand. Every sized role whose chosen model has a non-empty effort ladder gets
an effort proposal, and Apply or --apply inserts or replaces its model_reasoning_effort value.
The effort is placed on the chosen model’s own levels: its lowest, its default, one above the
default, or its highest. An empty ladder gives no effort proposal (proposedEffort: null);
Apply omits effort and leaves any existing effort pin as it was. A null proposal never clears a pin.
ocx agent roles suggestocx agent roles suggest --model xai/grok-4.5 --applyThe CLI’s --apply accepts up to 50 changed roles in one history batch. A larger operation is
refused before any role update; preview it and apply individual choices with ocx agent roles set.
An interrupted operation reports confirmed writes and the unconfirmed request, and leaves earlier
writes applied. See the CLI apply and recovery contract before retrying.
Under the table, Change history lists the newest changes a save, an auto-assign apply or ocx agent roles set wrote, newest first, in whole batches and never more than 50, so an Apply all is listed with every role it wrote or not at all, with each role’s model and effort before and after. One Apply all is one entry. Restore writes the earlier values back through the same save, so a restore is listed too. A save never removes a model pin, so a role that had none keeps its current model and gets only its effort back. The history lives in integrations/codex-agent-roles.jsonl under the opencodex config directory, which retains at most 256 KiB and 1000 rows by dropping its oldest whole batches. Compaction targets 128 KiB and preserves a Restore together with its complete source batch when they fit both limits, making room by dropping unrelated older batches; history can still expire as later writes arrive. The writer captures the bounded history before appending so a Restore does not evict its own source at the retention boundary; the omo card’s Restore center above tracks only ~/.omo/agent/models.json. Each change records the Codex home it was made in, and only the current home’s changes are listed or restored, so after pointing CODEX_HOME elsewhere the list starts empty and a Restore from another home’s history is refused. If the history file cannot be read, the section says so in place of the list, and the role table keeps working.
Kilo CLI, VS Code, and JetBrains share one global config. This integration writes
provider.opencodex into the first existing file among kilo.jsonc, kilo.json,
opencode.jsonc, opencode.json, and config.json under ~/.config/kilo
(XDG_CONFIG_HOME relocates that directory). If none exist, the destination is
kilo.jsonc. Project configs are never written.
Kilo merges all of these global files. If another candidate also defines
provider.opencodex, status names every competing file and Apply and Replace refuse;
remove provider.opencodex from those files before enabling the integration. An unreadable or unsafe
candidate also blocks the write. Disable can still remove a block owned in the recorded file
while another candidate conflicts or cannot be parsed; the other candidate is left untouched.
OpenCodex reads these candidate files as JSONC. A // line comment ends at a CR, LF, or CRLF
line ending, so a file saved with any of these line endings keeps the settings that follow a comment.
The owned fragment is only provider.opencodex (OpenCode V1 shape: npm, options,
models). Kilo’s published schema has no OpenCode V2 providers key, so that block is
not emitted; Kilo instead receives the per-model reasoning choices as a variant map inside
its provider block, built from the same effective canonical metadata as the OpenCode
export. $schema, model, enabled_providers, MCP, and other keys stay
user-owned. Select opencodex/<provider/model> in Kilo after applying.
Loopback uses {env:OPENCODEX_KILO_API_KEY} as options.apiKey. A non-loopback bind
moves admission to options.headers["x-opencodex-api-key"] and never serializes a real
key. Apply rewrites the whole global file as pretty JSON, so comments and trailing
commas in other keys are not preserved. Kilo is on the implicit catalog fan-out: ocx sync, POST /api/sync, and visibility, provider, or preset changes refresh an owned Kilo
block under the same safe owned-only rules as every other client, and a new Kilo session
loads the updated file.
When reading these candidate files, OpenCodex accepts a trailing comma after an array element or
object property. Write an empty array or object as [] or {}; [,] and {,} are refused as malformed.
ocx integration client enable --client kiloocx export --client kilo --out ./kilo.jsoncCommand Code
Section titled “Command Code”Command Code keeps its custom providers in ~/.commandcode/providers.json. This
integration writes provider.opencodex into that file and is loopback-only.
Which root it writes. Command Code resolves its provider map as
document.provider ?? document.providers, so the singular root wins whenever it
exists. The integration therefore reads the target file first and writes into the
root that file already uses: a document that already carries a providers map keeps
it, and your other providers stay readable. A fresh file gets the singular provider
root. Both roots are declared as managed paths, so Disable removes the block from
whichever one it was written into.
The credential form. The provider is written with apiKey: false, the documented
form for an endpoint that needs no key. A raw string is refused by Command Code, and
no service-token file is read, referenced, or exported by this integration.
Home resolution. There is no COMMANDCODE_HOME override here. The published
client resolves HOME ?? USERPROFILE and then appends /.commandcode/providers.json;
it does not consult that variable. An override this integration honoured but the
client ignored would make Apply report success at a path Command Code never opens, so
the path is always ~/.commandcode/providers.json.
On Windows with the default home, the integration uses HOME ?? USERPROFILE
only when the selected value is nonblank and Windows-absolute; otherwise it uses
homedir(). A separately supplied home is preserved. A HOME set by Git for
Windows or MSYS2 is therefore honoured only when it passes those checks.
Paste the key yourself if you later move the provider off loopback: Command Code
stores keys in ~/.commandcode/auth.json (via /connect), not in providers.json.
ocx integration client enable --client commandcodeocx export --client commandcode --out ./providers.jsonocx commandcode restore --op <opId> accepts only Command Code operations, including
--preview and --plan-fingerprint requests. It rejects client/profile overrides.
The running proxy must support client-scoped restore; older proxies return an error
without falling back to the generic restore endpoint.
Use ocx integration client restore for the generic journal and the explicit Aside
profile commands above for Aside operations.
Factory Droid
Section titled “Factory Droid”Run Droid once to create ~/.factory, then explicitly enable this integration with
ocx integration client enable --client droid. OpenCodex adds only documented
customModels entries to your personal settings.json, using a keyless local
Chat Completions endpoint. Choose a row from Droid’s /model picker. Disable
removes the managed rows; Undo restores the exact saved file. Other settings and
custom models remain yours.
If Droid normalizes a customModels row by adding id or index, OpenCodex ignores those two
client fields when checking ownership so saved reasoning defaults remain available.
Open Connect → Factory Droid (/#integrations/droid) to set a reasoning
default for each connected model. Choose from the model’s supported efforts,
review the changes, then confirm. No default clears that model’s draft setting;
use Save / review changes and confirm to apply the removal.
Models without a declared effort list show that no default is available.
The CLI supports the same defaults in Droid apply/overwrite. For example, replace the fictional model below with an exact connected ID and supported effort:
ocx integration client preview --client droid --operation apply --reasoning-default example/model-a=high --json > droid-preview.jsonAfter reviewing the applicable plan and choosing the write:
DROID_FINGERPRINT="$(jq -er 'select(.canApply == true) | .fingerprint' droid-preview.json)"ocx integration client enable --client droid --reasoning-default example/model-a=high --plan-fingerprint "$DROID_FINGERPRINT" --jsonocx integration client status --client droid --jsonRepeat --reasoning-default MODEL=EFFORT to submit the complete replacement map.
The last equals sign separates model and effort; case and exact IDs are preserved.
Duplicate model keys refuse. Omitted flags preserve defaults;
--clear-reasoning-defaults explicitly sends an empty map. To clear, preview with
that flag and repeat it in the matching enable command. Clear and entries cannot
combine. These flags are Droid-only and do not apply to disable/restore. Repeat
the same map in preview and commit; the server validates model-specific efforts
and refuses stale binding rather than silently accepting a changed map.
For an intended complete clear, preview that separate action:
ocx integration client preview --client droid --operation apply --clear-reasoning-defaults --json > droid-clear-preview.jsonAfter reviewing and choosing it:
DROID_CLEAR_FINGERPRINT="$(jq -er 'select(.canApply == true) | .fingerprint' droid-clear-preview.json)"ocx integration client enable --client droid --clear-reasoning-defaults --plan-fingerprint "$DROID_CLEAR_FINGERPRINT" --jsonThe default applies only when Droid omits an effort from its request. An explicit request effort takes precedence over this default; existing OpenCodex pins and caps still apply. Droid may continue to display Dynamic even when OpenCodex applies the configured default. Requests without the Droid default header are unaffected; the header is a request preference, not proof of client identity. For combos and routing policies, each concrete target checks the preference against its own effort list, so an incompatible first target does not remove it from a compatible fallback.
A saved default that the routed model no longer supports is ignored for requests. If the connected model’s declared effort list no longer includes the saved value, it is omitted from the panel’s defaults and removed from the managed row on refresh. Reviewing without editing lets OpenCodex preserve the remaining supported defaults.
Refresh preserves defaults while the exact provider/model selector remains
connected and declares the saved effort. Renaming a provider, model, or combo alias replaces that managed row
and clears its default; choose a default for the renamed row again. Disable
removes the defaults with the managed model rows, and Undo restores the saved
rows and their defaults together.
Models whose IDs or display names contain , or ] are skipped because the
managed selector cannot address them safely; export and managed settings show
the same rows. A nonempty catalog with no addressable models is refused.
Droid also reads legacy config.json and local settings.local.json. Resolve
legacy rows that use the OpenCodex endpoint, a generated model ID, or an
OpenCodex: display name, and any local customModels override, before enabling;
OpenCodex refuses those ambiguous settings. It also refuses an
unsafe target or a row edited since apply. The integration is loopback only and
never copies provider credentials. Factory documents the BYOK schema
and personal settings path.

