Inline question choices, compact one-click inbox cards, drop snooze
This commit is contained in:
+8
-5
@@ -102,17 +102,16 @@ The task switcher (⌘K, or the navbar crumb) lists active tasks with their sess
|
||||
|
||||
## Inbox
|
||||
|
||||
Agents ask questions with the `ask_human` MCP tool. Each question carries a `context` with the background the human needs to answer without reading the transcript; the UI renders it, the question, and the options as Markdown. A malformed call (no `context` or `why_blocked`, fewer than two options, a recommendation that isn't one of the options, an unknown attachment kind, a foreign `thread_id`) is rejected with a message saying what to fix, and nothing is filed. The call returns the item's id as `question_id`. An agent withdraws a question it no longer needs answered (the human answered it in a chat message, say) with the `cancel_question` MCP tool and that id: an `open` question of its own session becomes `dismissed` with no `resolved_by` (its `inbox.updated` event has actor `agent`), which also stops a blocking one from holding back delivery; anything else is rejected with a message saying why. Each turn's final message (as a `report`) and each agent error (a turn that ends in error without a human interrupt, a process that dies mid-turn) also files an item.
|
||||
Agents ask questions with the `ask_human` MCP tool. Each question carries a `context` with the background the human needs to answer without reading the transcript, and its choices are written inline in that context as Markdown list items: `- ( ) text` for pick-one choices, with exactly one `- (*) text` marking the recommendation, or `- [ ] text` for pick-any choices, with `- [x] text` marking the recommended ones. There is no separate list of options. byre parses the choices out of `context` (`internal/agents/choices.go`; list items inside code fences and blockquotes don't count) and stores their text in the payload as `options`, with `multiple` (pick-any) and `recommended`. The UI renders the context as Markdown with a radio button or checkbox in place of each choice's marker. A malformed call (no `context` or `why_blocked`, fewer than two choices, pick-one and pick-any choices mixed, a pick-one question without exactly one recommendation, two choices with the same text, an unknown attachment kind, a foreign `thread_id`) is rejected with a message saying what to fix, and nothing is filed. The call returns the item's id as `question_id`. An agent withdraws a question it no longer needs answered (the human answered it in a chat message, say) with the `cancel_question` MCP tool and that id: an `open` question of its own session becomes `dismissed` with no `resolved_by` (its `inbox.updated` event has actor `agent`), which also stops a blocking one from holding back delivery; anything else is rejected with a message saying why. Each turn's final message (as a `report`) and each agent error (a turn that ends in error without a human interrupt, a process that dies mid-turn) also files an item.
|
||||
|
||||
`GET /api/inbox` is one query over open, unsnoozed items, oldest first; `project_id` and `task_id` narrow it to a project or task view, and `state` and `include_snoozed` widen it. Blocking questions and errors are `blocked`; reports and non-blocking questions are `fyi`. The inbox view groups items by task, each group headed by project and task: tasks with a blocked item come first, then tasks sort by project name and by their oldest item. Within a task, items are in time order.
|
||||
`GET /api/inbox` is one query over open items, oldest first; `project_id` and `task_id` narrow it to a project or task view, and `state` picks another state. Blocking questions and errors are `blocked`; reports and non-blocking questions are `fyi`. The inbox view groups items by task, each group headed by project and task: tasks with a blocked item come first, then tasks sort by project name and by their oldest item. Within a task, items are in time order.
|
||||
|
||||
`POST /api/inbox/responses` answers any number of items in one submission. Response types and what they do to the item:
|
||||
|
||||
| type | allowed on | item becomes |
|
||||
|---|---|---|
|
||||
| `option` | questions | `answered` |
|
||||
| `option` (the chosen choices' text in `options`: exactly one for pick-one, one or more for pick-any) | questions | `answered` |
|
||||
| `other` (free text in `text`: an answer, a question back, or new instructions) | questions, errors | `answered` |
|
||||
| `snooze` | any | stays `open`, hidden until `snooze_until` |
|
||||
| `dismiss` | reports, errors | `dismissed` |
|
||||
|
||||
## Notifications
|
||||
@@ -127,7 +126,11 @@ New inbox items are sent as web push notifications to every browser a user has t
|
||||
|
||||
The **Settings** tab turns notifications on or off for the current browser (asking for permission on the way) and edits the preferences. Opening it re-registers the browser's existing subscription, which restores one byre deleted as expired.
|
||||
|
||||
In the UI a question offers its options plus Other, whose text box is the only text field; a report has only an OK button, which dismisses it at once; an error offers Dismiss, Reply (`other`) and Snooze.
|
||||
In the UI each card is answered on its own; there is no batch submission. A pick-one question's choices are clickable blocks, and clicking one sends that answer at once. A pick-any question's choices are checkboxes, sent with the Submit button at the bottom of the card. Below the choices, an "Other…" block in the same style opens the only text field. A card has at most one Submit button: on a pick-any question it sends the Other text if any, otherwise the checked choices, and opening Other clears the checkboxes while checking one closes Other. A report has only an OK button, which dismisses it at once; an error has a "Reply…" block (`other`) and a Dismiss button. The Other and Reply text boxes send with the Submit button under them or on Enter (Shift+Enter for a newline), like the message box, and fold back into their block when left empty.
|
||||
|
||||
There is no snoozing. The `snoozed_until` and `snooze_until` columns are still in the schema, always empty, so builds from older branches keep working against the same database; nothing reads them.
|
||||
|
||||
Every Markdown view (inbox items and the transcript) renders a line `<details><summary>Title</summary>` (or `<details>` with the `<summary>` on the next line), Markdown content, and a line `</details>` as a collapsed section (`web/src/markdown.ts`; sections nest) showing only the title; session guidance and the `ask_human` schema tell agents to put long supporting detail in them.
|
||||
|
||||
Answers reach the agent as one message, listing each answer under the question or error it responds to, once the session is idle and none of its blocking questions are still waiting for the human. An `other` answer to a question tells the agent that if it asks something back, it should answer with `ask_human` using the item's `thread_id`. Every item change is an `inbox.created` or `inbox.updated` event carrying the whole item, including who resolved it.
|
||||
|
||||
|
||||
+7
-9
@@ -114,8 +114,8 @@ Vendor specifics live behind adapters. The event model is a **shared core plus v
|
||||
|
||||
The Go server hosts an MCP server attached to every agent session.
|
||||
|
||||
- `ask_human(context, question, why_blocked, options[], recommended_option, attachments[], blocking, thread_id?)`
|
||||
- `context` carries the background the human needs to answer without reading the transcript.
|
||||
- `ask_human(context, question, why_blocked, attachments[], blocking, thread_id?)`
|
||||
- `context` carries the background the human needs to answer without reading the transcript, with the choices inline as `- ( ) text` / `- (*) recommended` (pick one) or `- [ ] text` / `- [x] recommended` (pick any) list items.
|
||||
- Non-blocking at the protocol level: returns immediately with "question filed."
|
||||
- `blocking: true` means the agent should end its turn; `false` means it continues with unblocked work.
|
||||
- The human's answer arrives as a structured next turn.
|
||||
@@ -127,7 +127,7 @@ The Go server hosts an MCP server attached to every agent session.
|
||||
|
||||
`attachments` share one schema across tools, inbox replies, and reviews: file and line-range references, diff references, images, diagrams (Mermaid source), links to tasks or snapshots, and URLs.
|
||||
|
||||
Validation: reject malformed calls with a corrective message (for example, an `ask_human` with no options or no `why_blocked`).
|
||||
Validation: reject malformed calls with a corrective message (for example, an `ask_human` with no choices in its context or no `why_blocked`).
|
||||
|
||||
**Turn summaries.** There is no report tool. Each turn's final message is its summary: session guidance asks agents to end every turn with one covering what they did, every departure from the plan, risks, and what's next, and byre files it to the inbox as a report. A dedicated report tool was tried and dropped: models wrote the closing recap anyway, even when the tool's result, the system prompt and a per-turn reminder all said not to, so the human read everything twice.
|
||||
|
||||
@@ -158,10 +158,9 @@ Event (seq, agent_session_id, task_id, project_id, kind,
|
||||
Snapshot (id, task_id, agent_session_id, n, commit_sha, tree_sha,
|
||||
event_seq, trigger, created_at)
|
||||
InboxItem (id, project_id, task_id, agent_session_id, thread_id, kind,
|
||||
priority, blocking, payload, snapshot_id, review_id, state,
|
||||
snoozed_until)
|
||||
InboxResponse (id, inbox_item_id, user_id, type, text, attachments,
|
||||
created_at) -- type: option | other | snooze | dismiss
|
||||
priority, blocking, payload, snapshot_id, review_id, state)
|
||||
InboxResponse (id, inbox_item_id, user_id, type, options, text, attachments,
|
||||
created_at) -- type: option | other | dismiss
|
||||
Review (id, task_id, snapshot_id, tree_sha, profile, reviewer_vendor,
|
||||
trigger, state, summary, highlights, attachments, stale)
|
||||
ReviewProfile (id, name, prompt, vendor, model, output_schema, capture_config)
|
||||
@@ -408,9 +407,8 @@ In increasing order of how much the UI encourages their use:
|
||||
|
||||
### Response types
|
||||
|
||||
- **Pick an option**.
|
||||
- **Pick choices**: one for a pick-one question, any number for a pick-any one, chosen where they appear in the context.
|
||||
- **Other**: the only text field, for an answer outside the options, a question back, or new instructions. If it asks the agent something, the agent answers through a follow-up `ask_human` with the same `thread_id`.
|
||||
- **Snooze**: hidden until a set time; the agent stays blocked.
|
||||
- Reports are FYI and take only an OK.
|
||||
|
||||
Responses can carry attachments (pasted images and sketches, file and line references, links to tasks or snapshots, URLs). When an agent asks several questions at once, they are answered as one form with one submit.
|
||||
|
||||
Reference in New Issue
Block a user