Codex

codex_local runs OpenAI's Codex CLI on the same machine as ThinkingMach. Use it when you want a local coding agent with persistent session state, managed CODEX_HOME, and ThinkingMach skills injected into the Codex skills directory.


When To Use

  • You already use the Codex CLI on the host machine.
  • You want session continuity across heartbeats.
  • You want ThinkingMach to manage a per-company Codex home when possible.
  • You want repo instructions, ThinkingMach instructions, and Codex's own runtime behavior to work together.

When Not To Use

  • The runtime lives behind a webhook or remote API. Use HTTP.
  • You only need a shell command or script. Use Process.
  • Codex CLI is not installed or cannot be executed from the ThinkingMach host.

Common Fields

Field Required Notes
cwd no Absolute working directory for the agent. Recommended in practice. If omitted, the adapter falls back to the current process working directory. ThinkingMach creates the path when permissions allow.
engine no How Codex is run: auto (the default — runs ACP), acp (always the Agent Client Protocol), or cli (always the classic Codex CLI). See ACP Engine.
model no Codex model id. See Models. If you leave it unset, the adapter omits --model so the Codex CLI uses its own default.
promptTemplate no Prompt template used for the run.
instructionsFilePath no Markdown file prepended to the stdin prompt sent to codex exec.
modelReasoningEffort no Reasoning effort override passed through Codex config. The choices depend on the model — see Reasoning Effort.
search no Runs Codex with --search.
fastMode no Enables Codex Fast mode by setting service_tier="fast" and features.fast_mode=true. Supported on gpt-6.1-sol, gpt-6-astra, gpt-6-sol, gpt-6-luna, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, and gpt-5.4, and passed through for manual model ids. When the configured model can't use it, ThinkingMach ignores the setting and notes why.
dangerouslyBypassApprovalsAndSandbox no Runs Codex with --dangerously-bypass-approvals-and-sandbox so unattended runs never stop for approval. Defaults to on when you leave it unset — see Permissions.
command no Defaults to codex.
extraArgs no Extra CLI arguments appended to the Codex invocation.
env no Environment variables passed to the runtime. Secret refs are supported.
timeoutSec no Run timeout in seconds. On local and SSH targets, 0 means no adapter wall-clock timeout. On a sandbox target, 0 or an unset value uses the 14,400-second sandbox default; use a positive value to override it or a negative value to opt out of the adapter timeout.
graceSec no Grace period before a forced stop.
outputInactivityTimeoutMs no How long the adapter waits for Codex to produce output before treating the run as stuck. The timer resets whenever Codex writes output (and, on Linux, when its processes show real CPU, disk, or child-process activity), so a busy run never trips it. Defaults to 30 minutes (1800000) when unset. Set it to null to switch the monitor off entirely — only do that for tasks you know go quiet for long stretches, since ThinkingMach's platform-level one-hour silent-run safety net still applies. When it fires, the adapter stops the Codex process and reports the run as failed with a message like monitor: no codex activity (output or process) for 30m 0s.
workspaceStrategy no Execution workspace strategy, such as git_worktree.
workspaceRuntime no Reserved workspace runtime metadata.

Note: Codex sends the prompt through stdin and uses codex exec --json. The adapter's environment test checks both the command and the auth path before you try to run a real heartbeat.


Permissions

Codex agents run in full-auto mode by default. If you leave dangerouslyBypassApprovalsAndSandbox unset, ThinkingMach passes --dangerously-bypass-approvals-and-sandbox, so Codex works through a task without pausing for approvals that no one is around to give.

You stay in control when you say otherwise. ThinkingMach skips the bypass — keeping Codex in its writable workspace sandbox, or in the mode you picked — when any of these is true (the last three only count while dangerouslyBypassApprovalsAndSandbox is unset):

  • You set dangerouslyBypassApprovalsAndSandbox to false.
  • Your extraArgs pick a sandbox mode or profile yourself — for example --sandbox, --profile, --full-auto, or a sandbox_mode= config override.
  • Your extraArgs set an approval_policy= override, or switch network access off with sandbox_workspace_write.network_access=false.
  • The execution target denies network access.

This bypass applies to the classic Codex CLI lane (engine: "cli"). The ACP engine keeps Codex in its writable workspace sandbox, with network access switched on for each turn.

Heads-up: Earlier versions kept Codex in the workspace sandbox unless you opted in to the bypass. If you relied on that, set dangerouslyBypassApprovalsAndSandbox to false explicitly.


ACP Engine

