Repository navigation
feat(hooks): attach non-blocking hook failures to the tool result - #245
Merged
Merged
Conversation
A PreToolUse hook that exits 1, times out, or crashes after it starts lets the tool run. createHookRunner attaches one copy of that notice to the tool result for the session. A hook that never starts still denies the call. Co-authored-by: Lionel <[email protected]>
An object result becomes a json string with the notice suffix. An image result keeps its file part and gains a text part. Co-authored-by: Lionel <[email protected]>
Cap the stderr excerpt, not the whole `script exited N:` line, so the model still sees which hook failed. Attach leftover PreToolUse notices when a later hook or the permission gate denies the call. Co-authored-by: Lionel <[email protected]>
Bun 1.4.0 segfaults after a long single-process `bun test` on Windows even when every case passed. Ubuntu already splits by directory for that crash; run the same steps on Windows. Co-authored-by: Lionel <[email protected]>
Providers JSON.stringify json outputs, so a string suffix on an object result double-encoded write_file and bash. A json row with notices is now text whose prefix matches the clean path. recordToolResult is the only tool-phase push. Directories fail closed before spawn. Co-authored-by: Lionel <[email protected]>
Ubuntu and Windows passed on 7e49995. macOS failed transcriptSelection.test.tsx with shown="" for entry 17, which this branch does not touch. Co-authored-by: Lionel <[email protected]>
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.
Fixes #241
What and why
A PreToolUse hook that exits 1 currently deny-blocks the call, and a PostToolUse failure is a TUI
errorevent only. The model never sees the failure, so it cannot fix a broken hook and hits the same silent miss on every call.Non-blocking hook failures (non-zero other than exit 2, timeout, crash after start) now attach a short, session-deduplicated notice to that tool call's result: hook name, exit code, and a stderr excerpt capped at
HOOK_REASON_MAX_CHARS(300, tail). Identical repeats collapse. Spawn and a missing or non-regular script file still deny (unrunnable), which is #173, not this issue.How
HookOutcomesplitsfailed(hook started, non-blocking) fromunrunnable(never started, still deny).createHookRunnerreports failed notices without blocking, collapses identical messages for the session, and still fail-closes spawn. A directory or unreadable path is unrunnable before spawn (bash would exec a directory and exit 126).runLooprecords every tool-phase row throughrecordToolResult. Ajsonoutput with notices becomestextwhose prefix isJSON.stringify(value)(the same bytes the clean path puts on the wire) plus aHook notices:block. Leftover PreToolUse notices also ride along when a later hook or the permission gate denies the call.Rejected: wrapping the result in a new output type, a missing-script stderr heuristic (#236), and treating bash exit 127 as "never ran".
Verification
bun run typecheckbun test apps/cli/tests/hooks/run.test.ts apps/cli/tests/hooks/gate.test.ts apps/cli/tests/loop/loop.tools.test.ts apps/cli/tests/loop/parallelReadEvents.test.ts apps/cli/tests/loop/loop.pathDenial.test.ts apps/cli/tests/imageParts.test.ts(148 pass, 5 skip)bun run build, and ran the resulting binary (seri 0.1.3)Negative control: PreToolUse exit 1 used to set
block; the new test expects the tool to run and the tool-result to contain the stderr once, then collapse the repeat. A later hook block or aread-onlygate deny still carries the leftover notice.Cross-platform impact
Linux live-bash hook tests pass. PowerShell live tests are
describe.skiphere. CI covers Windows.Windows
buildon an earlier SHA died after every test passed: Bun 1.4.0 segfault at process teardown. That is the same class Ubuntu already splits the suite for. This branch runs that split on Windows too.Blast Radius
Hook runner plus the loop's tool-result rows. Path denials still skip PreToolUse. Containment denials are unchanged. Subagents share the same runner, so collapse is drive-wide: a child never sees a notice the parent already delivered, and a compacted row does not re-arm it. Parallel reads still attach PreToolUse notices without painting a false in-flight throw. A dropped image next to a notice text part still keeps the "dropped image" placeholder.
Notes for the reviewer