Skip to main content

Codex

Codex is OpenAI's official CLI agent. As a Jetty runtime, it runs your runbook inside a sandbox and uses OpenAI's models — directly or via OpenRouter for access to other model families with an OpenAI-shaped API.

Use codex when:

  • You're running an OpenAI model (gpt-5.4, gpt-5.5, o4-mini, etc.)
  • You want OpenRouter access to non-Anthropic models with an OpenAI client
  • You need codex's specific tool-use shape and JSON event stream

For Anthropic models, use claude-code. For Google, use gemini-cli. For multi-provider parity, use hermes.

Provider support

ProviderSupportedHow it routes
OpenAINative API via OPENAI_API_KEY
OpenRouterOPENAI_BASE_URL=https://openrouter.ai/api/v1 (OpenAI-compatible proxy)
AnthropicUse claude-code
AWS BedrockUse hermes
GoogleUse gemini-cli

Confirmed-working models

ProviderModel IDNotes
OpenAIgpt-5.5Latest flagship reasoning model
OpenAIgpt-5.4Default for the runtime when model is unset
OpenAIo4-miniLightweight reasoning model
OpenRoutermoonshotai/kimi-k2-0905Pass the OpenRouter slug directly as the model ID
OpenRouterqwen/qwen3-coderPass the OpenRouter slug directly

When routing through OpenRouter, the model field is the OpenRouter slug (vendor-prefixed). Codex sees it as the model field on its OpenAI-shaped request and OpenRouter resolves it.

Setup

OpenAI

TOKEN="$(cat ~/.config/jetty/token)"
cat <<'BODY' | curl -s -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"https://flows-api.jetty.io/api/v1/collections/{COLLECTION}/environment" \
--data-binary @-
{
"environment_variables": {
"OPENAI_API_KEY": "sk-..."
}
}
BODY

OpenRouter

{
"environment_variables": {
"OPENROUTER_API_KEY": "or-..."
}
}

The runner copies OPENROUTER_API_KEY into OPENAI_API_KEY and overrides OPENAI_BASE_URL so codex routes through OpenRouter without further config. Set OPENROUTER_BASE_URL to point at a self-hosted OpenRouter proxy / corp gateway and the runner picks it up.

Use it in a runbook

---
agent: codex
model: gpt-5.5
snapshot: python312-uv
---

# My Runbook

Do something interesting. Write the result to `{{results_dir}}/output.md`.

Or via the Chat Completions API:

{
"model": "gpt-5.5",
"jetty": {
"runbook": true,
"agent": "codex",
"collection": "my-collection",
"task": "my-task"
}
}

How it runs

The runner invokes:

codex exec \
--dangerously-bypass-approvals-and-sandbox \
--skip-git-repo-check \
--model <model> \
--json \
-- '<runbook content>'

--dangerously-bypass-approvals-and-sandbox is safe inside Jetty because the Daytona sandbox is already isolated. --json produces a structured event stream that the trajectory parser consumes for tool-call and message visibility.

Authentication happens via $CODEX_HOME/auth.json (which the runner sets to /logs/agent/auth.json); the runner writes the file pre-run and deletes it post-run so the credential never persists in trajectory artifacts.

Streaming events

Codex emits NDJSON events. The Jetty parser surfaces these in the trajectory log:

Event typeSurfaced as
item.started (command_execution)[agent] exec: <command>
item.completed (command_execution)[agent] output (exit N): <stdout>
item.completed (agent_message)[agent] <text>
turn.completed[agent] turn done — tokens in=X out=Y (and accumulates usage)

Limitations

  • OpenAI / OpenRouter only. No Anthropic, Bedrock, or Google support.
  • Auth is short-circuited via auth.json. If you need OAuth or per-organization scoping, that's not yet wired — file-based bearer-token auth is what the runner does today.
  • Sandbox is bypassed inside Daytona. The --dangerously-bypass-approvals-and-sandbox flag is intentional (Daytona itself provides isolation), but it means Codex's own permission prompts never appear — the agent has full shell access inside the sandbox.