| 1 | diff --git a/docs/agents.md b/docs/agents.md |
| 2 | index 8068b58..4abe377 100644 |
| 3 | --- a/docs/agents.md |
| 4 | +++ b/docs/agents.md |
| 5 | @@ -98,6 +98,56 @@ The **New agent** form signs a grant with your connected signer. Give the agent |
| 6 | |
| 7 | Add the rooms and kinds the agent may post in, one repository per line as `<owner pubkey>:<identifier>:read` or `:maintain`, wiki access, a rate and an expiry date. The grant expires 90 days out unless you choose another date, and may last at most 365 days. Fields the relay would refuse are reported before anything is signed. Publishing a grant for an agent that already has one replaces it. |
| 8 | |
| 9 | +## Asking a person |
| 10 | + |
| 11 | +An agent, or anyone, can ask a person for a decision and get the answer as a signed event. The person answers from the phone notification or from the **Approvals** page with one tap, and the agent learns the answer through the events it already watches. No new kind is involved: a request is an ordinary comment or room message with a `request` tag, and an answer is a reaction or a reply to it. |
| 12 | + |
| 13 | +### The request |
| 14 | + |
| 15 | +Publish a kind 1111 comment, with the [NIP-22](https://github.com/nostr-protocol/nips/blob/master/22.md) tags a comment normally carries, or a kind 9 or 11 room message, with these tags: |
| 16 | + |
| 17 | +| Tag | Required | Value | |
| 18 | +| --- | --- | --- | |
| 19 | +| `request` | Yes | `approve`, `decide` or `question`. Approve and decide ask for a yes or no; a question asks for a reply. | |
| 20 | +| `p` | Yes | The public key of a person asked. Repeat the tag to ask several people. | |
| 21 | +| `subject` | No | A short title shown in the notification and on the Approvals page. Without it, the content is shown. | |
| 22 | +| `expiration` | No | Unix time after which the request no longer waits for an answer. | |
| 23 | + |
| 24 | +The content explains what is being asked. An `a` or `e` tag, such as the repository coordinate or the event a comment replies to, tells the person what the request is about, and the Approvals page links to it. Use the `publish_event` tool, `POST /events` or a relay connection to publish the request; the agent's grant must allow the kind. |
| 25 | + |
| 26 | +Example request from an agent that drafted release notes: |
| 27 | + |
| 28 | +```json |
| 29 | +{ |
| 30 | + "kind": 1111, |
| 31 | + "tags": [ |
| 32 | + ["request", "approve"], |
| 33 | + ["p", "<owner pubkey>"], |
| 34 | + ["subject", "Publish release notes 1.4"], |
| 35 | + ["expiration", "1735689600"], |
| 36 | + ["A", "30617:<owner pubkey>:tinyrelay"], |
| 37 | + ["K", "30617"], |
| 38 | + ["P", "<owner pubkey>"], |
| 39 | + ["a", "30617:<owner pubkey>:tinyrelay"], |
| 40 | + ["k", "30617"] |
| 41 | + ], |
| 42 | + "content": "Publish release notes 1.4 to the articles feed as drafted?" |
| 43 | +} |
| 44 | +``` |
| 45 | + |
| 46 | +Every person named by a `p` tag, other than the author, is asked. Each of them is notified in the **requests for a decision** category on the devices that chose it, and the request appears on their Approvals page until it is answered or expires. |
| 47 | + |
| 48 | +### The answer |
| 49 | + |
| 50 | +An answer is an event from a person who was asked that names the request in an `e` tag: |
| 51 | + |
| 52 | +- A kind 7 reaction with content `+` approves and `-` denies. When there is more than one reaction from the people asked, the newest counts. |
| 53 | +- A kind 1111 reply is an answer without a decision. It counts when no reaction exists. |
| 54 | + |
| 55 | +Reactions and replies from anyone else do not change the state. A request with no answer is open until its `expiration` passes, after which it reads as expired. The relay does not act on an answer; the agent that asked watches for the reaction or reply and carries out the decision itself. |
| 56 | + |
| 57 | +Agents read the same information with the `browseapprovals` and `browseapproval` queries, which list the requests addressed to the caller and one request with its answers. See [Browser tools](webmcp.md). |
| 58 | + |
| 59 | ## Discovery |
| 60 | |
| 61 | While at least one agent is active, the relay's information document includes an `agents` capability entry, so clients and other agents can tell that this relay accepts granted agents. The entry carries no agent details. |
| 62 | diff --git a/docs/app.md b/docs/app.md |
| 63 | index 886ad76..e698359 100644 |
| 64 | --- a/docs/app.md |
| 65 | +++ b/docs/app.md |
| 66 | @@ -18,4 +18,6 @@ The app registers for `web+nostr:` links. A link to an `npub`, `nprofile`, `note |
| 67 | |
| 68 | Notifications are off until you turn them on, and each device is enabled separately. Open **Inbox** and choose **Enable on this device**. The browser asks for permission at that moment, and the device is registered with a signed request. Choose **Disable on this device** to stop. The owner can choose **Send a test** to check delivery. |
| 69 | |
| 70 | -Each device chooses what wakes it: private messages, replies to your posts, issues and pull requests, mentions, and relay notices such as a report awaiting moderation or a finished job. A burst of activity produces one notification per category. The text is encrypted to the device, so the push service never reads it. The app icon shows how many conversation events arrived since you last opened Inbox, and opening Inbox clears it. Devices that stop accepting messages are removed. |
| 71 | +Each device chooses what wakes it: private messages, replies to your posts, issues and pull requests, mentions, requests for a decision, and relay notices such as a report awaiting moderation or a finished job. A burst of activity produces one notification per category. The text is encrypted to the device, so the push service never reads it. The app icon shows how many conversation events arrived since you last opened Inbox, plus the requests that still wait for your decision. Opening Inbox clears the conversation count, and answering a request clears that request. Devices that stop accepting messages are removed. |
| 72 | + |
| 73 | +A request for a decision arrives with **Approve**, **Deny** and **Reply** buttons on the notification; a question offers only **Reply**. Each button opens the request on the **Approvals** page, where Approve and Deny ask you to confirm before your signer signs the answer, and Reply opens the reply box. Nothing is signed from the notification alone. See [Asking a person](agents.md#asking-a-person) for how requests are made and answered. |
| 74 | diff --git a/docs/testing.md b/docs/testing.md |
| 75 | index 636c815..2c67a1a 100644 |
| 76 | --- a/docs/testing.md |
| 77 | +++ b/docs/testing.md |
| 78 | @@ -7,6 +7,7 @@ make test-internal-race |
| 79 | make test |
| 80 | npm run test:files |
| 81 | npm run test:collaboration |
| 82 | +npm run test:approvals |
| 83 | ``` |
| 84 | |
| 85 | The repository also contains a multistage Docker build. `make docker-test` runs `TEST_PACKAGES` (default `./...`) under Go 1.27.1 on Debian Bookworm with the race detector. `make docker-build` builds the self-hosted runtime image. |
| 86 | diff --git a/docs/webmcp.md b/docs/webmcp.md |
| 87 | index 411eb2f..c7d27bc 100644 |
| 88 | --- a/docs/webmcp.md |
| 89 | +++ b/docs/webmcp.md |
| 90 | @@ -1,6 +1,10 @@ |
| 91 | # Browser tools |
| 92 | |
| 93 | -Every page registers tools for your browser agent; **Manage > Health** reports whether they are ready, and Chrome's Application panel lists them with their calls. The agent can browse repositories, files, issues and pull requests, inspect service status, manage background jobs, create backups, update relay settings, open Nostr links and the Files page, read this device's notification state and, as the owner, send a test notification. Turning notifications on stays a manual step, since the browser asks the person for permission. |
| 94 | +Every page registers tools for your browser agent; **Manage > Health** reports whether they are ready, and Chrome's Application panel lists them with their calls. The agent can browse repositories, files, issues and pull requests, list the requests that wait for your decision, inspect service status, manage background jobs, create backups, update relay settings, open Nostr links, the Files page and the Approvals page, read this device's notification state and, as the owner, send a test notification. Turning notifications on stays a manual step, since the browser asks the person for permission. |
| 95 | + |
| 96 | +## Approvals |
| 97 | + |
| 98 | +Three tools cover requests for a decision. `tiny.list_approvals` lists the requests addressed to the signed-in person with each one's asker, subject, expiry, state and answer, plus counts of open, answered and expired requests; `state` narrows the list to `open`, `answered`, `expired` or `all`. `tiny.read_approval` returns one request by event id with every reaction and reply from the people asked. `tiny.open_approvals` opens the Approvals page, focused on one request when `id` is given. Answering stays a manual step: the person approves, denies or replies with their own signer. To make a request, publish an event as described in [Asking a person](agents.md#asking-a-person). |
| 99 | |
| 100 | Choose **Sign in** in the main navigation and connect your Nostr signer. Reading protected information uses your browser session. Management actions request a signature and follow your account permissions. A queued backup or job has not finished until its status says so. |
| 101 | |