Codex App-Server Provider Slice
Date: 2026-05-10
Goal#
Replace the current one-shot codex exec --json harness path with a managed
Codex app-server transport, while preserving Code Almanac's provider-neutral
AgentRunSpec -> HarnessEvent -> process manager boundary.
The user-facing goal is richer observability for foreground runs and
almanac jobs attach: tool/file/command activity should be visible as
structured events, not only as coarse text.
Non-goals#
- Do not copy T3 Code's full server architecture, Effect runtime, WebSocket UI, or persistence model.
- Do not add a hosted service.
- Do not change the public CLI surface.
- Do not implement long-lived multi-turn user chat. Each Code Almanac operation is still one managed run.
- Do not add Codex subagent support in this slice unless the protocol can be
mapped cleanly without changing
AgentRunSpec.
Current State#
src/harness/providers/codex.ts runs:
codex exec --json --sandbox workspace-write --skip-git-repo-check -C <cwd> <prompt>It parses stdout JSONL into the existing HarnessEvent union. This is good
enough for a simple one-shot job but weak for live inspection.
T3 Code uses codex app-server over JSON-RPC and receives richer notifications:
thread/startedthread/status/changedthread/tokenUsage/updatedturn/startedturn/plan/updatedturn/diff/updateditem/starteditem/completeditem/agentMessage/deltaitem/commandExecution/outputDeltaturn/completed
Design#
1. Keep the Harness Boundary#
Do not change operation code. Build, Absorb, and Garden still produce
AgentRunSpec. The Codex provider adapter owns all Codex-specific process,
protocol, and event mapping behavior.
2. Add Structured Tool Details to HarnessEvent#
Extend existing events rather than replacing them:
type ToolDisplayKind =
| "read"
| "write"
| "edit"
| "search"
| "shell"
| "mcp"
| "web"
| "agent"
| "unknown";
interface HarnessToolDetails {
kind?: ToolDisplayKind;
title?: string;
path?: string;
command?: string;
cwd?: string;
status?: "started" | "completed" | "failed" | "declined";
exitCode?: number | null;
durationMs?: number | null;
summary?: string;
raw?: unknown;
}Then add optional display?: HarnessToolDetails to tool_use and
tool_result.
Existing logs remain valid because this is additive. Foreground rendering can show:
[tool] Reading file .almanac/pages/foo.md
[tool] Running command almanac health
[tool] Editing file .almanac/pages/bar.md3. App-Server Runtime#
Create a small local runtime under src/harness/providers/codex-app-server.ts
or equivalent:
- spawn
codex app-server --listen stdio:// - send JSON-RPC
initialize - send
thread/startwith:cwdmodelsandbox: "workspace-write"approvalPolicy: "never"baseInstructions/developerInstructionsfromsystemPromptwhen usefulephemeral: true
- send
turn/startwith one text input containing the combined prompt - collect async notifications until
turn/completed - close/kill the child process
The runtime must manage:
- request ids and pending responses
- stdout line framing
- stderr capture
- child error/close handling
- timeout-free normal operation
- cleanup on completion or failure
4. Event Mapping#
Map app-server notifications to the existing HarnessEvent model:
item/agentMessage/delta->text_deltaitem/plan/delta->tool_summaryortext_deltawith a clear prefixturn/plan/updated->tool_summaryitem/started:- commandExecution ->
tool_usewithdisplay.kind = "shell" - fileChange ->
tool_usewithdisplay.kind = "edit" - mcpToolCall ->
tool_usewithdisplay.kind = "mcp" - dynamicToolCall ->
tool_usewith inferred kind/title - webSearch ->
tool_usewithdisplay.kind = "web" - imageView ->
tool_use - collabAgentToolCall ->
tool_usewithdisplay.kind = "agent"
- commandExecution ->
item/completed-> matchingtool_resultcommandExecutionOutputDelta->tool_summaryfor now, preserving output without overwhelming foreground outputthread/tokenUsage/updated->context_usageturn/completed-> terminaldoneand finalHarnessResulterror/ failed completed turn ->error
For command actions, infer readable labels:
read-> "Reading file"listFiles-> "Listing files"search-> "Searching"- unknown command -> "Running command"
5. Tests#
Add or update tests around:
- JSON-RPC request/response correlation
- successful fake app-server run
- tool event mapping for command read/search/shell and file changes
- assistant deltas and final result
- malformed JSON or child exit classification
- foreground event formatting with structured display details
- provider metadata update for Codex streaming/session/interrupt capability if app-server becomes the default transport
6. Review Loop#
After the first working implementation:
- Run focused tests.
- Commit and push.
- Request a code-quality review subagent.
- Fix must-fix and should-fix findings.
- Re-run focused tests plus full
npm test. - Commit and push review fixes.
- Garden the wiki pages for Codex provider/process architecture if the code changed the durable design.
Acceptance Criteria#
almanac garden --foreground --using codexuses app-server by default.- Foreground runs show structured tool activity with useful labels/paths where available.
.almanac/runs/<id>.jsonlcontains structured tool details.- Existing operations still work with Claude.
- Codex unsupported feature failures remain clear.
- Tests cover the app-server runtime without requiring real Codex auth.
npm testpasses before final push.