codex

package
v0.14.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

Codex Executor

OpenAI Codex CLI executor for AILANG. Implements the executor.Executor interface as a subprocess driver around the OpenAI Codex CLI, mirroring the internal/executor/claude and internal/executor/gemini packages.

Status

  • Milestone: M-EXEC-EXPAND / M1 (core) + M2 (registration)
  • Target version: v0.15.0
  • Default model: gpt-5-codex

Registration

The Codex executor self-registers with the global executor factory via init():

package codex

func init() {
    Register()
}

func Register() {
    executor.GlobalFactory().Register("codex", func(cfg *executor.Config) (executor.Executor, error) {
        return New(cfg)
    })
}

As a result, any Go file that blank-imports this package automatically discovers Codex through executor.ExecutorProvider, with no changes to the coordinator. This matches the pattern already used by claude and gemini.

// internal/coordinator/provider_executor.go
import _ "github.com/sunholo-data/ailang/internal/executor/codex"

Command-line Flags

The executor invokes Codex in non-interactive mode, streaming NDJSON to stdout:

Flag Purpose
exec Non-interactive subcommand (one-shot run)
--json Emit NDJSON events on stdout
--skip-git-repo-check Allow running in non-git workspaces
--dangerously-bypass-approvals-and-sandbox Auto-approve tool calls (coordinator context)
--model <name> Select model (default gpt-5-codex)

Example invocation:

codex exec --json --skip-git-repo-check \
  --dangerously-bypass-approvals-and-sandbox \
  --model gpt-5-codex \
  "write fizzbuzz in python"

Source: Flag surface documented at https://developers.openai.com/codex/cli/ and confirmed by the NDJSON fixture at testdata/codex_response.jsonl.

Authentication

Codex CLI reads credentials from (in priority order):

  1. OPENAI_API_KEY environment variable — recommended for CI/CD and cloud machines
  2. The local credential cache created by codex login (ChatGPT Plus/Pro OAuth2 session)

The executor does not fail HealthCheck when OPENAI_API_KEY is unset — Codex may be using cached auth. A DEBUG_AGENT=1 trace prints a warning so missing auth is still surfaced for the developer.

Headless / Remote / Cloud Machines

On machines without a browser (cloud VMs, remote SSH sessions, coordinator workers):

# Device authorization flow — prints a URL + code; authorize on any device with a browser
codex login --device-auth

This is the OAuth2 Device Authorization Grant (RFC 8628). It avoids the browser redirect requirement of the standard codex login flow. Once authorized, credentials are cached at ~/.codex/auth.json (or equivalent platform path) and reused by all subsequent codex invocations on that machine.

Which to use:

Environment Method
Laptop / desktop with browser codex login
Cloud VM / SSH session / CI runner codex login --device-auth
Automated pipeline (no human) OPENAI_API_KEY env var
AILANG coordinator daemon OPENAI_API_KEY in coordinator env (preferred)

For the AILANG coordinator, OPENAI_API_KEY in the process environment is the most reliable approach — it survives container restarts and doesn't require cached session files to be present on every worker node. codex login --device-auth is the right choice for interactive developer machines that don't expose API keys.

Event Schema

Codex uses a flat NDJSON schema, distinct from Claude/Gemini's nested stream_event wrapping. See internal/executor/codex_compat_test.go for the compatibility analysis. Event types handled:

Type Handling
session / session_start / init Capture session id, start turn span
message Emit text, accumulate cumulative token totals
tool_use / tool_call Increment ToolCallCount, fire handler
tool_result Fire handler with output
result / turn_complete / message_stop Mark success, finalize tokens

Unknown event types are preserved in Result.ProviderData["codex_events"] as the original raw event map, so schema drift does not break the executor.

Token Semantics

Codex emits cumulative tokens_used in each message event (matching OpenAI API usage semantics), not per-turn deltas. The parser takes max(…) rather than summing, so the final totals reflect the cumulative state from the last event, not a double-counted sum.

Cost Model

gpt-5-codex: $1.25 / $10.00 per 1M tokens (input / output).

Token kind Cost per 1K
Input $0.00125
Output $0.0100
Cache read $0.000125

