Skip to main content

Overview

The Agent Runner wraps workspace creation, prompt rendering, and app-server client integration. It creates/reuses workspaces, builds prompts from workflow templates, launches the coding agent subprocess, and forwards app-server events to the orchestrator.
On any error during the agent run, the worker attempt fails and the orchestrator will retry according to the configured backoff policy.

Agent Runner Contract

The Agent Runner executes the following sequence:

1. Workspace Creation

Create or reuse workspace for the issue

2. Prompt Building

Build prompt from workflow template

3. Session Startup

Start app-server session

4. Event Forwarding

Forward app-server events to orchestrator
Workspaces are intentionally preserved after successful runs to enable continuation across multiple agent sessions.

Workspace Creation and Lifecycle

Workspace Path Determination

Workspace Root
workspace.root (normalized path; the config layer expands path-like values and preserves bare relative names)
Per-Issue Workspace Path
<workspace.root>/<sanitized_issue_identifier>

Creation and Reuse Algorithm

This section does not assume any specific repository/VCS workflow. Workspace preparation beyond directory creation (e.g., dependency bootstrap, checkout/sync, code generation) is implementation-defined and typically handled via hooks.

Workspace Hooks

Supported hooks and their execution semantics:

after_create

When: Only when a workspace directory is newly createdFailure: Aborts workspace creationTimeout: Uses hooks.timeout_ms (default: 60000)

before_run

When: Before each agent attempt after workspace preparation and before launching the coding agentFailure: Aborts the current attemptTimeout: Uses hooks.timeout_ms (default: 60000)

after_run

When: After each agent attempt (success, failure, timeout, or cancellation) once the workspace existsFailure: Logged but ignoredTimeout: Logged but ignored

before_remove

When: Before workspace deletion if the directory existsFailure: Logged but ignored; cleanup still proceedsTimeout: Logged but ignored

Workspace Safety Invariants

Invariant 1: Agent CWD

Run the coding agent only in the per-issue workspace path. Before launching the coding-agent subprocess, validate:cwd == workspace_path

Invariant 2: Path Containment

Workspace path must stay inside workspace root:
  • Normalize both paths to absolute
  • Require workspace_path to have workspace_root as a prefix directory
  • Reject any path outside the workspace root

Invariant 3: Sanitized Keys

Workspace key is sanitized:
  • Only [A-Za-z0-9._-] allowed in workspace directory names
  • Replace all other characters with _

Prompt Construction

Inputs

workflow.prompt_template
string
required
Markdown body from WORKFLOW.md
issue
object
required
Normalized issue object with all fields
attempt
integer | null
Optional retry/continuation metadata

Rendering Rules

  • Render with strict variable checking (unknown variables fail rendering)
  • Render with strict filter checking (unknown filters fail rendering)
  • Convert issue object keys to strings for template compatibility
  • Preserve nested arrays/maps (labels, blockers) so templates can iterate

Retry/Continuation Semantics

Failure Behavior

If prompt rendering fails:
  • Fail the run attempt immediately
  • Let the orchestrator treat it like any other worker failure and decide retry behavior

Codex App-Server Integration

Launch Contract

Command
string
required
codex.command (default: codex app-server)
Invocation
string
required
bash -lc <codex.command>
Working Directory
string
required
Workspace path
Stdout/Stderr
string
required
Separate streams
Framing
string
required
Line-delimited protocol messages on stdout (JSON-RPC-like JSON per line)

Session Startup Handshake

The client must send these protocol messages in order:

Session Identifiers

Thread ID

Read from thread/start result: result.thread.id

Turn ID

Read from each turn/start result: result.turn.id

Session ID

Emit as: session_id = "<thread_id>-<turn_id>"

Continuation

Reuse the same thread_id for all continuation turns inside one worker run

Streaming Turn Processing

The client reads line-delimited messages until the turn terminates.

Line Handling Requirements

  • Read protocol messages from stdout only
  • Buffer partial stdout lines until newline arrives
  • Attempt JSON parse on complete stdout lines
  • Stderr is not part of the protocol stream:
    • Ignore it or log it as diagnostics
    • Do not attempt protocol JSON parsing on stderr

Emitted Runtime Events

The app-server client emits structured events to the orchestrator callback. Each event includes:
event
string
required
Event type enum/string
timestamp
timestamp
required
UTC timestamp
codex_app_server_pid
string
Process ID if available
usage
object
Optional token counts
Important event types:

session_started

startup_failed

turn_completed

turn_failed

turn_cancelled

turn_ended_with_error

turn_input_required

approval_auto_approved

unsupported_tool_call

notification

other_message

malformed

Approval, Tool Calls, and User Input Policy

Approval, sandbox, and user-input behavior is implementation-defined.
Each implementation should document its chosen approval, sandbox, and operator-confirmation posture.

Timeouts and Error Mapping

codex.read_timeout_ms
integer
default:5000
Request/response timeout during startup and sync requests
codex.turn_timeout_ms
integer
default:3600000
Total turn stream timeout (1 hour)
codex.stall_timeout_ms
integer
default:300000
Enforced by orchestrator based on event inactivity (5 minutes). If <= 0, stall detection is disabled.

Build docs developers (and LLMs) love