08
Series Finale — Article 8 of 8
Anatomy of an AI Coding Agent

Building on the Giant

SDKs, APIs, and What's Next for AI-Powered Development

01

Standing on the Shoulders

Across seven articles, we've disassembled every major subsystem of the OpenAI Codex CLI. We traced signals from keystroke to kernel call, watching how a natural-language prompt becomes validated shell commands executing inside hardened sandboxes. We studied the event queues that keep the UI responsive, the approval policies that keep the system safe, and the multi-model reasoning architecture that keeps the answers sharp.

Now we arrive at the translation layer—the SDKs and APIs that let you build on top of all that machinery. If the CLI is the reference implementation, the SDKs are the contracts that let you automate, extend, and embed it in your own workflows.

The Journey So Far

01
The Grand Tour
Bird's-eye view of every subsystem—CLI parsing, model routing, sandbox execution, and the terminal UI.
02
The Nervous System
Dual event queues (SQ + EQ), the agent loop, turn lifecycle, and how streaming responses stay non-blocking.
03
The Brain
Model providers, rollout logic, multi-model orchestration, prompt assembly, and response parsing.
04
The Hands
Tool execution—shell commands, file writes, apply-patch, MCP integration, and how the agent takes action.
05
The Senses
Context gathering—file reading, repository mapping, conversation memory, and the context window budget.
06
The Exoskeleton
Platform sandboxes—macOS Seatbelt, Linux Landlock/seccomp, Docker isolation, and the network firewall.
07
The Safety Net
Approval policies, command allow/deny lists, auto-approval heuristics, and the trust escalation model.
08
Building on the Giant
SDKs, APIs, architecture patterns, and the future of AI-powered development. You are here.
02
TypeScript SDK

The TypeScript SDK

The TypeScript SDK is the most direct interface to the Codex engine. It mirrors the internal architecture closely, exposing three core abstractions: Codex (the client), CodexExec (a configured execution environment), and Thread (a conversation with the model).

Core Class Hierarchy

TypeScript
// Three levels of abstraction
const codex = new Codex({ apiKey, model });

// CodexExec: configured with sandbox policy + working dir
const exec = codex.exec({
  workingDir: "/path/to/repo",
  sandbox: "seatbelt",
  approvalPolicy: "auto-edit",
});

// Thread: a live conversation
const thread = exec.startThread("Fix the failing unit tests");
// or resume an existing one
const resumed = exec.resumeThread(threadId);

run() vs runStreamed()

The SDK offers two execution modes. run() returns a Promise<ThreadResult>—you get the final answer when the agent is done. runStreamed() returns an AsyncGenerator<ThreadEvent>, yielding events as they happen. This is the key to building responsive UIs and automation pipelines.

Event Types

EventWhen It Fires
ThreadStartedThread is initialized, model connection established
TurnStartedA new agent turn begins (prompt sent to model)
ItemStartedThe model begins producing a new item (message, tool call, etc.)
ItemUpdatedStreaming delta—partial text, partial tool arguments
ItemCompletedAn item is fully resolved
TurnCompletedAll items for this turn are done

Item Types

Each Item inside an event is one of these discriminated union variants:

Example: TODO-Fixer Automation

TypeScript
import { Codex } from "@openai/codex";

async function fixTodos(repoPath: string) {
  const codex = new Codex({ model: "o4-mini" });
  const exec = codex.exec({
    workingDir: repoPath,
    approvalPolicy: "auto-edit",
  });

  const stream = exec.runStreamed(
    "Find all TODO comments and fix the ones that " +
    "describe clearly actionable bugs."
  );

  for await (const event of stream) {
    if (event.type === "ItemCompleted") {
      const item = event.item;
      if (item.kind === "FileChange") {
        console.log(`Modified: ${item.path}`);
      }
      if (item.kind === "CommandExecution") {
        console.log(`Ran: ${item.command} (exit ${item.exitCode})`);
      }
    }
  }
}

Key insight: Because the stream is an AsyncGenerator, you can break out of it at any time, implement backpressure, or pipe events into another system. The agent keeps running until you stop consuming or the turn completes.

03
Python SDK

The Python SDK

The Python SDK takes a fundamentally different architectural approach. Instead of direct library bindings, it communicates with the app-server binary via JSON-RPC over stdio. The binary is the same compiled Rust/TypeScript core that powers the CLI—the Python layer is a thin typed wrapper around a subprocess pipe.

Architecture
┌──────────────────┐    stdio (JSON-RPC)    ┌──────────────────┐
│  Python Client    │ ◄──────────────────► │  app-server bin  │
│  (Pydantic)       │    req/res + events   │  (Rust/TS core)  │
└──────────────────┘                        └──────────────────┘
  Auto-generated                              Same sandbox,
  from protocol                               same model layer,
  schema                                      same everything

All the Pydantic models—ThreadEvent, Item, ExecPolicy—are auto-generated from the protocol schema. This means the Python types are always in sync with the TypeScript types, and you get full IDE autocompletion and type checking.

Context Manager Pattern

Python
from codex_sdk import CodexClient

# Context manager spawns/tears down the app-server
async with CodexClient(model="o4-mini") as client:
    result = await client.run(
        prompt="Refactor auth module to use JWT",
        working_dir="/path/to/repo",
        approval_policy="auto-edit",
    )
    for change in result.file_changes:
        print(f"  {change.path}: {change.action}")

Batch Code Generation

Python
import asyncio
from codex_sdk import CodexClient

async def generate_tests(modules: list[str]):
    """Generate test files for multiple modules in parallel."""
    async with CodexClient(model="o4-mini") as client:
        tasks = [
            client.run(
                prompt=f"Write comprehensive unit tests for {mod}",
                working_dir="/path/to/repo",
                approval_policy="full-auto",
            )
            for mod in modules
        ]
        results = await asyncio.gather(*tasks)
        for mod, result in zip(modules, results):
            print(f"{mod}: {len(result.file_changes)} files created")

asyncio.run(generate_tests(["auth", "billing", "notifications"]))

TypeScript vs Python: Side by Side

TypeScript SDK
// Direct library binding
const codex = new Codex({...});
const exec = codex.exec({...});

// Streaming via AsyncGenerator
for await (const ev of stream) {
  // handle event
}

// Manual lifecycle
thread.stop();

// Types: hand-written TS
// Transport: in-process
Python SDK
# JSON-RPC over stdio
async with CodexClient() as c:
    result = await c.run(...)

# Streaming via async iterator
async for ev in c.stream(...):
    # handle event

# Auto lifecycle (context mgr)
# exits on __aexit__

# Types: Pydantic (auto-gen)
# Transport: subprocess pipe
04
Extensibility

Custom MCP Servers

The Model Context Protocol (MCP) is the plug-in architecture for Codex's tool ecosystem. By building a custom MCP server, you give the agent access to any capability—databases, APIs, hardware devices, internal services—without modifying the Codex core.

SQLite Query Server Example

TypeScript
import { McpServer } from "@modelcontextprotocol/sdk";
import Database from "better-sqlite3";

const db = new Database("./analytics.db");
const server = new McpServer({
  name: "sqlite-query",
  version: "1.0.0",
});

server.tool(
  "query",
  "Run a read-only SQL query against the analytics database",
  {
    sql: { type: "string", description: "SELECT query to execute" },
  },
  async ({ sql }) => {
    // Safety: only allow SELECT statements
    if (!sql.trim().toUpperCase().startsWith("SELECT")) {
      throw new Error("Only SELECT queries are allowed");
    }
    const rows = db.prepare(sql).all();
    return {
      content: [{
        type: "text",
        text: JSON.stringify(rows, null, 2),
      }],
    };
  }
);

server.listen();  // stdio transport

Registration

Shell
# Register with the --mcp-server flag
$ codex --mcp-server sqlite-query --mcp-server-cmd "node ./sqlite-server.js"

# Or in .codex/config.json
{
  "mcpServers": {
    "sqlite-query": {
      "command": "node",
      "args": ["./sqlite-server.js"]
    }
  }
}

Once registered, the agent can invoke query like any other tool. The MCP protocol handles discovery, schema negotiation, and invocation—you just implement the business logic.

05
Patterns

Architecture Patterns Worth Stealing

Regardless of whether you use Codex, its codebase is a masterclass in building robust agent systems. Here are four patterns that deserve to be extracted and reused.

SQ/EQ Decoupling

Pausable, observable, recoverable, nestable event processing

The Submission Queue (SQ) and Event Queue (EQ) pattern decouples what the agent wants to do from what the system allows. Every tool call goes into SQ; the approval layer gates it; only approved calls reach EQ for execution.

Conceptual
User Prompt
    
    
[Model] ──▶ [SQ: Submission Queue]
                    
             [Approval Gate] ← pause/resume/deny
                    
              [EQ: Event Queue]
                    
            [Sandbox Execution]
                    
              [Result → Model]

Why it matters: You can pause the entire pipeline (for human review), observe every transition (for logging/auditing), recover from failures (retry from SQ), and nest sub-agents (each with their own SQ/EQ pair).

Platform-Specific Sandbox Abstraction

The Sandbox trait pattern—one interface, multiple OS backends

Instead of littering the codebase with if (os === "darwin") checks, Codex defines a Sandbox trait with a uniform interface. Each platform provides its own implementation: Seatbelt profiles on macOS, Landlock + seccomp-bpf on Linux, Docker container isolation as a cross-platform fallback.

TypeScript
interface Sandbox {
  exec(cmd: string, opts: ExecOpts): Promise<ExecResult>;
  writeFile(path: string, data: Buffer): Promise<void>;
  readFile(path: string): Promise<Buffer>;
  destroy(): Promise<void>;
}

// Factory selects the right implementation
function createSandbox(policy): Sandbox {
  if (platform === "darwin") return new SeatbeltSandbox(policy);
  if (platform === "linux")  return new LandlockSandbox(policy);
  return new DockerSandbox(policy);
}

Policy-as-Code: ExecPolicy

Declarative security boundaries—no imperative guard clauses

The ExecPolicy is a data structure, not a function. It declares what's allowed (file globs, command patterns, network hosts) and the execution engine interprets it. This makes policies auditable, composable, and serializable.

JSON
{
  "autoApprove": {
    "commands": ["npm test", "npm run lint", "git diff *"],
    "fileGlobs": ["src/**/*.ts", "test/**/*.ts"],
    "networkHosts": ["registry.npmjs.org"]
  },
  "deny": {
    "commands": ["rm -rf *", "curl * | bash"],
    "fileGlobs": [".env*", "**/secrets/**"]
  }
}

Hexagonal Architecture

Ports and adapters—the core knows nothing about the outside world

The agent core communicates through ports (abstract interfaces) connected to adapters (concrete implementations). The model provider is a port. The sandbox is a port. The UI is a port. The MCP transport is a port. This is why the same core can power the CLI, the SDK, and a hypothetical web UI.

Conceptual
          ┌─ Model Provider (OpenAI, local, etc.)
          
[Port] ◄──┤
          
   [Agent Core] ──▶ [Port] ◄── Sandbox (Seatbelt, Landlock, Docker)
          
[Port] ◄──┤
          
          └─ UI Adapter (Terminal, SDK stream, Web)
06
Retrospective

Series Retrospective

Eight articles. Thousands of lines of source examined. Here is the complete mental model we've built:

The Complete Mental Model

  1. Prompt enters the CLI — parsed, validated, combined with system instructions and context.
  2. Model provider routes the call — selects the right model, manages rollout percentages, assembles the full prompt with tool schemas.
  3. Response streams back — through the EQ, rendered incrementally in the Ink-based terminal UI.
  4. Tool calls are intercepted — placed in the SQ, checked against ExecPolicy, presented for approval if needed.
  5. Approved calls execute in the sandbox — platform-specific isolation ensures the agent can't escape its boundaries.
  6. Results feed back to the model — completing the loop, enabling multi-turn reasoning and iterative problem-solving.
  7. Safety runs at every layer — from declarative policies at the top to kernel-level sandboxing at the bottom.
  8. SDKs expose it all programmatically — typed events, streaming generators, and the same guarantees as the CLI.

The throughline: Every design decision in Codex optimizes for the same thing—giving an AI agent maximum capability within minimum trust boundaries. Power without peril. Autonomy within constraints. This is the template for every agent system that will follow.

07
Future

The Next Frontier

The architecture we've studied isn't just a snapshot—it's a forward-compatible foundation. Here's what the next generation will bring, and why the current design is ready for it.

Now

Better Reasoning Models

Longer chains of thought, deeper planning, fewer hallucinations. The agent loop doesn't change—only the model behind the port.

Richer Tool Ecosystems

MCP servers for every API, every database, every cloud service. The plug-in architecture scales without touching the core.

Multi-Agent Coordination

Nested SQ/EQ pairs already support sub-agents. The next step is agents that delegate, review each other's work, and merge results.

Fine-Tuning & Specialization

Organization-specific models that know your codebase conventions. The model provider port makes swapping seamless.

Formal Verification

Proving that sandbox policies enforce what they claim. Policy-as-code makes this tractable—you can verify data, not code paths.

Decentralized Computing

Running agents on local hardware, edge devices, or distributed clusters. The hexagonal architecture already decouples compute from interface.

Notice the pattern: every future capability maps to an existing port in the architecture. Better models? Swap the model adapter. More tools? Add MCP servers. Multiple agents? Nest the event queues. This is the power of getting the architecture right from the start.

08

Conclusion

We set out to understand how an AI coding agent really works—not the marketing pitch, not the demo, but the actual engineering. What we found was a system built with the same principles that make any great software great: clear boundaries, explicit contracts, defense in depth, and composable abstractions.

The Codex CLI is not magic. It is event queues and sandbox profiles and policy engines and model providers, wired together with discipline and taste. And because it's open source, because it runs on your machine, because you can read every line—it's a system you can understand.

Understanding is the prerequisite to trust. Trust is the prerequisite to delegation. And delegation—giving an AI agent real autonomy over your code—is where we're all heading.

The future of AI coding isn't in cloud-hosted black boxes.
It's in local, transparent systems you understand and control.

SERIES COMPLETE

You've finished all 8 articles of "Anatomy of an AI Coding Agent: Dissecting the OpenAI Codex CLI." You now have the complete mental model—from keystroke to kernel call, from prompt to policy, from local sandbox to the frontier of what's next.