feat(headless): add a Codex runtime alongside Claude#74
Merged
Conversation
The auto-compact guard queued the 500k/600k/700k self-compact nudges and relied on auto-send when the agent next emits a Stop. A freshly-resumed or idle session never emits a Stop, so those gentle nudges never fired and context coasted up to the 750k hard /compact — which then collided with whatever prompt landed next (e.g. the morning nudge), losing it. Paste the nudge straight into the session instead. Claude queues pasted input and runs it after the current turn, so a busy agent still finishes gracefully, while a resumed/idle session self-compacts immediately and is well below the limit by the time its next prompt arrives.
The headless engine was Claude-only. Add a runtime abstraction so an agent can declare runtime: codex and be driven by the Codex CLI: - runtime.ts: descriptor table (bin, arg building, canResume, selfCompact) mirroring the macOS CodingAssistant entity, scoped to the headless path - config: optional runtime field (defaults to claude), validated - identity/launch/reconcile: thread runtime through; build the codex command (--no-alt-screen + full-auto bypass flags), always launch fresh (Codex mints its own session id and the reviewer is per-PR), tag the card assistant=codex - launcher exports KANBAN_SESSION_ID so the shared hook.sh correlates events to the agent's card regardless of the id the runtime mints internally - hooks: install Codex hooks (~/.codex/hooks.json) pointing at the same hook.sh; self-compact is naturally skipped for codex (no statusline context json) - tests + headless-runtime spec scenario
Codex's hooks.json nests events under a "hooks" key (mirroring the config.toml [hooks] table); without the wrapper Codex silently ignores the file, so no codex hook events fired. Caught dogfooding on the box (empty Slack channel).
…ript Codex agents now stream to Slack like Claude agents: the bridge discovers the agent's Codex rollout by workspace cwd (findCodexRollout) and posts its agent_message + exec_command events (formatCodexRolloutLines). Inbound steering already worked via tmux paste. Also stop auto-installing Codex hooks: codex 0.134.0 gates command hooks behind an interactive trust prompt that --dangerously-bypass-hook-trust does not suppress in the inline TUI, which hung the headless session on a modal. The rollout-transcript mirror replaces the hook-based announce for Codex.
…huge) Codex embeds full base_instructions in the session_meta first line (~20KB), so the bounded read truncated it and JSON.parse failed, so findCodexRollout never matched and no agent movement was mirrored. Extract cwd with a regex from a larger bounded read instead.
…age' Codex has no UserPromptSubmit hook (trust gate), so injected prompts were not announced to the channel the way Claude's are. Mirror user_message events from the rollout tail using the same received-message format, and guard against echoing a prompt that was relayed from a Slack human.
…arts/compaction Codex writes a new rollout file per session, so a relaunched agent (or its own auto-compaction) rotated the file out from under the bridge tail. The poll loop now switches to the newest rollout for the agent's cwd and mirrors it from the start, so the channel keeps showing the agent's conversation without restarting the bridge.
The 500k/600k/700k soft nudges stay queued and auto-send on the next Stop, as designed: a soft nudge is meant to land at a stop point, and an idle session simply rides up to the 750k hard /compact, after which the next prompt arrives post-compaction. Reverts the daemon paste-straight-away change so the queuePrompt path is preserved.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Lets a headless agent be driven by the Codex CLI instead of Claude Code, by declaring
runtime: codexin the agents config. Built for a Codex PR-reviewer agent on the dev agents box.Stacked on #73 (self-compact-straightaway, the branch currently deployed on the box); once #73 merges this diff is just the Codex commit.
How
canResume,selfCompact, config dir) mirroring the macOSCodingAssistantentity, scoped to what the headless path needs. Single branch point instead of Claude assumptions scattered around.runtimefield (defaults toclaude), validated.codex --no-alt-screen --dangerously-bypass-approvals-and-sandbox --dangerously-bypass-hook-trust [-m model], always fresh (Codex mints its own session id and a per-PR reviewer needs no cross-reboot resume); the card is taggedassistant: codex.KANBAN_SESSION_ID(the stable uuidv5 of the slug) into the tmux session; the sharedhook.shprefers it, so the daemon/bridge correlate Codex events to the agent's card regardless of the id Codex mints.installCodexHookswrites~/.codex/hooks.jsonpointing at the samehook.sh;installHooksinstalls both. Self-compaction is naturally skipped for Codex (no statusline context json), which is correct since Codex auto-compacts.sendand Slack-inbound steering are unchanged — they paste into the tmux session by slug, which is runtime-neutral.Tests
204 pass (7 new): runtime descriptor arg building, config parsing/validation of
runtime,installCodexHooksidempotency, and a real-tmux test that a codex agent launches fresh (no resume) and tags its card. Plus a headless-runtime spec scenario.Follow-up (not in this PR)
Live transcript mirroring of Codex output to Slack would need a Codex rollout (
~/.codex/sessions/*.jsonl) parser; for now Slack inbound steering + the daemon's receipt announce work, and the reviewer posts its findings to the PR and Slack itself.