v0.5.0 - Codex adapter
Stroq 0.5.0 adds a third agent: OpenAI Codex CLI.
Codex support
stroq init --agent codexwrites.codex/hooks.json(or~/.codex/hooks.jsonwith--user;--dry-runpreviews) and registersstroq hook codexonPreToolUse(Bash|exec_command|shell|local_shell|apply_patch|ApplyPatch|mcp__.*) andPostToolUse(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 policyask(destructive commands, external pushes, unknown-packagenpxfrom tool output) is rendered as a deny whose reason beginsStroq would ask before this action (<rule>): …and names~/.stroq/policy.yamlas the place to relax it. The audit keeps the realask. - Fail-closed the only way Codex honours it. An internal error, unparsable stdin or a failed stdin read on a high-impact
PreToolUseexits with code 2 and the reason on stderr, which Codex treats as a block without parsing stdout. Codex has nofailClosedknob, so if the hook command itself cannot start Codex continues — install globally (npm install -g @stroq/cli). apply_patchis 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 pluspatch/arguments. A high-impact call whosetool_inputwas non-empty but yielded no command or path is denied ascodex-unreadable-input, with a reason that names only the keys it saw. - Self-protection.
.codex/hooks.jsonand.codex/config.tomlare nowconfig.selffor 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 ahooksvalue that is not an object, and re-runs idempotently. stroq doctorgains acodex hooksline; a runnable demo (examples/demo/run-codex-demo.sh) asserts every decision and runs in CI.- Documented limits:
askis 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-shapedstroq attackscenarios 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.jsonheader past 200 KB;Bashcommands under keys other thancommandandapply_patchbodies 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 turnedgit commit -m 'rm -rf /'into a destructive command; and ahooks.jsonshape ambiguity that could makedoctorreport 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.