small gleam coding and persistent agent daemon with a detachable cli and a canvas ui albedo.sh

a command hook's permissionDecision "ask" is silently an allow #175

Open aly.codes
Open opened by aly.codes

A Claude Code command hook may answer permissionDecision: "ask", which in Claude Code raises a prompt. Albedo's hook seam has no prompt and no verdict for it, so the answer is dropped and the call runs. A policy that says "review this" is silently widened to "allow", which is the wrong direction for the one case a policy writer thought was risky.

Evidence #

priv/python/albedo_hook_command.py (answer/1) maps only two refusals:

if output.get("decision") == "block":                      # -> block
if specific.get("permissionDecision") == "deny":           # -> block

An ask therefore merges as {}. Run through the real helper (python3 priv/python/albedo_hook_command.py with the daemon's request shape and $ALBEDO_HOME/hooks.json running chook, which is a Claude Code hook that answers ask):

$ ... command: "rm -rf /tmp/chook-probe-does-not-exist"
{"block": null, "halt": null, "arguments": null, "context": [], "notes": []}

$ ... command: "sudo -n true"       # chook denies this one
{"block": "sudo is not allowed. Find an alternative that doesn't require elevated privileges.", ...}

and in a real turn, on the deny:

PermissionError: blocked by a hook: sudo is not allowed. Find an alternative that
doesn't require elevated privileges.

while the ask-ed command ran untouched (run("rm", "-rf", ...) returned exit 0).

robot-docs/hooks.md documents exit 2, decision: "block" and permissionDecision: "deny" only, so this may be a deliberate omission -- but then the payload should not invite it either: albedo sends "permission_mode": "default", which reads as "an interactive mode exists".

Why it matters #

The natural pattern for a policy hook is deny / ask / allow, and ask is exactly where a rule author puts the judgement calls. Under albedo those become allows with no trace -- not a note, not an event, nothing in the transcript.

Options #

  • Treat ask as a block whose reason is permissionDecisionReason, since there is nobody to ask. That is what CHOOK_UNATTENDED=1 already makes chook do for its own asks, so the two agree.
  • Or carry it as a distinct verdict, so a client with a prompt (the TUI) could ask and a client without one turns it into a refusal.
  • Or, at minimum, turn it into a note and document that ask is ignored, so a hook author is not quietly loosening their policy.

im really going to ask to please put these in your own words. i realize i myself am not consistent with this but these drive-bys are hard to read and im not a huge fan of being a meat proxy. im not even sure if youre running into these on your own or automating them (which is fine!) but id like to know that too

@okami.mom sincere apologies! it looked like agent-to-agent dev, and there was no clear AI policy, so i rolled with it. i'll write up anything i submit from now on!

and happy to close these if you prefer!

this is a good issue to keep and implement once there is a permission system of some kind

Sign up or Sign in to add to the discussion.