@biroai/agent-events-codex
v2026.513.0
Published
**P1.3** from `ROADMAP-NEXT.md` — the second runtime adapter for the Biro universal agent event bus.
Readme
@biroai/agent-events-codex
P1.3 from ROADMAP-NEXT.md — the second runtime adapter for the Biro universal agent event bus.
Parses Codex CLI exec --json NDJSON events and emits normalized BiroAgentEvent
objects from @biroai/agent-events. Pure: no I/O, no subprocess management,
no OpenAI SDK runtime dependency — input is an event object or async line stream,
output is validated schema events.
Usage
Per-line (sync)
import { parseCodexStreamLine, translateCodexEvent } from "@biroai/agent-events-codex";
import { randomUUID } from "node:crypto";
const sessionState = { threadId: "unknown-session", model: "unknown", turnNumber: 0 };
const ctx = {
companyId: "co-abc",
agentId: "agent-xyz",
issueId: null,
generateEventId: () => randomUUID(),
inFlightTools: new Map(), // reuse across lines for durationMs tracking
sessionState, // reuse across lines for thread_id and turn counting
};
for (const line of rawLines) {
const raw = parseCodexStreamLine(line);
if (!raw) continue;
const events = translateCodexEvent(raw, ctx);
for (const ev of events) {
console.log(ev.type, ev.eventId);
}
}Async streaming
import { translateCodexStream } from "@biroai/agent-events-codex";
import { createInterface } from "node:readline";
import { randomUUID } from "node:crypto";
async function processStream(readable: NodeJS.ReadableStream) {
const rl = createInterface({ input: readable, crlfDelay: Infinity });
const ctx = {
companyId: "co-abc",
agentId: "agent-xyz",
issueId: null,
generateEventId: () => randomUUID(),
};
// translateCodexStream auto-creates inFlightTools and sessionState
for await (const ev of translateCodexStream(rl, ctx)) {
await persistEvent(ev);
}
}Event type coverage
| Raw Codex event | Item type | Emitted BiroAgentEvent(s) |
|---|---|---|
| thread.started | — | agent.session.started |
| item.started | command_execution | agent.tool.requested |
| item.started | file_change | agent.tool.requested |
| item.started | tool_use | agent.tool.requested |
| item.completed | command_execution | agent.tool.completed (+ agent.tool.requested if no prior item.started) |
| item.completed | file_change | agent.tool.completed (+ agent.tool.requested if no prior item.started) |
| item.completed | tool_use | agent.tool.completed |
| item.completed | tool_result | agent.tool.completed |
| item.completed | agent_message | (stored as pending summary for next turn.completed) |
| turn.completed | — | agent.turn.completed + agent.session.ended |
| turn.failed | — | agent.turn.completed + agent.session.ended (endReason=error) |
| turn.started, error, unknown | — | (empty — dropped silently) |
| item.completed | reasoning, error, unknown | (empty — dropped silently) |
permissionMode mapping
Codex does not surface a permission mode in the event stream. The
--dangerously-bypass-approvals-and-sandbox flag affects runtime behavior
but is not reflected in thread.started. All Codex sessions emit
permissionMode: "unknown".
endReason mapping
| turn.completed subtype | is_error | endReason |
|---|---|---|
| success | false | completion |
| error_max_turns | any | budget_exhausted |
| error_during_execution | any | error |
| error | any | error |
| timeout | any | timeout |
| absent or unknown | true | error |
| absent or unknown | false | other |
Not emitted
agent.subagent.spawned/agent.subagent.completed— Codex has no sub-agent primitive. These events will never be emitted by this translator.agent.compacted— Codex has no in-stream compaction event as of 2026-05.
Wire-format TODOs
The Codex CLI --json flag is experimental. The schema here is inferred from
first-party adapter code in this monorepo (packages/adapters/codex-local/) and
direct observation as of 2026-05.
thread.startedhas nocwdorpermission_modefield. Both are alwaysnull/"unknown"in emitted events.- Reasoning tokens appear as
item.completedwithtype: "reasoning"and are dropped silently. They are not counted separately inoutputTokensbecause the usage totals onturn.completedalready include them. turn.completedvsresultevent: unlike Claude's separateresultevent, Codex usesturn.completedas both turn-end and session-end. This translator emits bothagent.turn.completedandagent.session.endedfrom a singleturn.completedline.- Cache write tokens: Codex does not emit
cache_creation_input_tokens.cacheWriteTokensis always0. - Field names and subtypes should be re-verified on each Codex CLI major version bump.
Schema pointer
Schema types and Zod validators: @biroai/agent-events
