Expand description
Codex CLI: native command hooks in ~/.codex/hooks.json ($CODEX_HOME), each running
<borg> hook codex <event>. The file has Claude Code’s shape, and so do the events and the answers
Codex reads back (hookSpecificOutput.additionalContext, {"decision":"block","reason"}), except
the edit tool: apply_patch sends a patch, which Codex::normalise turns into a canonical Edit.
Codex runs a new or changed hook only once the engineer trusted it (/hooks in codex), and skips an
untrusted one without a word: borg install says so, and borg doctor asks Codex itself which of
borg’s hooks are not trusted (codex app-server, hooks/list). borg never writes that trust.
Structs§
Constants§
- CONTEXT_
CHARS - Protocol (Codex’s default
additionalContextLimitof a hook): anadditionalContextup to this many characters reaches the model whole as a developer message; a longer one is cut to its head and tail with a “truncated output” warning, the whole text saved underhook_outputs. Measured on Codex 0.153.2 against a local mock of the Responses API (2026-10-28): 10,000 characters arrived whole, 10,010 cut. - EVENTS
- Codex hook events and the canonical borg events they become. Codex has no failure event (a failed
tool call fires no PostToolUse at all), so denials are not captured.
Interrupt(an Esc) ends the turn of a session that listens for wakes (listen.rs), which a turn with no stop and a tool call that never ended would hold off for at leastlisten::TOOL_MS. - RETIRED
- Hooks an older borg installed and this one does not.
- TIMEOUTS
- Guards (protocol once trusted): seconds Codex gives each hook, written explicitly (its own default is 600 s, and 1 s for
SessionEnd, which it clamps to 3 s), so a hung borg holds Codex’s turn no longer than this at each
event. The stop’s (
STOP_TIMEOUT_S) is also its budget: the stop’s model call and the expert drone keep inside it. The trust Codex keeps covers the timeout too: changing one here makes every engineer trust borg’s hooks again.
Functions§
- patch_
edit - One
apply_patchas the canonical edit: the first file the patch names (*** Add|Update|Delete File:) asfile_pathwith its added lines asnew_string, each further file ineditsthe same way. A rename (*** Move to:after an Update) names its destination as a further file, which gets the hunk’s added lines. Headers are matched on the trimmed line, as Codex does. Relative paths are taken fromcwd. None for a patch naming no file. - rollout_
of - Thread
id’s rollout undersessions: a file named after it whosesession_metanames it. - sessions
- Where Codex keeps every thread’s rollout,
<sessions>/YYYY/MM/DD/rollout-<time>-<thread>.jsonl:$CODEX_HOME/sessions, elsehome/.codex/sessions. - spawn_
runs - One run per session whose agent spawned others (
spawn_agent), oldest first: ofsessiononly when given, from sessions that started at or aftersince. Every thread keeps its own rollout (<sessions>/YYYY/MM/DD/rollout-*.jsonl), whose first line,session_meta, names a spawned thread’s parent (source.subagent.thread_spawn.parent_thread_id, with itsagent_path, nickname and role; checked against the serde names in codex 0.153.2). An agent spawned by a spawned agent belongs to its root session’s run, which isRun::loose. Codex has no structured output, so findings come from an agent’s text. This reader is built from codex’s serde names and hand-made rollouts, not checked against a real spawned one yet. - trust_
findings borg doctor’s findings from ahooks/listanswer: borg’s hooks (hooksrecognises their commands) that Codex would skip (untrusted, changed since trusted, disabled), and Codex’s load warnings.