Repository navigation
feat: answer inbox requests from the desktop, the cli and paired phones - #911
Conversation
There was a problem hiding this comment.
Important
The pasted reply hint, filtered thread pages, and follow-ups that also send workspaceId need a fix before this host is what desktop and phones call.
Reviewed changes
Host side of the external inbox: an ext: address asks a live agent, the question is pasted when the turn ends, and desktop, CLI, and paired phones read or wait for the reply.
- Inbox verbs.
inbox.summary,threads,thread,targets,ask,cancel,markRead,purge, and local-onlyinbox.wait, advertised asinboxV1with no protocol bump. - Answers.
orchestration.replyrecordsreply_to_idand accepts--body-fileand--body-stdin. The paste banner tells the agent the sender cannot see the terminal. - Delivery. Only the messages that fit in one paste are stamped delivered. The rest stay queued for the next turn, including agent-to-agent batches.
- Clients. Phones are allowlisted for the first eight verbs and receive
inboxChanged. Hub reverse forwards the inbox verbs by name. A reply push stays inattentionand carries ids only. - Guards. An
ext:address cannot act as a terminal in send, ask, dispatch, or spawn, and a reply to a message an inbox received is refused. Orchestration requests now apply the mobile allowlist.
⚠️ Desktop drops inboxChanged
broadcast_inbox_change emits inboxChanged, and phones forward every event, but the desktop client only publishes names in runtimeHostEventNames. inboxChanged is not in that set, so a desktop listener never sees the revision and will not refresh until reconnect.
Technical details
# Register inboxChanged on the desktop client
## Affected sites
- `rust/alera-cli/src/terminal_host/server/inbox_requests.rs:70` - broadcasts `inboxChanged`
- `lib/src/features/workbench/infra/terminal_host/terminal_host_client_models.dart:309` - `runtimeHostEventNames` has no `inboxChanged`
- `lib/src/features/workbench/infra/terminal_host/terminal_host_client_session_events.dart:63` - drops events missing from that set
## Required outcome
- An authenticated desktop client receives `inboxChanged` the same way it receives `orchestrationBoardChanged`.
- Phones already forward every event. Do not add a second filter there.
## Suggested approach
- Add `inboxChanged` to `runtimeHostEventNames` in the same change as the broadcast.grok-4.7 | 𝕏
f6306a4 to
eeced6a
Compare
There was a problem hiding this comment.
ℹ️ No new issues in this delta.
Reviewed changes
Re-reviewed the host changes since f6306a4. Overdue questions now expire on their own, and a paste in flight is no longer treated as a settled cancel or expiry.
- Expiry sweep. The presence tick expires overdue questions about every 30 seconds, wakes parked
inbox.waits, and broadcasts the new revision. - Paste window. Cancel is refused while that terminal's paste is in flight, and a wait does not report expiry until the paste's Enter decides. A successful stamp still restores an expired inbox row to delivered.
- Thread-bound sends.
orchestration.sendto an inbox must continue a question that inbox asked this terminal, and the message is stored on that thread. - Expiry bounds.
expiresInMsthat is not a whole number of milliseconds up to 7 days is refused before a question is written. - Gone recipient. A follow-up that inherits its terminal fails when that session is no longer known.
grok-4.7 | 𝕏
eeced6a to
2b29222
Compare
There was a problem hiding this comment.
ℹ️ No new issues in this delta.
Reviewed changes
Re-reviewed the host changes since eeced6ae. The pasted reply hint is now shell-portable, and a follow-up that also sends a workspace keeps the thread's recipient.
- Reply hint. The banner no longer suggests a bash heredoc. It points at
--bodyor--body-file. - Follow-ups.
inbox.askwiththreadIdandworkspaceIdand notoinherits the thread recipient. A workspace still only chooses the recipient of a new thread. - Stack. The store files left this diff. They now live on
feat/inbox-store.
grok-4.7 | 𝕏
2b29222 to
8db4d42
Compare
8db4d42 to
8d50a50
Compare
8d50a50 to
4387172
Compare

Summary
Host side of the external agent inbox. A caller outside any terminal asks an agent from an
ext:<name>address. The question is pasted into the agent when its turn ends, even if its task or dispatch is still open. The agent answers withalera orchestration reply, and any surface reads or waits for the reply.inbox.summary,inbox.threads,inbox.thread,inbox.targets,inbox.ask,inbox.cancel,inbox.markRead,inbox.purge,inbox.wait. They live outsideorchestration.*and go throughrequire_authenticated_local_request.inbox.waitstays local, since phones use theinboxChangedevent. Noorchestration.*verb is added.inboxV1, advertised to local clients and inmobile.hello. No protocol version changes.inbox.ask: takes a terminal, or the single running agent of a workspace narrowed by agent type (with candidates when ambiguous). The origin surface comes from the connection (cli,desktop, ormobilewith its device). It snapshots the target and defaults toext:user. A follow-up inherits the thread's inbox and recipient.inbox.wait: parks per question or per inbox after a cursor. It wakes on replies, cancellation and purge, and does a final store read at the deadline before reportingtimeout.inboxChanged {revision}goes to every authenticated client after inbox requests, orchestration requests and delivery stamps.reply --body-stdin.orchestration.reply: now recordsreply_to_idand gains--body-fileand--body-stdin.ext:address cannot be the sender ofsendorask, the target ofaskordispatch, a coordinator, an assignee or a spawn terminal. Replying to a message an inbox received is refused, so nobody bypasses expiry and the pending limit.attentioncategory, with a generic title and ids only. It carries no reply text and no tab id.alera inboxand the phone on the hub share one inbox.Stacked on #910.
Validation
inbox_requests_testsdrive the real dispatcher with CLI, desktop and phone clients. They cover recipient resolution and errors, origin, targets, correlation, waking waiters, the read state shared across clients, cancellation waking a waiter, the deadline final check, the inbox listener cursor, the phone allowlist (inbox.waitrefused) and theext:guards.cargo clippy --workspace --all-targets -- -D warningspasses.cargo test -p alera-cli: 1857 passed. One unrelated workflow worktree test (workflow_worktrees_snapshots_derive_retained_ownership_and_reject_replacement, "workflow integration is busy") failed once under load and passed 3 of 3 in isolation. All integration tests passed.Risk
The delivery change means a batch over 16 KiB is now pasted across turns instead of telling the agent that messages were omitted. Pruning only touches
ext:conversations.