feat: pre-execution critic gate for side-effecting tools
What does this PR do?
Adds a flag-gated (ALTIMATE_CRITIC_GATE, default OFF) pre-execution "critic gate". Before a side-effecting tool (bash/write/edit/sql_execute/dbt_run/patch) runs, a pluggable Verifier checks the proposed args; on a hard verdict the call is skipped and the reason is fed back so the model can fix-and-retry, instead of executing a bad action. No behavior change when the flag is unset.
tool/critic.ts— pure, testable gate + pluggableVerifierinterface.ALLOW_ALL— ungated (opt-out / tests).basicSafetyVerifier— the wired default: a conservative, dependency-free heuristic that blocks catastrophic, unambiguous host-destructive bash (rm -rf /incl. system-path / glob / compound / fully-qualified /${HOME}variants, fork bombs,mkfs/ddon a raw device, recursivechmodof/). Best-effort safety net, not a security boundary — documented as such; defense-in-depth stays with the OS sandbox, the permission system, and a richer verifier a product may inject.gate()is fail-open on verifier error AND on a verifier timeout, so a broken or hung verifier never breaks/hangs the agent.
session/prompt.ts— wired into the native-tool execute wrapper just beforeitem.execute, marker-wrapped, emitstool.execute.afteron a block so observability sees it. MCP tools use a separate wrapper and are intentionally not gated (documented onDEFAULT_GATED).
Split out of #857 / PR #858, where the critic was previously an unwired no-op flag. This PR makes the flag actually do something.
Type of change
- New feature (non-breaking change which adds functionality)
Issue for this PR
Closes #862
How did you verify your code works?
- 100 tests across three files: unit (
critic.test.ts), adversarial bypass probes that document caught-vs-known-limits (critic-adversarial.test.ts), and e2e driving the REALBashToolthrough the gate with real filesystem effects (critic-e2e.test.ts) — no mocked tool calls. All green; 448 tool-suite tests pass;tsgotypecheck clean;altimate_changemarkers inprompt.tsbalanced (37/37); default-off path unchanged. - Adversarial testing found + fixed real bugs: compound commands where the fatal
rmwasn't last (rm -rf / && rm -rf ./safe), and a separator glued to the target (rm -rf /;). - Reviewed by a multi-model panel (Gemini 3.1 Pro, GLM-5, MiniMax M2.7) before opening. Their findings were applied: removed false positives (
rm -rf *,rm -rf ., andrmburied in an argument likegit commit -m "...rm -rf /..."), closed false negatives (glob wipesrm -rf /var/*,/.//.., fully-qualified/bin/rm,${HOME}, long-form/globchmod), and added an input length cap + verifier timeout.
Checklist
- My code follows the style guidelines of this project
- I have performed a self-review of my code
- I have added tests that prove my feature works
- New and existing unit tests pass locally with my changes
Summary by cubic
Adds a flag-gated pre-execution critic for side-effecting tools that blocks clearly dangerous bash before it runs. Default is off; when enabled, blocked calls return feedback so the model can fix-and-retry.
-
New Features
- Controlled by
ALTIMATE_CRITIC_GATE; pluggableVerifierwithALLOW_ALLand defaultbasicSafetyVerifier. - Blocks catastrophic bash (e.g.,
rm -rf /variants, fork bombs,mkfs/ddto devices, recursive chmod/chown of/); fail-open on verifier error or 5s timeout. - Wired into
session/prompt.tsjust before execution; on block emitstool.execute.afterand skips execution. MCP tools are not gated. - Gated tools:
bash,write,edit,sql_execute,apply_patch. Tests: 104 (unit, adversarial, e2e with realBashTool).
- Controlled by
-
Bug Fixes
- Corrected gated tool id to
apply_patch; removed non-existentdbt_run. - Closed bypass: inline env assignments (
FOO=1 rm -rf /,IFS=x ... rm) now resolve command position correctly. - Fixed timer leak by clearing the verifier timeout after resolution; tightened types to
Record<string, unknown>inVerifier.verifyandgate, and updated thepromptwrapper cast (no behavior change).
- Corrected gated tool id to
Summary by CodeRabbit
- New Features
- Optional pre-execution safety gate for side-effecting tools (e.g., bash) that can block dangerous commands, return informative feedback when blocked, and otherwise allow normal execution.
- Tests
- Added comprehensive unit, adversarial, and end-to-end tests covering detection heuristics, gating behavior, timeouts, robustness, and fail-open guarantees.