Skip to content

feat(box): an agent can ask for you without stopping, and gets your answer as its next prompt - #222

Draft
Stener1 wants to merge 10 commits into
cosscom:mainfrom
Stener1:bra-3276-attention-post
Draft

Stener1 wants to merge 10 commits into
cosscom:mainfrom
Stener1:bra-3276-attention-post

Conversation

@Stener1

@Stener1 Stener1 commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #219 (attention items on the box): this branch is #219's three commits plus four of its own. Review the last four, or the diff against 5b1b834.

Summary

A question form stops an agent until someone answers. For what needs a person but can wait (a PR ready to merge, a key only they can rotate), an agent now posts an ask and carries on. When the person answers or closes it, the box types that into the agent as its next prompt.

sequenceDiagram
    participant Agent
    participant Box as berthd
    participant App as Shipyard (a plugin on berth.attention)
    Agent->>Box: berth_ask / berthd ask {kind, title, details?, options?, recommended?}
    Box-->>Agent: {key: session:<name>, revision, status: open} (at once)
    Note over Agent: ends its turn, or keeps working
    App->>Box: PATCH /v1/attention/session:<name> {answer}
    Box->>Box: before:session.send gate, then save with telling = who settled it
    Box->>Agent: prompt held until idle, idem key attention:<key>@<settled>
    Box->>Box: told → telling cleared (each sweep retries until it is)
Loading
 internal/box/
   attentionapi.go
+    POST /v1/sessions/{name}/attention   # AgentAsk: strict decode, kind ∈ decision|action|ready|blocked,
+                                          # options only on a decision, at the session's own key
     PATCH /v1/attention/{key}
+      settling a session's ask → before:session.send for that session
+      tellAskers()                        # also from every sweep: survives a restart, never twice
   attention.go
+    AttentionItem.telling                 # set at settle, cleared once the box holds the prompt
   api.go
+    decodeStrict                          # decodeLimit that refuses unknown fields, naming them
 internal/mcpserver/mcp.go
+  berth_ask                               # additionalProperties: false, returns at once
 internal/boxcmd/askcmd.go
+  berthd ask --kind K --title T [--details D] [--option "LABEL[: what it means]"]... [--recommended L]
 internal/integrations/skills/berth/SKILL.md
+  "Need the user but can go on? Ask without stopping"

An ask is words and nothing else: no link or command for a button to follow, and every kind but a decision gets the app's own buttons. The box refuses an unknown kind and any field it doesn't name, so an agent can't sneak in a url or a command.

Evidence

Live, real Claude Code on a dev box (.claude/skills/verify-berthd/scripts/scenarios/ask-live.mjs on dev-box.sh). Session asker runs with only this build's berthd mcp (--strict-mcp-config) and calls berth_ask. Session ask runs bin/berthd ask. A small test plugin on berth.attention shows both asks, and the scenario clicks in it:

Both asks, posted while each agent kept going After Monday on the decision and Close on the ready
two asks answered and closed
- asker's ask is on the box (key session:asker, by mcp), and its turn ended without an answer;
  mcp-posted.txt: {"key":"session:asker","replaced":false,"revision":1,"status":"open"}
- mcp-answer.txt: Monday
- cli-answer.txt: The user closed your ask in Shipyard (ready: "PR #12 is ready to merge") without answering it (resolved). ...
- asker: turn asker#2, origin laptop:devbox, idem_key attention:session:asker@2026-11-06T13:35:01.840177069Z, finished
- ask:   turn ask#2,   origin laptop:devbox, idem_key attention:session:ask@2026-11-06T13:35:01.938993468Z
OK, console clean

Each agent wrote the answer it got as its next prompt to a file, using its own Bash tool. The scenario reads the file back, and also the turn the box recorded (prompted by the person's laptop, under the ask's idempotency key).

Unit tests (go test ./internal/box -run 'AnAgentsAsk|ASettledAsk|SettlingAnAsk|AnswerText', ./internal/mcpserver -run AnAsk):

  • A repeated ask replaces the last one: revision 2, still one item.
  • Refused: kind review or mark, an extra field (url, key, an option's command), options on a ready, a session that isn't there.
  • A snooze tells nothing. An answer is held for a busy agent once, and changing it later doesn't tell the agent again. A close tells it, and a mark doesn't.
  • A box that went down between saving the answer and queueing it: the next sweep tells the agent, once. telling survives a restart of the store, a snooze while the agent is being told doesn't send the answer twice, and a session that's gone lets it go.
  • A before:session.send hook that refuses → the settling PATCH gets 403 and the ask stays open.
  • Mutation checks: with the sweep retry off, or the gate off, those tests fail.

go vet ./... and go test -race ./... pass (only TestTheCatalogIsCapped fails here: it counts this machine's own commands and fails on main too). The app's checks, test, build and tsc -p e2e pass. docs-site builds. cargo check didn't run: no Rust changed, and this machine has no GTK.

Review: Codex review and adversarial-review ran once, plus one re-review. Fixed:

  • an answer lost if the box restarted after saving it (now telling plus the sweep retries)
  • a settle getting around the session's send gate
  • a duplicate send when a snooze landed during delivery (now keyed on when the ask settled)

Merge Danger

Door: two-way

Additive: one route, one MCP tool, one command, one optional field on attention items. Agents that never ask lose nothing, and reverting removes it all.

Blast Radius: small

New behaviour only runs when an agent posts an ask. One existing path changes: a PATCH that settles a session's own ask (session:<name>), even one a plugin posted, now types into that session and passes its before:session.send gate. decodeLimit now shares decodeBody with decodeStrict and behaves as before.

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

View guided diff

Stener1 and others added 8 commits November 6, 2026 12:39
…you between computers (BRA-3274)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent, and simplify the store

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, and never lose items on a failed read or write

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nswer as its next prompt (BRA-3276)

berthd ask and the berth_ask MCP tool post the session's own attention
item: a decision, an action, work ready to merge, or blocked. It returns at
once, and a new ask replaces the last. The box refuses a kind it doesn't
know, any field it doesn't name, and options on anything but a decision, so
an ask is words with no link or command for a button to follow. When a
person answers or closes it, the box types that into the agent, held until
it is idle, with the item's revision as the idempotency key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e answer from a plugin as its next prompt (BRA-3276)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he agent is told without the person waiting (BRA-3276)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… an ask passes the session's send gate (BRA-3276)

The settled ask keeps who settled it in telling until the box holds the
prompt for the agent; the PATCH starts the telling and every sweep tries
again what didn't go. A PATCH that would settle a session's own ask asks
before:session.send for that session first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3276)

Delivery goes by when the ask settled, not its revision: the idempotency
key and told both use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Nov 6, 2026

Copy link
Copy Markdown

@Stener1 is attempting to deploy a commit to the seanbrydonio's projects Team on Vercel.

A member of the Team first needs to authorize it.

Stener1 and others added 2 commits November 6, 2026 13:41
…'s description says when to ask (BRA-3276)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…packages' ptr (BRA-3276)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant