OMP Codex Bridge #
@oh-my-pi/codex-bridge is a compatibility shim that lets ChatGPT Desktop (and other clients expecting the Codex CLI / app-server protocol) connect over SSH and drive an Oh My Pi (OMP) agent session.
The client talks standard Codex app-server protocol; the bridge translates it to OMP sessions, models, tools, and approval requests.
Quick Start #
1. Install the codex shim #
./scripts/install-local.sh "$HOME/.local"
Verify that your SSH login shell can find it:
ssh <host> 'codex --version && codex doctor'
Note: Ensure
~/.local/binis exported in your login shell config (e.g.~/.zprofile,~/.bash_profile, orenv.nu), not just interactive rc files.
2. Configure default model and aliases #
Create ~/.omp/codex-bridge/config.json:
{
"backend": "auto",
"defaultModel": "gemini-3.7-flash",
"modelAliases": {
"gemini-3.7-flash": "google-antigravity/gemini-3.7-flash",
"claude-opus-5": "anthropic/claude-opus-5",
"claude-sonnet-5": "anthropic/claude-sonnet-5",
"gpt-5.6-luna": "openai-codex/gpt-5.6-luna"
},
"defaultApprovalPolicy": "on-request",
"defaultReasoningEffort": "medium"
}
Using the Prime backend #
Prime is a separate agent binary, so ask for it explicitly and make sure prime-agent (Prime 0.8.0 or newer) is reachable from your login shell:
{
"backend": "prime",
"defaultModel": "gpt-5.6-luna",
"modelAliases": {
"gpt-5.6-luna": "openai-codex/gpt-5.6-luna"
},
"defaultReasoningEffort": "medium",
"sessionOptions": {
"primeExecutable": "/opt/homebrew/bin/prime-agent"
}
}
sessionOptions.primeExecutable is optional — without it the bridge tries OMP_CODEX_PRIME_BIN, then ~/.local/bin, ~/.bun/bin, ~/.nix-profile/bin, /opt/homebrew/bin, /usr/local/bin, then prime-agent on PATH. Check what it resolved and start it:
OMP_CODEX_BACKEND=prime codex doctor # reports backendSelection + primeExecutable
codex app-server --backend prime # one-off stdio server
codex remote-control start --backend prime # or the shared daemon
Prime ships its own short model catalog, so the ChatGPT model picker lists Prime's models — gpt-5.6-luna, sent to Prime as the provider-qualified selector openai-codex/gpt-5.6-luna — instead of the bridge's OMP featured list.
Warning: Prime has no Codex command/file approval primitive and no sandbox. Under
backend: "prime"the bridge cannot ask before a shell command or a file write:defaultApprovalPolicyis not applied and the agent runs with the daemon's own privileges. Use it only where unattended edits are acceptable. The OMP backends (auto,sdk,rpc) are unchanged and still prompt.
Using OMP and Prime together #
Set backend to both. The bridge advertises itself as a custom model
provider so Desktop/iOS accept backend-qualified model IDs:
{
"backend": "both",
"defaultModel": "gpt-5.6-sol-via-omp",
"modelCatalog": {
"include": ["gpt-5.6-*", "gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "claude-opus-5", "claude-sonnet-5", "claude-fable-5", "claude-haiku-4-5"],
"excludeProviders": ["google-antigravity", "kimi-code", "laguna-local", "nanbeige-local", "ollama", "lmstudio"],
"backendOrder": ["omp", "prime-agent"],
"order": ["gpt-5.6-sol", "gpt-5.6-luna", "gpt-5.6-terra", "gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "claude-opus-5", "claude-sonnet-5", "claude-fable-5", "claude-haiku-4-5"]
}
Models exposed by both agents appear twice:
GPT-5.6 Luna (OMP)—gpt-5.6-luna-via-ompGPT-5.6 Luna (Prime Agent)—gpt-5.6-luna-via-prime-agent
All OMP rows are ordered before all Prime Agent rows. The selected qualified ID chooses the agent when a thread is created. That choice is persisted with the thread, so inactive threads and forks reopen through the same agent.
modelCatalog is local policy, not a built-in bridge allowlist. include and
exclude accept * globs over canonical model IDs; excludeProviders removes
whole providers; backendOrder groups agent rows; and order ranks matching
models inside each group. Omitting the section preserves the backend catalogs'
normal visibility and ordering.
3. Connect from ChatGPT Desktop #
Add your machine under ChatGPT Desktop's SSH Connections and select your project workspace.
Features #
- Protocol Compatibility: JSONL over stdio, Unix domain socket WebSocket, and proxy transport.
- Model Discovery: Dynamically queries OMP's model catalog and maps requests through configured aliases.
- Streaming & Reasoning: Assistant text, reasoning/thinking blocks, and command output stream in real-time.
- Native Projections: Translates OMP activity into Codex
commandExecution,fileChange,dynamicToolCall,mcpToolCall,webSearch, andimageViewitems, including plan updates and unified diffs. - Approvals & Sandbox: Server-initiated approvals for shell commands and file mutations with session-level caching (OMP backends only;
backend: "prime"enforces neither). - Backends: OMP in-process SDK or
ompRPC subprocess (auto,sdk,rpc), Prime'sprime-agentsubprocess (prime), and a deterministicmockfor tests. - Goals: Persists
thread/goal/*state, synchronizes OMP/goalupdates, accounts usage, and supplies active objectives to resumed turns. - Daemon Lifecycle: Background daemon management with
codex remote-control start|status|stop. - External Sessions: Threads driven by another client - including hand-started terminal
ompandprime-agentsessions - stream their turns and items live, and can be steered and interrupted (see below).
Controlling externally started sessions #
Prime Agent #
With backend: "prime", the bridge discovers saved sessions in
~/.prime/agent/sessions (or PRIME_AGENT_SESSION_DIR) and correlates them
with prime-agent's daemon registry. Durable history uses the same Pi v3 JSONL
projection as OMP; the bridge keys it by the session ID inside the file rather
than assuming the filename is identical.
Live Prime sessions use the local Prime daemon protocol for event replay, status, prompt/steer, interrupt, and compaction. If the daemon is unavailable, saved sessions remain readable and resumable, but an already-running session cannot be controlled until the daemon is reachable again.
OMP #
The bridge can observe any OMP session by tailing its JSONL, but writing to a
session it does not own needs a channel into that process. extension/control-socket.mjs
is an ordinary OMP extension that gives each session a private Unix socket, so
it survives OMP upgrades and needs no patched binary.
Enable it for every session by listing it under extensions in the OMP config
(~/.omp/agent/config.yml):
extensions:
- /path/to/omp-codex-bridge/extension/control-socket.mjs
With it loaded, a thread another client is driving stops being read-only:
turn/start steers the running turn and reports back the real turn it landed
in, turn/steer queues into it, and turn/interrupt aborts it. Without it,
those calls behave exactly as before - turn/start still reports BUSY.
Sockets live in ~/.omp/codex-bridge/control/<session-id>.sock (override with
OMP_CONTROL_SOCKET_DIR), are created 0600 inside a 0700 directory, and are
removed on session shutdown. Anything able to open one can inject instructions
into a running agent, which is why it is opt-in and owner-only.
Testing #
# Run unit & integration test suite
npm test
# Check syntax of all modules
npm run check
# Full release verification
./scripts/verify-release.sh
Documentation #
- AGENTS.md — Architecture, backend resolution, env vars, and debugging guide
- docs/ARCHITECTURE.md — Protocol mapping & internal data flow
- docs/COMPATIBILITY.md — Detailed RPC method compatibility table
- SECURITY.md — Security boundaries and approval model
- Shell 65%
- JavaScript 35%