feat(web,desktop): coding agents post what they need of the developer to the plan's Inbox

Merged
DA
7d ago
+867-9

Part 3 of 3 in the plan Inbox stack: #2161 (backend) → #2162 (desktop Inbox tab) → #2163 (agents post to it).

Summary

This PR turns the Inbox on.

  • Three new coder tools:

    • post_to_inbox posts an item, or replaces the one under the same key and reopens it;
    • close_inbox_item takes an item back, with a reason;
    • read_inbox reads the agent's items.

    Every call returns the agent's own inbox: every open item with what it asks (details, options, recommendation), and the latest settled ones with their answers. So the model keeps track of its items even after its context is compacted, and learns a task is done without being messaged. Workers don't get these tools.

  • Instructions:

    • put what the agent needs from the developer in the Inbox, never in a message;
    • one item per need, under a key of its own;
    • never repost an item as a reminder, and close it once it's no longer needed;
    • never restate an open item in a message.

    The final-message instruction no longer asks the agent to list things the developer must decide.

  • Transcript rows: the agent's tab draws each inbox call as a row of its own, such as "Posted to your inbox: …", with the title as plain words, as its card shows it. A refused call shows its reason, so it never reads as done.

  • Privacy: PRIVACY.md says what the Inbox stores and where an answer goes. This is a product decision to review.

Evidence

  • ./scripts/check.sh passes on this branch.
  • macOS Electron verification (./scripts/verify.sh): not run. CI cannot verify the transcript rows' UI; a Mac break would be caught at the release rehearsal on a tag.
  • New tests:
    • tests/coder-inbox.test.ts (7): posting and replacing, the whole-inbox replies, refusals, the open-item limit, answers, cascade on plan deletion;
    • tests/coder-agent.test.ts (2): the instructions, and the tool telling the model how an answer opens;
    • tool-call-model.test.ts (2): the rows, and a refused call showing why.

Visual evidence

An agent's tab with its inbox calls as rows (headless Chrome over the demo's mock service):

Agent rows

Invariants

  • ./scripts/check.sh green. ./scripts/verify.sh not run (no Mac).
  • JSON Schema goldens unchanged.
  • Test count: +11 tests, none removed.
  • Every behavior change has, in this PR, a test that fails without it; no change-detector tests.
  • Gateway envelope goldens unchanged.
  • No new as assertion outside the allowlisted files, no Effect.run* outside the runtime edges, no new raw async primitives outside the recorded files, and no Effect, Stream, or Layer dropped as a statement (anti-slop and discarded-effect pass).
  • No hand-rolled file replaced.
  • No CLAUDE.md/AGENTS.md sentence names a changed part.
  • Package deps match imports; barrel/door rule kept (the renderer reaches only @sidecar/hosted/inbox-wire, which is Schema only).
  • PRIVACY.md changed: a product decision, called out above.

šŸ¤– Generated with Claude Code