Wires Claude Code to a didbot personal data server: hooks, a plugin, and the harness contract they rest on.

feat(hook): show an agent the sign-in it is asked to approve #82

Merged permadeath.com
targeting main
Merged opened by and assigned to permadeath.com
targeting main

The plugin hook now bumps its wire version to 2, reads pending sign-in decisions from the daemon's answer, and renders each into additionalContext as a plain, factual block — client, what was asked for, what policy allowed, and how to approve or decline it (or, for a denied request, that there is nothing to approve). A deny's policy line prefers the newer reason field when the daemon sends one, formatted refused: <reason> (rule "<rule>"), and falls back to today's refused: <rule> when it does not, so hook and daemon can be upgraded independently. The identity notice is rewritten to describe this flow and drops the deprecated didbot confirm sentence. On PostToolUse the hook also scans tool output for a client's own authorize URL and hands the daemon the decoded request_uri as a fast path ahead of its own poll, sending nothing else from that output.

tools/test_hook.py runs the hook as a real subprocess against a stub unix-socket daemon and covers: a narrow decision (with a token) and a deny decision (without one, in the older rule-only shape) rendering correctly together; a deny carrying both reason and rule rendering the reason-led shape; an answer with no pending field rendering only the identity notice; the fast path sending the decoded request_uri and nothing else from a string or dict tool_response; and an unreachable socket still exiting 0 with no output. prek.toml gained a hook-tests hook so this runs on every commit that touches plugin/bin/hook.py or the test file. plan/collection.md now names the request_uri fast path as the second deliberate exception to "identifiers only".

An end-to-end run against a real daemon and PDS found that the seen_request_uris fast path is load-bearing rather than an optimisation: after a PDS restart, a decision the daemon's own long-poll could no longer see was still rendered because the hook had reported the request_uri it saw in tool output. Left out: the human-facing consent page and the daemon's four socket asks (approve, decline, pending) are piece B, already merged on the daemon side; this PR is the hook-side rendering and fast path only, coded against didbot-agentd's protocol.rs as built.

This pull request has no versions submitted yet.