
What happened
Testing Pi 0.84.2 with claude-sonnet-4-5, a hook returning { block: true, reason } on rm produced a normal tool error, not a crash, and rewrites to event.input.command took effect — but asking it to avoid rm led to unlink foo.tmp && unlink bar.tmp, which deleted the files.
Why it matters
A hook refusal is graceful rather than a hard stop, yet regex-based guards shift the burden of real safety onto the extension author, since Pi's framework does not guarantee semantic safety.
Summaries like this, in your inbox every morning.
The test began from a narrower question than safety in general: what actually reaches the model when a hook refuses a call. Pi's documentation covered the mechanics — event.input can be rewritten in place with no re-validation, and refusals return { block: true, reason?, terminate? } — but said nothing about the result the model sees. Running a minimal extension that rejects anything matching /\brm\b/ and prefixes other bash commands with an echo marker, the author watched the session log confirm that the marker really appeared in the output, and that the rejection arrived as an ordinary error result carrying the reason string. The model then re-checked the directory with ls -la, saw the tmp files still present, and asked the user whether to continue or clean up instead of retrying endlessly.
That orderly behavior changed once the instruction was strengthened. Told that the files had to be deleted even without rm, the model avoided the blocked pattern and used unlink, which passed straight through the regex, and the author confirmed only keep.txt remained. The model also listed find -delete, perl's unlink and a python os.remove one-liner as alternatives. The author's conclusion is that being able to refuse a call and being safe are different things, and that because Pi runs extensions in-process and exposes the argument contents, stronger filters are possible — but they remain the extension author's responsibility rather than a framework guarantee.
The author also notes that a prior Japanese-language write-up already showed the refusal-and-continue core using a mock provider, whereas this run went through the real Anthropic API and additionally covered command rewriting and instruction-based bypass. The terminate: true option, parallel tool calls and a measured comparison with OpenCode's out-of-process model were left for later.
Pick your industry and the AI tools you use, and get news related to your work every day.
Free · 30 seconds with Google · unsubscribe anytimeWhat is AIToday? →
Ask AI anything about this article. The AI reads this article, earlier AIToday articles, and Wikipedia, and cites its sources. Q&As are published on this page for other readers too.
Google released TorchTPU, a version of the PyTorch framework that lets developers run existing AI apps and mod…
In a benchmark called E-Commerce Bench, 18 AI models ran up to four simulated shops for 365 days, using 18 too…

ITmedia's AI Plus released a 14-page booklet on using Gemini Notebook's (formerly NotebookLM) Studio feature…

IBM Research's ALTK-Evolve turns each customer-service agent trial into short action rules and injects them in…

A Zenn author migrating a ten-year-old PHP/Laravel monolith — hundreds of endpoints and more than 600 shared m…

After six rounds of tuning TypeSafe AI's Jev for note selection, the author benchmarked it on 100 requests aga…
