Skip to content

v0.5.0 - Codex adapter

Choose a tag to compare

@AGGIB AGGIB released this 05 Sep 16:38
· 97 commits to main since this release

Stroq 0.5.0 adds a third agent: OpenAI Codex CLI.

Codex support

  • stroq init --agent codex writes .codex/hooks.json (or ~/.codex/hooks.json with --user; --dry-run previews) and registers stroq hook codex on PreToolUse (Bash|exec_command|shell|local_shell|apply_patch|ApplyPatch|mcp__.*) and PostToolUse (Bash|exec_command|shell|local_shell|mcp__.*). The engine, rules, policy, provenance, secret-egress guard and hash-chained audit are shared with the Claude Code and Cursor adapters unchanged.
  • Codex has no ask. Every policy ask (destructive commands, external pushes, unknown-package npx from tool output) is rendered as a deny whose reason begins Stroq would ask before this action (<rule>): … and names ~/.stroq/policy.yaml as the place to relax it. The audit keeps the real ask.
  • Fail-closed the only way Codex honours it. An internal error, unparsable stdin or a failed stdin read on a high-impact PreToolUse exits with code 2 and the reason on stderr, which Codex treats as a block without parsing stdout. Codex has no failClosed knob, so if the hook command itself cannot start Codex continues — install globally (npm install -g @stroq/cli).
  • apply_patch is classified from its patch body. Paths come from the *** Add/Update/Delete File: and *** Move to: header lines; one policy decision per path, the most severe wins, every path is audited; a patch declaring more than 64 files is denied (codex-patch-too-large) because classifying more would run past Codex's hook timeout, which fails open.
  • Defensive about a wire format that is inferred, not recorded. The command is read from command/cmd/input/script/raw (string, argv or a one-level nested object; [bash, -c, script] classifies the script alone, other argv is POSIX-quoted before joining), every command field found is judged on its worst decision, and patch text is read from those plus patch/arguments. A high-impact call whose tool_input was non-empty but yielded no command or path is denied as codex-unreadable-input, with a reason that names only the keys it saw.
  • Self-protection. .codex/hooks.json and .codex/config.toml are now config.self for every agent, so a tainted session cannot rewrite Codex's hook file or the file that can disable hooks.
  • Installer. Always writes the official nested { "hooks": { … } } shape, migrates root-level events from a community-style flat file without dropping anything, preserves foreign groups, events and unknown keys, ignores a hooks value that is not an object, and re-runs idempotently.
  • stroq doctor gains a codex hooks line; a runnable demo (examples/demo/run-codex-demo.sh) asserts every decision and runs in CI.
  • Documented limits: ask is lossy; runtime fail-open if Node cannot start; hosted tools (WebSearch) never reach hooks; project-local hooks need the .codex/ layer trusted (or --user); older releases need [features] hooks = true; PermissionRequest, updatedInput, session/compaction events, inline [hooks] TOML installation and Codex-shaped stroq attack scenarios are out of scope; Windows is untested. Recording real Codex payloads as fixtures is the next step.

Also

  • README: the downloads badge shows the all-time total (npm's monthly point endpoint lags about a week). SECURITY.md supported versions: 0.5.x.

Verification

  • 875 tests across 62 files (up from 744 / 56), coverage ≈ 97 / 89 / 100 / 99 (statements / branches / functions / lines); CI on Node 22 and 24 runs all three demos and the attack suite (12 scenarios: 8 blocked, 4 asked, 0 passed through).
  • The adapter went through per-task reviews, a whole-branch security review and two fix rounds. The reviews found and closed: a patch-truncation bypass that hid a .codex/hooks.json header past 200 KB; Bash commands under keys other than command and apply_patch bodies sent as arrays or nested objects reaching the engine empty and being allowed; a stdin-failure exit code Codex would have treated as fail-open; argv joining that turned git commit -m 'rm -rf /' into a destructive command; and a hooks.json shape ambiguity that could make doctor report a hook Codex never loads. The final re-review probed 38 payload shapes and 6 tool-name spellings against the real engine.

Install: npm install -g @stroq/cli then stroq init (Claude Code), stroq init --agent cursor or stroq init --agent codex; or npx @stroq/cli init.