Codex can run through one of two engines — ACP or the classic Codex CLI — selected by the engine field:

  • auto (default) — ACP. ThinkingMach runs Codex through the Agent Client Protocol (ACP). If the host doesn't meet the ACP prerequisites, the run fails with a setup error explaining why — ThinkingMach never switches engines on its own.
  • acp — always ACP. Force the Agent Client Protocol path.
  • cli — always the Codex CLI. Force the classic CLI wrapper and skip ACP entirely.

ACP gives you a richer, structured live transcript: session identity, status with context-window usage, assistant and thinking token deltas, and tool-call updates that fold into a single card as they progress. That extra detail is most useful when you're watching a sandbox run stream in.

When the engine resolves to ACP (auto or acp), these extra fields apply:

Field Default Notes
agentCommand package-local codex-acp Optional override for the Codex ACP server command.
mode persistent persistent keeps ACP session state between runs; oneshot starts fresh each run.
nonInteractivePermissions deny What to do if the ACP agent asks for input outside an interactive session — deny the request or fail the run.
stateDir ThinkingMach-managed Optional ACP session-state directory. Defaults to ThinkingMach's company- and agent-scoped storage.
warmHandleIdleMs 0 How long to keep the ACP process warm between runs, in milliseconds. 0 closes it after each run while still retaining persistent session state.

Heads-up: ACP is where the old standalone acpx_local adapter's capabilities now live. That adapter has been retired — pick codex_local (or claude_local / gemini_local) and leave engine on auto to get ACP by default.

ACP in sandbox environments

You can keep engine on auto when this agent runs in a ThinkingMach sandbox environment. If that sandbox provides ThinkingMach's bidirectional process session, ThinkingMach keeps the ACP engine and its structured live transcript; you do not add a separate bridge setting to the adapter config.

An environment that only runs one-shot commands cannot host an ACP session, so an auto run there fails with a setup error rather than switching to the Codex CLI. Set engine: "cli" when you want the CLI lane — for example on such environments.


Models

Pick any of the known Codex model ids in the model field. The picker lists them the way the ChatGPT app orders Codex models — newest model version first, most capable first within each version, and older models at the end:

  • gpt-6.1-sol
  • gpt-6-astra
  • gpt-6-sol
  • gpt-6-luna
  • gpt-5.6-sol (the adapter default)
  • gpt-5.6-terra
  • gpt-5.6-luna
  • gpt-5.5
  • gpt-5.4
  • gpt-5.4-mini
  • gpt-5
  • gpt-5-mini
  • gpt-5-nano
  • o3
  • o4-mini
  • o3-mini
  • codex-mini-latest

This is a curated list. Even when an OpenAI API key is available, the picker doesn't fill itself from OpenAI's model catalog, because that catalog also includes image, audio, and embedding models Codex can't run. If you need extra models in the dropdown — say, ones served through your own gateway — declare them with THINKINGMACH_ADAPTER_MODELS.

You can also type a model id that is not in this list. Anything ThinkingMach does not recognize is treated as a manual model id and passed straight through to the Codex CLI.

Heads-up: The default is now the concrete gpt-5.6-sol slug. The bare gpt-5.6 alias has no OpenAI-published metadata, so ThinkingMach rewrites gpt-5.6 to gpt-5.6-sol automatically — an agent still configured with the old alias keeps working and stops triggering the CLI's "model metadata not found" warning.

Tip: Leave model empty to let Codex choose. When no model is set, the adapter omits the --model flag entirely so the Codex CLI falls back to its own default model.

Reasoning Effort

The modelReasoningEffort choices follow the model you pick:

Model Effort levels
gpt-6.1-sol, gpt-6-astra, gpt-6-sol, gpt-5.6-sol, gpt-5.6-terra low, medium, high, xhigh, max, ultra
gpt-6-luna, gpt-5.6-luna low, medium, high, xhigh, max
Other models minimal, low, medium, high, xhigh

If you switch to a model that doesn't support the effort you had selected, the agent form clears the effort so it falls back to Auto.


Model Profiles

A model profile is a named alternative to the agent's main model settings, so you can run the same agent on a different lane without rewriting its adapter config. Codex ships one profile, and it comes from the adapter itself (source: "adapter_default"), so every codex_local agent has it available:

Key Label Description
cheap Cheap Use an explicitly configured lower-cost Codex model without changing the primary model.

The important part is explicitly configured: the cheap profile ships with an empty adapterConfig, so it does not choose a model for you. Enabling it changes nothing until you fill in what the cheap lane should use — normally a model, and optionally an override such as modelReasoningEffort. You decide which model counts as cheap for this agent, and the primary model in your adapter config stays exactly as it is.

Heads-up: cheap used to hard-code a specific Codex model and reasoning effort for you. It no longer does. If you were relying on that, set the model you want on the profile yourself.

New Codex agents start with this profile switched off. When an agent is created and its runtime config does not already mention cheap, ThinkingMach records modelProfiles.cheap = { enabled: false } — the profile is present but dormant, waiting for you to configure it and turn it on.


Session Persistence

Codex preserves the previous_response_id chain so heartbeats can continue the same conversation instead of starting fresh each time.

The session codec also preserves these location hints when present:

  • cwd
  • workspaceId
  • repoUrl
  • repoRef

If the working directory changes, the adapter starts a fresh session instead of reusing the old response chain.

Native Codex on ThinkingMach Runner

Codex can also run natively through ThinkingMach Runner (paperclip_runner with provider: "codex") rather than through codex_local. A few behaviors are specific to that native path:

  • Restarts don't cost you the conversation. If a server restart kills a native Codex turn mid-flight, ThinkingMach first makes sure the old process is really gone. Then it resumes the request once, in the same conversation and workspace, and asks the agent to check any unfinished commands or outside actions before carrying on. It doesn't resend the task. The run log records the interrupted turn with provider_turn_lost_on_restore. Anything that can't be reconciled still stops for a person.
  • Codex keeps its own base prompt. ThinkingMach sends its task context as additive developerInstructions when a thread starts or resumes, so Codex's stock base instructions stay in place. Conversations created before this change keep their saved prompt when resumed; reset those sessions if you want the stock base instructions back.
  • A model your ChatGPT account can't use says so. When Codex rejects the selected model for a ChatGPT sign-in, the task shows Model unavailable. with "The selected model is not supported by the current ChatGPT connection. Choose a supported model or a compatible AI connection." ThinkingMach doesn't retry that request automatically. Choose a supported model or clear the task's model override, then retry.
  • A busy model retries on its own. When the selected model is at capacity, ThinkingMach shows Model at capacity and retries after one minute, then after two. See When a run needs attention.

Skills Injection

Codex injects ThinkingMach skills into the effective CODEX_HOME/skills directory.

In the default managed-home mode, ThinkingMach uses a per-company Codex home under the active ThinkingMach instance and seeds it from the shared Codex home for auth continuity.

When ThinkingMach runs inside a managed worktree instance, that Codex home is worktree-isolated so sessions, logs, and skills do not leak across checkouts.

For manual local CLI use outside ThinkingMach, run:

pnpm thinkingmach agent local-cli codexcoder --company-id <company-id>

That command installs missing skills, creates an agent API key, and prints shell exports you can use to run Codex as that agent.


Instructions File

If you set instructionsFilePath, ThinkingMach reads that file and prepends it to the stdin prompt sent to codex exec on every run.

That is separate from Codex's own repo instruction discovery. If the working directory contains an AGENTS.md, Codex can still load it as part of its normal behavior.

Tip: Use instructionsFilePath for ThinkingMach-managed instructions. Use repo-local instruction files when you want Codex to pick them up naturally from the workspace.


Authentication And Per-Agent Homes

Each codex_local agent runs against its own managed Codex home, so one agent can never spend against another agent's login or share its Codex state. Because that home starts empty, ThinkingMach seeds credentials into it for you before launching Codex:

  • You inherit the host Codex login by default. If you are already signed in to Codex on the ThinkingMach host (a ChatGPT-subscription login), ThinkingMach links that login into the agent's managed home automatically. You do not have to log in again per company or per agent.
  • A per-agent API key wins when you set one. If the agent's env carries an OPENAI_API_KEY, ThinkingMach writes that key into the managed home as API-key auth instead of borrowing the host login. On the ACP engine, CODEX_API_KEY works the same way as an alternative name for that key.
  • Your own CODEX_HOME is left alone. If you point the adapter at a CODEX_HOME outside ThinkingMach's managed company tree, ThinkingMach treats it as self-managed and never seeds or overwrites it.
  • No silent credential-less runs. If a managed home ends up with no usable login and no configured API key, the run fails fast with a clear adapter error instead of starting Codex and hitting a 401 from the provider.

Signing in inside a sandbox

When an agent runs in a ThinkingMach sandbox environment that has no ready Codex login, the environment test reports that authentication is missing and ThinkingMach can start an interactive device login for you. Complete the login in your browser and ThinkingMach promotes the resulting subscription credential into the company scope so later heartbeats reuse it.

ThinkingMach also keeps a company-scoped, identity-keyed cache of subscription credentials (one usable credential per account), so a fresher login recovered from a sandbox run can refresh an account the host already holds. The cache never seeds an empty host and never switches which account an agent uses. It is on by default; set THINKINGMACH_CODEX_AUTH_CACHE to a falsy value (0, false, no, or off) on the ThinkingMach host to turn it off.

CODEX_HOME sync into a sandbox

When this agent runs in a ThinkingMach sandbox, the adapter does not copy your whole Codex home into the sandbox. Instead it stages an explicit allowlist, so only the material a run actually needs crosses the boundary and none of your host-local runtime state comes along for the ride. The synced entries are:

  • config.json
  • config.toml
  • instructions.md
  • auth.json
  • skills

Everything the stock codex binary writes at runtime — session databases (*.sqlite, *-wal), plugins/, cache/, sessions/, shell_snapshots/, and the like — stays on the host and is intentionally left out. This allowlist only applies to sandboxed execution; local and SSH targets read the managed home in place.


Environment Test

The Test Environment button checks:

  • Codex CLI is installed and executable.
  • The working directory is absolute and usable.
  • Authentication is ready through OPENAI_API_KEY or Codex's own login state.
  • The installed Codex CLI is new enough for the selected model. With ChatGPT sign-in, gpt-6.1-sol needs Codex 0.159.0 or newer, and gpt-6-sol and gpt-6-luna need 0.157.0 or newer. The test runs codex --version where the agent runs (locally, over SSH, or in the sandbox) and reports codex_cli_version_compatible or codex_cli_version_incompatible. When the CLI is too old, the hello probe is skipped.
  • The hello probe can run codex exec --json - with the prompt Respond with hello.

If the probe fails, fix the command or auth path before saving the adapter. If the Codex backend rejects the model for your ChatGPT account, the test reports codex_hello_probe_model_rejected and explains the likely cause: a Codex CLI that's too old, a plan that doesn't include the model, or a rollout that hasn't reached your account yet. In a sandbox, the fix for an old CLI is to promote an image that ships the Codex version ThinkingMach pins. Adding a model to the catalog doesn't update the CLI baked into existing images.


Custom Providers And Gateways

Codex has no CLI flag or env var for pointing at a custom OpenAI-compatible endpoint. Custom endpoints live as [model_providers.<id>] tables in $CODEX_HOME/config.toml. To route Codex at your own gateway or provider without hand-editing that file, set the THINKINGMACH_CODEX_PROVIDERS environment variable on the ThinkingMach host.

THINKINGMACH_CODEX_PROVIDERS is a JSON object that maps onto Codex's config.toml schema:

{
  "providers": {
    "my-gateway": {
      "name": "My gateway",
      "base_url": "https://gateway.example.com/v1",
      "env_key": "OPENAI_API_KEY",
      "wire_api": "responses"
    }
  },
  "model_provider": "my-gateway"
}
  • Each key under providers becomes a [model_providers.<id>] table. Common fields are name (optional display name), base_url (the OpenAI-compatible endpoint), env_key (the env var Codex reads the bearer key from at request time), and wire_api (the protocol Codex speaks to the provider). Any other field Codex supports is passed through too, including query_params, http_headers, env_http_headers, and request_max_retries.
  • model_provider is optional. When set, it selects the top-level provider and must match one of your providers entries. The model field then picks the model within that provider.
  • String values may use {env:VAR} placeholders. ThinkingMach expands them server-side against the run env and process.env, which is handy for fields that must carry a literal value such as http_headers. Prefer Codex's own env_key indirection for the bearer key. Unresolvable placeholders are left intact.

ThinkingMach writes these definitions into a managed region of the per-company managed Codex home's config.toml, delimited by # >>> paperclip codex providers ... managed, do not edit >>> marker comments. Your existing config is preserved, conflicting definitions are excised so the managed ones win, and the original file is restored after the run.

Note: This merge only happens for the managed Codex home. If your adapter config explicitly sets env.CODEX_HOME, ThinkingMach leaves that user-managed home untouched and surfaces a note instead of merging.


Example

{
  "adapterType": "codex_local",
  "adapterConfig": {
    "cwd": "/Users/me/projects/paperclip-workspace",
    "model": "gpt-5.6-sol",
    "instructionsFilePath": "/Users/me/projects/paperclip-workspace/INSTRUCTIONS.md",
    "modelReasoningEffort": "medium",
    "search": false,
    "dangerouslyBypassApprovalsAndSandbox": true,
    "env": {
      "OPENAI_API_KEY": {
        "type": "secret_ref",
        "secretId": "secret-id",
        "version": "latest"
      }
    },
    "timeoutSec": 300,
    "graceSec": 15
  }
}

Next Steps