Agent Runner & Codex Adapter
The AgentRunner is the framework layer that dispatches AI workloads to
provider-specific adapters. In production, CodexAdapter invokes the Codex CLI
as a subprocess. In tests, FakeAgentAdapter returns pre-configured responses.
Why an adapter layer?
Stages submit typed AgentTask objects to AgentRunner. The runner selects
the appropriate adapter, applies timeouts, coordinates cancellation, and returns
a normalized AgentResult. This keeps stages and the framework decoupled from
any specific AI provider.
CodexAdapter — the production adapter
CodexAdapter (in src/agents/adapters/codex.py) invokes the Codex CLI binary
as a subprocess. Key behavior:
- Builds the CLI invocation with
--json(streaming structured output mode),--schemafor JSON-schema enforcement, and provider config flags. - Inherits
OPENAI_API_KEYandOPENAI_BASE_URLfrom the process environment (or explicitly fromCodexRuntimeConfig.env). - Streams output lines and emits
agent_stream/agent_logevents. - Registers the subprocess PID with
CancellationManagerfor forceful termination on cancellation. - Parses the final JSON output and returns an
AgentResult.
Local vs. AWS behavior
| Context | OPENAI_BASE_URL |
Isolation |
|---|---|---|
| Local dev | Not set (direct OpenAI) or http://localhost:4000/v1 (llm-proxy) |
Subprocess in local shell |
| AWS scan runner | https://llm-proxy.internal/v1 |
Subprocess in ECS container |
| Tests | N/A | FakeAgentAdapter — no subprocess |
No Docker isolation is used for agents. The subprocess runs directly in the runner process.
Codex CLI flags used
| Flag | Purpose |
|---|---|
--json |
Streaming JSON output mode |
--schema <json> |
Enforce structured output schema |
--model <id> |
Model identifier (from RunPolicy.model) |
--model-reasoning-effort <level> |
"low", "medium", "high" |
Cancellation
When CancellationManager.cancel_active_agent_calls() is called:
CodexAdapter.cancel(task_id)sendsSIGTERMto the subprocess.- If the process does not exit within the grace period,
SIGKILLfollows.
The adapter raises CodexAdapterCancelled which the runner translates to
AgentCancelled. BaseStage catches this and returns a cancelled StageOutput.
Configuration
# Required
VEGA_CODEX_BIN=/usr/local/bin/codex
OPENAI_API_KEY=sk-...
# Optional
OPENAI_BASE_URL=http://localhost:4000/v1 # vega-llm-proxy
VEGA_CODEX_REASONING_EFFORT=medium # low | medium | high
VEGA_CODEX_COLLAB=true
VEGA_CODEX_CWD=/tmp/codex-workdir
See Configuration for the full variable list.
Debugging agent calls
- Check
agent_startedandagent_completed/agent_failedevents. - Set
VEGA_CODEX_REASONING_EFFORT=highfor better output quality at higher cost. - Check
agent_logevents for raw subprocess output lines. - Confirm
OPENAI_API_KEYandOPENAI_BASE_URLare set correctly. - Run the manual debug runner:
python tests/manual/e2e_scan_debug.py.
Comparison: old vs. new
| Old architecture | New architecture |
|---|---|
codex binary called directly from stage scripts |
CodexAdapter via AgentRunner interface |
| Codex isolation via Docker | Direct subprocess (no Docker wrapper) |
| Stage-specific Codex invocation logic | Common invocation in CodexAdapter.run() |
| Cancellation by subprocess pid in stage | Cancellation registered with CancellationManager |
| No adapter abstraction | AgentAdapter protocol → multiple adapters |
FakeCodexRunner ad-hoc |
FakeAgentAdapter in testing/ |