Source: https://platform.openai.com/docs/pricing (gpt-5-codex).

Update the CostModel() method in codex.go if pricing changes.

Configuration

Codex is exposed through the shared executor.Config struct:

cfg := &executor.Config{
    CodexPath:      "codex",            // Path to codex CLI binary
    CodexModel:     "gpt-5-codex",      // Default model
    TimeoutSeconds: 300,                // Hard ceiling per task
}

Per-task overrides via executor.Task.Model take precedence over the config default.

Timeouts

  • Hard ceiling: task.Timeout or cfg.TimeoutSeconds (default 5 min). Kills the subprocess when exceeded, returns Success: false.
  • Idle timeout: task.IdleTimeout (default 3 min). Kills the subprocess when no stdout activity for the full window; resets on every event.

Both timeouts are implemented using time.NewTimer + sync/atomic for the last-activity clock.

Health Check

HealthCheck(ctx) runs codex --version:

  • Binary missing → returns error (install with: npm i -g @openai/codex)
  • Binary present but --version fails → returns error
  • Binary OK, OPENAI_API_KEY unset → warns under DEBUG_AGENT=1, does not fail
  • Binary OK → returns nil

Known Limits

  • No session resumption: executor.Task.ResumeSessionID is accepted but currently unused. Codex CLI does not expose a resume flag.
  • No plugin/skill install flow: unlike Claude Code, Codex has no plugin directory surface. task.PluginDirs and task.Plugins are ignored.
  • --dangerously-bypass-approvals-and-sandbox is required for autonomous coordinator use. In a hardened environment this flag may need to be swapped for a policy-based approval mechanism (out of scope for this sprint).
  • Non-JSON stdout preamble (e.g. "Creating GCP exporters…") is tolerated: the parser skips lines that do not start with {.

Testing

go test ./internal/executor/codex/...        # unit + mock-binary integration
go test -cover ./internal/executor/codex/... # expect ≥ 70% coverage

The test suite uses a POSIX shell script as a fake codex binary that replays the fixture at testdata/codex_response.jsonl. No real OpenAI account or network access is required to run the tests.

References

Documentation

Overview

Package codex provides an Executor implementation for OpenAI Codex CLI.

Codex CLI emits a different NDJSON schema than Claude/Gemini: events are flat records with top-level "type":"message", "text", and "tokens_used" fields (see internal/executor/codex_compat_test.go for the compatibility analysis). A dedicated parser is required.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Register

func Register()

Register registers the Codex executor with the global factory.

Types

type CodexExecutor

type CodexExecutor struct {
	// contains filtered or unexported fields
}

CodexExecutor executes tasks using OpenAI Codex CLI.

func New

func New(cfg *executor.Config) (*CodexExecutor, error)

New creates a new CodexExecutor.

func (*CodexExecutor) Capabilities

func (e *CodexExecutor) Capabilities() []executor.Capability

Capabilities returns the list of features this executor supports.

func (*CodexExecutor) Close

func (e *CodexExecutor) Close() error

Close releases any resources held by the executor.

func (*CodexExecutor) CostModel

func (e *CodexExecutor) CostModel() *executor.CostModel

CostModel returns pricing for gpt-5-codex (the default Codex model). Source: https://platform.openai.com/docs/pricing gpt-5-codex: $1.25/$10.00 per 1M tokens = $0.00125/$0.01 per 1K.

func (*CodexExecutor) Execute

func (e *CodexExecutor) Execute(ctx context.Context, task *executor.Task) (*executor.Result, error)

Execute runs a task and returns the result.

func (*CodexExecutor) ExecuteStreaming

func (e *CodexExecutor) ExecuteStreaming(ctx context.Context, task *executor.Task, handler executor.EventHandler) (*executor.Result, error)

ExecuteStreaming runs a task with real-time event callbacks, parsing the Codex NDJSON stream into normalized executor events.

func (*CodexExecutor) HealthCheck

func (e *CodexExecutor) HealthCheck(ctx context.Context) error

HealthCheck verifies the codex binary exists on PATH and responds.

func (*CodexExecutor) Name

func (e *CodexExecutor) Name() string

Name returns the executor identifier.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL