Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
518867c
docs: pin codex app-server protocol schema
Jul 12, 2026
659b61d
docs: cite app-server wire framing
Jul 12, 2026
f00055b
feat: add codex app-server behavior kinds
Jul 12, 2026
64ca07c
feat: add codex app-server manager core
Jul 12, 2026
8308850
feat: add codex app-server thread lifecycle
Jul 12, 2026
188f978
docs: document codex app-server transport
Jul 12, 2026
9608262
docs: verify codex app-server rollout continuity
Jul 12, 2026
f8e3460
feat: resume codex app-server sessions by thread
Jul 12, 2026
b210388
feat: add codex app-server readiness polling
Jul 12, 2026
05ae562
feat: default codex launchers to app-server
Jul 12, 2026
2913a6e
feat: interrupt codex app-server before kill
Jul 12, 2026
27107ba
test: cover codex app-server delivery tier
Jul 12, 2026
fe6d8ae
feat: add codex app-server host ops
Jul 12, 2026
9c35a1f
chore: keep spawn under file-size guard
Jul 12, 2026
8a5395d
feat: add codex app-server pane bridge
Jul 12, 2026
9061e8f
feat: route codex app-server approvals structurally
Jul 12, 2026
7e38326
feat: complete codex app-server host lifecycle
Jul 12, 2026
930fdfd
fix: remove runtime circular dependencies
Jul 12, 2026
a86acac
fix: sort circular dependency guard input
Jul 12, 2026
d6edf50
fix: update app-server verification tests
Jul 12, 2026
f7d08c8
fix: address app-server review issues
Jul 12, 2026
01a5573
chore: merge origin main for pan-2597
Jul 14, 2026
e0315aa
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 14, 2026
08458d5
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 14, 2026
c42c963
fix: restore PAN-2597 verification tests
Jul 14, 2026
550c0f1
Revert "fix: restore PAN-2597 verification tests"
Jul 14, 2026
4f66a55
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 14, 2026
d6ecff7
fix: restore PAN-2597 verification after main merge
Jul 14, 2026
5470220
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 14, 2026
cd80b51
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 14, 2026
03e7927
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 14, 2026
b55b256
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 14, 2026
e1a89d6
fix: repair agent spawner test fixtures
Jul 14, 2026
1f75b0b
test: remove stale planning task materializer assertions
Jul 14, 2026
b9b9816
test: remove stale agent bead count test
Jul 14, 2026
8a754e4
test: update stale record verification assertions
Jul 14, 2026
dce8ca1
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 14, 2026
22cce3b
fix: update review feedback delivery assertion (PAN-2597)
Jul 14, 2026
3787001
Merge origin/main into feature/pan-2597
Jul 15, 2026
9b0f8e2
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 15, 2026
963d704
fix: restore lint guards after main sync (PAN-2597)
Jul 15, 2026
5ac7e40
Merge remote-tracking branch 'origin/main' into feature/pan-2597
Jul 15, 2026
eb12cf2
test: isolate Codex auth route fixtures (PAN-2597)
Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ git push origin v0.9.4

## Harnesses

Overdeck supports three coding-agent harnesses: `claude-code` (default), `pi`/`ohmypi` (alternative, multi-provider), and `codex` (OpenAI Codex CLI — first-party agent loop for the GPT model family; work agents run as live TUI sessions via `codexMode: work-tui`, runtime adapter at `src/lib/runtimes/codex.ts`). The harness is picked per spawn at plan kickoff, role runs, work agent start, and the conversation panel; roles read harness/model defaults from Settings. Pi + Anthropic + subscription auth is the only blocked combination (ToS gate in `src/lib/harness-policy.ts`).
Overdeck supports three coding-agent harnesses: `claude-code` (default), `pi`/`ohmypi` (alternative, multi-provider), and `codex` (OpenAI Codex CLI — first-party agent loop for the GPT model family; Codex work agents use the persistent `codex app-server` transport by default, with `codex.transport: tui` as a temporary escape hatch to the legacy `codexMode: work-tui` path). The Codex runtime adapter remains `src/lib/runtimes/codex.ts`. The harness is picked per spawn at plan kickoff, role runs, work agent start, and the conversation panel; roles read harness/model defaults from Settings. Pi + Anthropic + subscription auth is the only blocked combination (ToS gate in `src/lib/harness-policy.ts`).

See [configuration/harnesses.mdx](configuration/harnesses.mdx) for installation, picker locations, ToS rules, and troubleshooting. The wider field of coding-agent harnesses Overdeck could adopt is surveyed in [reference/harness-landscape.mdx](reference/harness-landscape.mdx). (`docs/HARNESSES.md` is now a redirect stub — the harness docs are published in the Mintlify site.)

Expand Down
21 changes: 18 additions & 3 deletions configuration/harnesses.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ that runs in tmux. The **model** is what the binary calls. They are picked
independently per spawn.

<Info>
**Supported today:** `claude-code` (default) and `ohmypi`. For the wider field of
**Supported today:** `claude-code` (default), `ohmypi`, and `codex`. For the wider field of
coding-agent harnesses Overdeck could adopt next — and how each one handles
skills, MCP, and AGENTS.md — see the [Harness Landscape](/reference/harness-landscape).
</Info>
Expand All @@ -21,6 +21,7 @@ skills, MCP, and AGENTS.md — see the [Harness Landscape](/reference/harness-la
| --- | --- | --- | --- |
| `claude-code` | [Anthropic Claude Code](https://github.com/anthropics/claude-code) | All workflows | Default if you don't pick. Required for Anthropic-subscription users. |
| `ohmypi` | [oh-my-pi](https://github.com/oh-my-pi/pi-coding-agent) (`omp`) | Multi-provider work, large workspaces | Adds oh-my-pi RPC over a named pipe; reads `~/.claude/skills` for skill parity. |
| `codex` | [OpenAI Codex CLI](https://github.com/openai/codex) (`codex`) | OpenAI-model work | Uses persistent `codex app-server` sessions by default; requires Codex CLI `0.144.0` or newer. |

## Supported harnesses

Expand Down Expand Up @@ -51,8 +52,15 @@ skills, MCP, and AGENTS.md — see the [Harness Landscape](/reference/harness-la
- First-party OpenAI agent loop for the GPT model family (GPT-5.6 Sol/Terra/Luna,
GPT-5.5, GPT-5.4, GPT-5.3-Codex, …) with ChatGPT-subscription or API-key auth.
- `gpt-5.6-sol` is the default OpenAI model surfaced by this harness.
- Work agents run as live, attachable TUI sessions (`codexMode: work-tui`)
under the PTY supervisor — never one-shot `codex exec`.
- Work agents use a persistent `codex app-server` child by default. Overdeck
talks newline-delimited JSON-RPC over stdio, so message delivery, approvals,
readiness, and interruption are structured protocol events rather than TUI
keystrokes.
- Set `codex.transport: tui` as a temporary escape hatch to the legacy live,
attachable TUI path (`codexMode: work-tui`) under the PTY supervisor.
Both transports are persistent sessions — never one-shot `codex exec`.
- The app-server transport requires Codex CLI `0.144.0` or newer. Upgrade with
your normal Codex CLI install method, then verify with `codex --version`.
- Runtime adapter at `src/lib/runtimes/codex.ts`; per-agent thread-id pinning
enables session introspection and cost parsing
(`src/lib/cost-parsers/codex-parser.ts`).
Expand Down Expand Up @@ -198,6 +206,13 @@ If your dashboard shows a stale heartbeat, check
`~/.overdeck/heartbeats/<id>.json` — omp writes there on every
`tool_execution_end`.

### Codex app-server is unavailable or too old

Run `codex --version`. The app-server transport requires Codex CLI `0.144.0`
or newer and the `codex app-server` subcommand. If the version is too old,
upgrade Codex CLI and start a fresh session. To keep work moving during a
rollout, set `codex.transport: tui` to use the legacy TUI transport temporarily.

## Tradeoffs

### RPC over named pipe vs tmux paste-buffer
Expand Down
6 changes: 6 additions & 0 deletions docs/AGENT-STATE-PLANES.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ Lifecycle events project status, while the Deacon keeps a thin patrol as a
dropped-event safety net. A global Deacon pause gates every patrol and recovery
path.

Codex app-server sessions keep the same liveness oracle. The tmux pane hosts
Overdeck's Codex app-server host process, which owns the `codex app-server`
child over stdio and renders a readable event feed into the pane. Deacon still
patrols the tmux session; it does not treat the Codex child process as a
separate liveness source.

## Resume classifier and intent policy

`getAgentResumeGateBlockReason()` is the only classifier for `paused`, `troubled`,
Expand Down
152 changes: 152 additions & 0 deletions docs/CODEX-APP-SERVER-PROTOCOL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Codex App Server Protocol

Verified on this workspace with `codex-cli 0.144.1`.

The fixture at `src/lib/codex/__fixtures__/app-server-schema.json` was produced by:

```bash
codex app-server generate-json-schema --out /tmp/pan-2597-codex-schema
cp /tmp/pan-2597-codex-schema/codex_app_server_protocol.schemas.json \
src/lib/codex/__fixtures__/app-server-schema.json
```

The generated bundle includes the methods below. The v2-specific generated bundle
(`codex_app_server_protocol.v2.schemas.json`) uses the same active method names and
contains the resolved v2 parameter schemas.

## Wire Envelope

`JSONRPCRequest` is an object with required `id` and `method`, optional `params`,
and optional `trace`. `JSONRPCNotification` is an object with required `method`
and optional `params`; it has no `id`. `JSONRPCResponse` has required `id` and
`result`; `JSONRPCError` has required `id` and `error`.

The generated schemas do not define a `jsonrpc` property on requests,
notifications, responses, or errors. Do not send `jsonrpc: "2.0"`.

`RequestId` accepts either a string or an integer. The schema verifies that
integer IDs are valid, not that they are mandatory. Overdeck uses a monotonic
integer counter because the t3code reference manager reads `context.nextRequestId`,
increments it, and writes that value as `id`.

The generated schema describes one JSON-RPC message object at a time; it does
not encode stream framing. The newline-delimited stdio framing is verified from
the t3code reference manager, whose `writeMessage` serializes one message and
writes `${encoded}\n` to the Codex child stdin.

## Client Requests

| Purpose | Generated method | t3code reference | Status |
| --- | --- | --- | --- |
| Handshake request | `initialize` | `initialize` | Match |
| Start a thread | `thread/start` | `thread/start` | Match |
| Resume a thread | `thread/resume` | `thread/resume` | Match |
| Start a turn | `turn/start` | `turn/start` | Match |
| Interrupt a turn | `turn/interrupt` | `turn/interrupt` | Match |
| Read a thread | `thread/read` | `thread/read` | Match |
| Read account state | `account/read` | `account/read` | Match |
| Roll back a thread | `thread/rollback` | `thread/rollback` | Match; present for completeness although not in the first manager slice |

## Client Notifications

| Purpose | Generated method | t3code reference | Status |
| --- | --- | --- | --- |
| Complete handshake | `initialized` | `initialized` | Match |

## Server Notifications

| Purpose | Generated method | t3code reference | Status |
| --- | --- | --- | --- |
| Thread opened | `thread/started` | `thread/started` | Match |
| Turn began | `turn/started` | `turn/started` | Match |
| Turn finished | `turn/completed` | `turn/completed` | Match |
| Structured error | `error` | `error` | Match |

Relevant generated payload shapes:

- `ThreadStartedNotification` requires `thread`.
- `TurnStartedNotification` requires `threadId` and `turn`.
- `TurnCompletedNotification` requires `threadId` and `turn`.
- `ErrorNotification` requires `error`, `threadId`, `turnId`, and `willRetry`.

## Server Requests

| Purpose | Generated method | t3code reference | Status |
| --- | --- | --- | --- |
| Command approval | `item/commandExecution/requestApproval` | `item/commandExecution/requestApproval` | Match |
| File-change approval | `item/fileChange/requestApproval` | `item/fileChange/requestApproval` | Match |
| User input for a tool | `item/tool/requestUserInput` | `item/tool/requestUserInput` | Match |
| File-read approval | Not present in generated schema | `item/fileRead/requestApproval` | Divergence: codex-cli 0.144.1 does not expose this server request in the generated schema |

The generated schema also includes server requests outside the current Overdeck
plan: `item/permissions/requestApproval`, `item/tool/call`,
`mcpServer/elicitation/request`, `account/chatgptAuthTokens/refresh`,
`attestation/generate`, plus deprecated `applyPatchApproval` and
`execCommandApproval`.

## Response Payloads

Approval replies are JSON-RPC responses to the server request `id`:

- `CommandExecutionRequestApprovalResponse` requires `decision`.
- `FileChangeRequestApprovalResponse` requires `decision`.
- `ToolRequestUserInputResponse` requires `answers`, an object whose values are
`ToolRequestUserInputAnswer` objects.
- `ToolRequestUserInputAnswer` requires `answers`, an array of strings.

t3code matches this shape by converting provider answers into
`Record<string, { answers: string[] }>` before writing `{ id, result: { answers } }`.

## Transcript And Cost Continuity

Checkpoint C1 was verified with a live `codex-cli 0.144.1 app-server` session
using an isolated temporary `CODEX_HOME`.
`thread/start` returned a planned rollout path immediately, but Codex did not
create the JSONL until the first `turn/start` completed.

After a minimal prompt (`Reply with exactly OK.`), Codex wrote:

```text
<isolated-CODEX_HOME>/sessions/2026/07/12/rollout-2026-07-12T14-28-38-019f5796-a6eb-7ec0-91e6-ac452b37e193.jsonl
```

The existing rollout path helpers worked unchanged:

- `findLatestRollout(codexHome)` returned the live JSONL.
- `findRolloutPath(codexHome, "019f5796-a6eb-7ec0-91e6-ac452b37e193")`
returned the same JSONL.

The existing cost parser also worked unchanged. `parseCodexSessionSync` returned
one assistant message, model `gpt-5.6-sol`, 26,389 input tokens, 9,984 cached
input tokens, 5 output tokens, and total cost `0.087167`.
`parseCodexSessionCostEventsSync` emitted one cost event with the same usage.

The existing conversation reader path worked unchanged. `getCachedMessages`
already treats `rollout-*.jsonl` as Codex and dispatches to
`parseCodexConversationMessages`; that parser returned two chat messages
(`user`, `assistant`), total tokens `26394`, total cost `0.087167`, and the
assistant text `OK`.

No fallback transcript adapter is required for app-server. The durable transcript
and cost source remains the Codex rollout JSONL under the per-agent
`CODEX_HOME/sessions/YYYY/MM/DD/` tree.

## Per-Turn Model Checkpoint

Checkpoint C3 was verified with `codex-cli 0.144.1` using an isolated
temporary `CODEX_HOME`. A thread was started with model
`gpt-5.6-sol`, then a later `turn/start` in the same thread included an explicit
`model: "gpt-5.6-sol"` field. The app-server accepted the request without a
protocol error, so Overdeck uses the native per-turn `model` field for
app-server model overrides on this version. No restart-and-resume fallback is
needed for Codex CLI 0.144.x.

## Divergences

1. `item/fileRead/requestApproval` appears in t3code's pending approval union and
routing logic, but it is absent from codex-cli 0.144.1's generated
`ServerRequest` schema. Overdeck implementation beads must not assume a
file-read approval request exists unless a later schema version verifies it.
2. The plan shorthand "integer id counter" is an Overdeck/t3code implementation
choice, not a schema restriction. The generated `RequestId` allows string or
integer IDs; Overdeck uses integers.
17 changes: 13 additions & 4 deletions packages/contracts/src/harness-behavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ import type { Harness } from "./types"
export type RuntimeName = Harness
export type HarnessName = RuntimeName | "pi"

export type HarnessLaunchCommandKind = "claude-code" | "ohmypi-rpc" | "codex-work-tui"
export type HarnessDeliveryKind = "pty-supervisor" | "rpc-fifo" | "codex-exec-resume" | "tmux-paste"
export type HarnessReadinessKind = "claude-session-signal" | "ohmypi-ready-file" | "codex-tui-prompt"
export type HarnessLaunchCommandKind = "claude-code" | "ohmypi-rpc" | "codex-work-tui" | "codex-app-server"
export type HarnessDeliveryKind =
| "pty-supervisor"
| "rpc-fifo"
| "codex-exec-resume"
| "codex-app-server-rpc"
| "tmux-paste"
export type HarnessReadinessKind =
| "claude-session-signal"
| "ohmypi-ready-file"
| "codex-tui-prompt"
| "codex-app-server-ready"
export type HarnessTranscriptKind = "claude-jsonl" | "ohmypi-jsonl" | "codex-rollout-jsonl"
export type HarnessSessionIdSource = "launcher-session-id" | "transcript-jsonl" | "codex-thread-id"
export type HarnessContextLayerKind = "claude" | "pi" | "codex"
Expand All @@ -29,7 +38,7 @@ export interface HarnessBehavior {
readonly usesRpcFifo: boolean
readonly usesCodexHome: boolean
readonly injectsPromptTimeMemory: boolean
readonly workAgentMode: "claude-code" | "ohmypi-rpc" | "codex-work-tui"
readonly workAgentMode: "claude-code" | "ohmypi-rpc" | "codex-work-tui" | "codex-app-server"
readonly readyTimeoutSeconds: number
}

Expand Down
2 changes: 0 additions & 2 deletions scripts/circular-deps-baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ src/dashboard/server/services/conversation-service.ts > src/dashboard/server/ser
src/dashboard/server/services/conversation-service.ts > src/dashboard/server/services/conversation/activity-summary.ts > src/dashboard/server/services/pi-conversation-parser.ts
src/lib/activity-logger.ts > src/lib/agents.ts > src/lib/agents/agent-state.ts
src/lib/agents.ts > src/lib/agents/activity.ts
src/lib/agents.ts > src/lib/agents/activity.ts > src/lib/runtimes/codex.ts > src/lib/tmux.ts > src/lib/config-yaml.ts > src/lib/config-yaml/defaults.ts > src/lib/config-yaml/roles.ts
src/lib/agents.ts > src/lib/agents/activity.ts > src/lib/runtimes/codex.ts > src/lib/tmux.ts > src/lib/config-yaml.ts > src/lib/config-yaml/defaults.ts > src/lib/config-yaml/roles.ts > src/lib/config-yaml/percent.ts > src/lib/config-yaml/schema.ts
src/lib/agents.ts > src/lib/agents/activity.ts > src/lib/runtimes/ohmypi.ts > src/lib/launcher-generator.ts
src/lib/agents.ts > src/lib/agents/agent-state.ts > src/lib/agents/identity.ts > src/lib/agents/runtime-state.ts
src/lib/agents.ts > src/lib/agents/agent-state.ts > src/lib/agents/identity.ts > src/lib/overdeck/agent-rollback-state.ts
Expand Down
1 change: 1 addition & 0 deletions scripts/lint-circular-deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ current=$(mktemp)
--exclude '\/(__tests__|__mocks__)\/|\.(test|spec)\.(ts|tsx)$|\.d\.ts$' \
src/ 2>/dev/null > "$current_raw" || true
node "$CANON" < "$current_raw" > "$current"
sort -o "$current" "$current"
rm -f "$current_raw"

# Regen mode: rewrite the baseline from the current cycle list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ export function usePendingInputDialogs({ agents, issues }: UsePendingInputDialog
});

const codexApprovalMutation = useMutation({
mutationFn: async ({ id, optionNumber }: {
mutationFn: async ({ id, optionNumber, toolUseId }: {
id: string;
optionNumber: number;
label?: string;
Expand All @@ -299,7 +299,7 @@ export function usePendingInputDialogs({ agents, issues }: UsePendingInputDialog
const res = await fetch(`/api/conversations/${encodeURIComponent(id)}/codex-approval`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ optionNumber }),
body: JSON.stringify({ optionNumber, toolUseId }),
});
if (!res.ok) {
let message = `Failed to send approval (${res.status})`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,7 @@ describe('spawnConversationSession PTY supervisor wiring', () => {
expect(dismissDevChannelsDialogMock).not.toHaveBeenCalled();
});

it('wraps Codex TUI conversations with the PTY supervisor and waits for its socket', async () => {
createSupervisorSocket = true;
it('launches Codex app-server conversations without the PTY supervisor', async () => {
const { spawnConversationSession } = await import('../../../../lib/overdeck/conversation-runtime.js');

await spawnConversationSession(
Expand All @@ -162,13 +161,14 @@ describe('spawnConversationSession PTY supervisor wiring', () => {
expect(launcher).toContain("export OVERDECK_AGENT_ID='conv-codex-supervisor-test'");
expect(launcher).toContain(`export CODEX_HOME='${join(overdeckHome, 'agents', 'conv-codex-supervisor-test', 'codex-home')}'`);
expect(launcher).toContain("node '");
expect(launcher).toContain("/dist/pty-supervisor.js' codex");
expect(existsSync(join(overdeckHome, 'agents', 'conv-codex-supervisor-test', 'pty-token'))).toBe(true);
expect((statSync(join(overdeckHome, 'sockets', 'pty-conv-codex-supervisor-test.sock')).mode & 0o777)).toBe(0o600);
expect(launcher).toContain("/dist/codex-app-server-host.js'");
expect(launcher).not.toContain('pty-supervisor.js');
expect(existsSync(join(overdeckHome, 'agents', 'conv-codex-supervisor-test', 'pty-token'))).toBe(false);
expect(existsSync(join(overdeckHome, 'sockets', 'pty-conv-codex-supervisor-test.sock'))).toBe(false);
expect(dismissDevChannelsDialogMock).not.toHaveBeenCalled();
});

it('resumes Codex TUI conversations with the persisted thread id', async () => {
it('resumes Codex app-server conversations with the persisted thread id', async () => {
createSupervisorSocket = true;
const session = 'conv-codex-resume-supervisor-test';
const threadId = '019eaaec-4dfa-7ab1-90ba-9104d16534d1';
Expand All @@ -191,7 +191,8 @@ describe('spawnConversationSession PTY supervisor wiring', () => {
);

const launcher = launcherFor(session);
expect(launcher).toContain(`/dist/pty-supervisor.js' codex resume -c project_doc_max_bytes=0 '${threadId}'`);
expect(launcher).toContain(`/dist/codex-app-server-host.js' --model 'gpt-5.5' --resume '${threadId}'`);
expect(launcher).not.toContain('pty-supervisor.js');
expect(launcher).not.toContain('codex exec resume');
});

Expand Down
2 changes: 1 addition & 1 deletion src/dashboard/server/services/codex-conversation-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* OpenAI Codex → chat panel adapter (PAN-1520).
*
* Codex conversations write a "rollout" JSONL under the per-agent
* $CODEX_HOME/sessions/YYYY/MM/DD/rollout-<uuid>-<threadId>.jsonl. The schema
* $CODEX_HOME/sessions/YYYY/MM/DD/rollout-<timestamp>-<threadId>.jsonl. The schema
* is OpenAI's, not Claude Code's, so the Claude parser (and even the Pi parser,
* which the path also happens to match) find zero message entries and the chat
* panel renders blank. This module is the adapter — it reads a Codex rollout
Expand Down
Loading