Solutions

Below the permission list, where it can't be stepped around.

A deny rule sitting next to a wildcard interpreter grant stops an honest agent, not a redirected one. The guard reads the command string whichever interpreter would have run it, and that placement is the entire point.

It inspects what a call does

The command, the paths in the payload, and executable content — not the bytes a document happens to carry.

It asks before it refuses

Block, allow once, or allow for a set time. A block that only ever says no gets worked around.

Every block names its caller

The tool, the session id, the working directory, and the process tree above the hook — which program is actually running.

What it blocks

Pattern
Originalsthe screen-capture store, Messages, Mail, SSH keys, Keychains, the Photos library, executed documents
Destructive verbsrecursive remove, DELETE FROM, DROP TABLE, --force, the privacy-reset command

Three surfaces, and one deliberate omission

SurfaceDrawn fromChecked against
Commandthe command, cmd, script or code field of the calloriginals and destructive verbs
Pathsfile_path, path, notebook_path, pattern, glob, any *_path or *_fileoriginals
Executable contentcontent or new_string, only when the target looks executable — a script extension, a path under bin/ or hooks/, or no extension at alloriginals and destructive verbs

File contents are otherwise not inspected. A document that names a path is not an access to it, and matching prose bought nothing: text about a destructive verb deletes nothing, and a script that carries one is refused twice over — when it is written to an executable path, and again when it is run.

Unrecognised payloads degrade safely

If the JSON parses but no field is recognisable, every byte is still checked for a path to an original — one appearing in any argument means something is being pointed at one — while destructive verbs are only checked when the tool's name suggests it executes things. That way a search query or a message body can say the words. If the payload is not JSON at all, the whole thing is checked against both patterns.

Answerable, so it survives contact with work

A hook has no terminal and no window, so it cannot ask anything itself. It writes a request to disk, polls for a verdict, and the app raises the prompt. The request id is a fingerprint of the layer plus the inspected surface: that call, not that pattern.

Allow for N minutes writes an override the guard consults before blocking, so an identical call goes through until it expires. Overrides are listed in Settings with a Revoke button, and they expire on their own.

Pause offers fifteen minutes, an hour, four hours, or until you resume it. The guard re-reads its state on every call, so a pause takes effect now rather than at the next restart — and it keeps logging throughout, so you can see what went through while it was off. A banner runs across every screen and the menu bar icon goes hollow.

Fourteen cases, re-run on every scan

The behaviour matrix is tested against a sandboxed home directory, and the interesting rows are the allowances: prose naming an original and a destructive verb is allowed; a search query mentioning a destructive verb is allowed; the same verb inside a shell script is blocked. Four of those rows run again as rules on every scan.