2dfb795cb7
When the CLI approval callback raises, when no callback is registered on the thread while prompt_toolkit owns the terminal, or when the input() read is interrupted, prompt_dangerous_approval returned "deny" and the command gate rendered "BLOCKED: User denied this command" — attributing a refusal to a user who was never asked (#22992). #112308 fixed the gateway half of the class (withdrawn prompts -> outcome "cancelled" with a cause); this closes the CLI residual on the same shape. - tools/approval_prompt.py: those three paths return an Unanswered("cancelled") sentinel carrying the cause; MCP elicitation consent maps it to "cancel". - tools/approval.py: the CLI gate renders "BLOCKED: <noun> was not approved: the approval prompt could not be delivered or was not answered (<cause>)" with outcome "cancelled" — still fail-closed, "Silence is not consent". - tools/file_tools_write_guards.py: the protected-instruction write gate reports the undelivered prompt instead of "was denied by the user". - Shared metrics: "cancelled" is a counted approval outcome (contract + v2 schema) instead of falling into "unknown". - Docs: hook `choice="cancelled"` now covers the CLI causes. Fixes #22992