Skip to content

feat: add basic Codex hook provider (presence + turn-end + Bash events)#335

Open
arc-web wants to merge 1 commit into
pixel-agents-hq:mainfrom
arc-web:feature/codex-hook-provider
Open

feat: add basic Codex hook provider (presence + turn-end + Bash events)#335
arc-web wants to merge 1 commit into
pixel-agents-hq:mainfrom
arc-web:feature/codex-hook-provider

Conversation

@arc-web

@arc-web arc-web commented Jul 10, 2026

Copy link
Copy Markdown

Summary

Adds a minimal HookProvider for OpenAI Codex CLI, following the single-subdirectory pattern documented in CLAUDE.md (server/src/providers/hook/codex/).

Codex's hooks system is experimental (feature-flagged off by default) and only fires 5 events today: SessionStart, PreToolUse/PostToolUse (Bash only), UserPromptSubmit, Stop. The payload shares hook_event_name/session_id/cwd field names with Claude Code's, so this is a straightforward normalization boundary, not a new event taxonomy.

Given Codex's current coverage, scope is intentionally small: no subagent events, no file-write hooks, no heuristic/transcript-polling fallback (Codex exposes no transcript file pixel-agents could poll). A Codex session shows up as an office character that goes active on Bash calls and idle on Stop — presence + turn-end, not full Claude-level tool-by-tool detail.

What changed

  • server/src/providers/hook/codex/ — provider (codex.ts), installer (codexHookInstaller.ts), hook script (hooks/codex-hook.ts). Mirrors the Claude provider's structure.
  • Installer writes to ~/.codex/hooks.json additively (merges into existing hook entries, doesn't overwrite) and flips the codex_hooks feature flag in config.toml via a targeted single-line edit — no new TOML dependency, deliberately limited to the common single top-level [features] table case.
  • Runtime change (the one non-additive part): HookEventHandler previously hardwired a single HookProvider per instance — the providerId param on handleEvent was received but unused (_providerId). Added an optional secondaryProviders: HookProvider[] list so AgentRuntime/HookEventHandler can normalize events from a second provider without touching the primary provider's team/tool-display logic (which Codex doesn't use). cli.ts passes [codexProvider] only when ~/.codex exists, so machines without Codex never get a ~/.codex/ directory created.
  • esbuild.js: bundles the new hook script to dist/hooks/codex-hook.js alongside the existing Claude one (switched from a single multi-entry outdir build, which nested output under claude/hooks//codex/hooks/, to per-entry outfile builds so both stay flat).
  • server/__tests__/codex.test.ts — unit coverage for normalizeHookEvent (all 5 events + unknown/malformed input) and formatToolStatus reuse.

Testing

  • Full server suite: 227/227 passing (213 existing + 14 new), no regressions.
  • npm run build clean (typecheck, lint, asyncapi drift check, e2e inventory drift check all pass).
  • Manual end-to-end smoke test against a running standalone server: POSTed a SessionStart then Stop event through the built codex-hook.js script and confirmed a live agent was created in the running instance's state.
  • Ran the installer against my own real ~/.codex/hooks.json, which already had several unrelated hooks configured (a Bash-deny guard, a Gmail notifier, a response-quality gate, some supacode-managed entries) — confirmed it merged in additively and left every existing entry untouched.

Known gap (pre-existing, not introduced here)

While testing, I found the standalone CLI's copyHookScript(distRoot) call passes dist/ as extensionPath, but copyHookScript joins extensionPath + 'dist' + 'hooks', producing a dist/dist/hooks/... miss on first run ("Hook script not found at .../dist/dist/hooks/claude-hook.js"). This affects the existing Claude provider too, not just Codex — happy to send a separate small fix if useful, kept out of this PR to stay scoped to Codex support.

Not attempted

Codex offers no equivalent to Claude's Agent Teams, subagent lifecycle, or file-write hooks today, so no TeamProvider, no getSessionDirs/heuristic fallback, and no subagent visualization for Codex sessions. Happy to extend if/when Codex's hooks API grows that coverage.

Codex's experimental hooks system (SessionStart, PreToolUse/PostToolUse
restricted to Bash, UserPromptSubmit, Stop) shares field names with
Claude Code's, so this adds a minimal HookProvider following the pattern
documented in CLAUDE.md: a self-contained subdirectory under
server/src/providers/hook/codex/ (provider, installer, hook script).

Scope is intentionally small given Codex's current hook coverage: no
subagent events, no file-write hooks, no heuristic/transcript fallback
(Codex exposes none pixel-agents could poll). Codex sessions show up as
office characters that go active on Bash calls and idle on Stop -- not
full parity with Claude, just presence + turn-end pulses.

Runtime change: HookEventHandler previously hardwired a single provider
per instance (the `providerId` param was received but unused). Added an
optional `secondaryProviders` list so AgentRuntime can normalize events
from a second provider (Codex) without disturbing the primary Claude
provider's team/tool-display logic, which secondary providers don't use.

cli.ts wires codexProvider only when ~/.codex exists, so machines
without Codex never get a ~/.codex/ directory created. The installer is
additive: it merges into any existing ~/.codex/hooks.json entries rather
than overwriting, and only flips config.toml's `codex_hooks` feature
flag via a targeted single-line edit (no TOML parser dependency).

Verified: full server test suite passes (227/227, 14 new), build clean,
and a manual hook POST end-to-end created a live agent in a running
standalone server instance.

Known pre-existing gap (not introduced here, also affects the Claude
hook script): the standalone CLI's copyHookScript(distRoot) call passes
dist/ as extensionPath, but copyHookScript joins extensionPath + 'dist'
+ 'hooks', producing a dist/dist/hooks/... miss. Both providers log
"hook script not found" on first standalone-CLI start until something
copies the file into place. Flagging for a follow-up fix.
@arc-web arc-web changed the title Add basic Codex hook provider (presence + turn-end + Bash tool events) feat: add basic Codex hook provider (presence + turn-end + Bash events) Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant