Repository navigation
acpmux: every chat's inbox; another agent's plain send is never a turn of its own (A1) - #27686
Conversation
…f their own (red) 300 agent messages waiting while the agent reads the one before them go in as one notice at its next tool step; an idle chat wakes for a blocker or its first work only, as one turn; a prompt queued behind a running turn never waits behind agent messages (#27566's case). The fake agent's FAKE_STEER_STEP answers a steer at the test's next tool step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
|
Owned by an agent session · session |
…n of its own A plain send from another agent (acpmux send, the MCP send tool) goes into the receiving chat's inbox, in a thread (--pr / --thread, else the sender and the PR its first line names, else the sender). The agent hears of new messages as ONE notice at its next tool step; an idle chat wakes as one debounced turn only for a blocker, a report of work it gave, its first work, or an action from its parent or delegator; an FYI never wakes it, and anything left waits at most 10 minutes. MCP inbox_read / inbox_reply and _acpmux/inbox_threads, inbox_thread and inbox_reply read and answer threads. Withdraw unsends an inbox item until a notice carried it, then retracts it into its thread. agentInbox: false puts plain sends back on the queue. Supersede-keyed, steer-only and team sends, peer and mesh forwards, lane reports, task cards and pair releases keep the queue path for now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
…nt-inbox # Conflicts: # cmux-tui/crates/acpmux/docs/acpmux-schema.json
|
Found 12 test failures on Blacksmith runners: Failures
...and 2 more test failures. View all on Blacksmith
|
… back off refusals - A chat closing or closed takes an agent's message as before the inbox; one its inbox holds, or a notice in flight carries, is answered with the closing error. - A notice turn's messages are noticed (logged) once its prompt is written, not at its turn's start; a Stop or failure before that leaves them new. - A refused steer backs off REFUSED_RETRY_MS within a turn; a turn's start or end clears it. - A resend of a noticed item says duplicate/noticed; a closed chat's inbox restores and the inbox methods wait for the chat's log load. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
…ounts as in Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
…s the inbox record Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Passes: CI passes on CI passes on Written by web and size checks: green Gallery before/after for
States
Pick other states with a |
- A notice turn counts as delivered once the harness writer confirmed its prompt (confirmed write), and each notice keeps its own landing/written mark. - inbox_review arms or disarms the wake timer under the lock it decided under, so an older review never aborts a newer timer. - trim never drops a message a notice in flight or landing carries. - A notice checks the queue and the turn cap again at dispatch (notice_held). - A chat closing meanwhile answers a notice's senders with the closing error. - A resend of a noticed item answers with its notice's logged outcome. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
…nt-inbox # Conflicts: # cmux-tui/crates/acpmux/src/hub/turns.rs
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
teamleaderleo
left a comment
There was a problem hiding this comment.
Review (codex gpt-6.1-sol high fast, independent review): SHIP at 69e370e
Sol reviewed this head because Astra's subrouter account hit its usage limit partway through the review. It covers the third merge with base. The one conflict is at the end of finish_turn: inbox_turn_ended now runs before base's new stopped_early return. A notice turn that base's Stop ends before its prompt was written still leaves its messages new, because notice_done gates on Inbox::written.
Earlier rounds:
- Round 9, Astra: SHIP at 201052a (base's peer_person_stop pin and a clean merge).
- Round 8, Astra: SHIP at b3e26bf (the second merge,
message_eventsscanninginboxrecords). - Round 7, Astra: SHIP at f54c0d1 (the first merge and the CLI flags).
- Round 6, Astra: SHIP at bf8dae1 (the inbox logic).
CI at this head: https://github.com/manaflow-ai/cmux/actions/runs/41053769339 ran fmt, protocol IR, clippy, lib tests (2193 passed) and every integration test binary. Codegen is clean. Every red fails the same way on base 7365192 (listed in the PR body), or failed on GitHub's API rate limit.
|
Shipped in:
|
|
Shipped in:
|
|
Base red from this PR on feat-cmux-next: |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo, 2026-10-29:
A coordinator session had 319 other agents' messages queued as 319 turns, behind a turn that the person's steers kept going.
The coordinator's design, from those words: agent messages never become turns; only the person's prompts are turns.
Default change. Before this PR, a plain send from another agent started a turn in an idle chat, or queued or steered into a busy one. Now it goes into the chat's inbox and starts no turn. It wakes an idle chat (one debounced turn) only when it is:
Any other action, and every FYI, waits for the chat's next turn, or up to 10 minutes in an idle chat. Config
agentInbox: falserestores the old behaviour.What
Every chat gets an inbox (
hub/inbox.rs). Another agent's plain send never becomes a turn or a steer of its own.What goes in (this PR, A1): a plain send from another agent on this host:
acpmux sendand the MCPsendtool. The one routing predicate isinbox::plain_send, which has a unit test.Still on the old queue path; A2 ports them as a follow-up:
--supersedekey;Their tests are unchanged and pin
agentInbox: falsewhere they send plain messages to test the queue path.Rollback switch: config
agentInbox: falseputs plain sends back on the queue without a new build.Storage. A text-only agent message goes into the receiver's inbox, kept apart from its queue. It is an
inboxrecord in the log, so a restart keeps it.Threads. A message's thread is, in order of preference:
--pr/--threadits sender gave (_meta.acpmux.pr/topic);Notices. The agent hears of new messages as ONE notice (provenance
inbox_notice), the way Claude Code reports background work. The notice has one line per thread with new messages: the count, the sender(s), and the newest message's first line cut to 120 characters. Blockers come first.Running turn: the notice is a steer read at the next tool step. Only one is in flight at a time, so a turn takes at most one per step. The turn cap (acpmux: turn cap, so a session taking many agents' messages can end its turn #27520) still holds.
A prompt waits in the queue: no notice steers in ahead of it; the notice goes into that prompt's own turn (fix(acpmux): queue automatic messages after the original steering window #27566's case).
Idle chat: it wakes as ONE turn, 2 s after the first message that may wake it, so a burst is one turn. A message wakes it only if it is:
block/question, aliasblocker);result, the default; aliasaction) from the session that started the chat or gave it its first work;An FYI never wakes a chat. Anything left waits at most 10 minutes before a notice brings it.
Replies. A sender waiting on
session/prompt(a waitedacpmux send) gets the answer of the steer or turn that carried its message. If the chat closes first, the sender gets the closing error. A chat that is closing or closed takes messages as it did before the inbox. A notice that went in logs its answer, or its error if it failed after it went in (aninbox_answeredrecord). A resend of one of its messages gets that same answer and is not carried again: before or after a restart, and after the in-memory inbox let go of the message (it keeps 500 and only lets go of answered, read ones).Durability. Messages are marked noticed (an
inbox_noticedrecord) only once the notice went in, meaning the agent's writer confirmed its prompt, for a steer and for a turn of its own. If a Stop or a failure comes before that, they stay new. Only one notice is in flight: a turn of its own until its prompt is written, a steer until the agent took it at its tool step. A steer notice checks again for a waiting prompt and the turn cap while it holds the input handoff, so nothing is written between that check and its own write. A refused notice is tried again after 30 s, or sooner when a turn starts or ends. A closed chat's threads survive a restart.Withdraw (
_acpmux/prompt_withdraw): an inbox item that no notice has carried yet isunsent(aninbox_removedrecord, which a restart honours). A resend of it answers withdrawn and never puts it back. A withdraw of a message whose notice is on its way but not yet written waits up to 10 s for it:unsentif the notice did not go in,retractedif it did. Once a notice has carried it, the withdraw answersretractedwithvia: "inbox", and the retraction goes into the item's thread.Reading and answering.
inbox_read(_acpmux/inbox_thread) reads a thread whole and marks it read;_acpmux/inbox_threadslists the threads.inbox_reply(_acpmux/inbox_reply) answers into the sender's inbox under the same thread, so it never becomes a turn for them.acpmux session inbox NAME [--thread T]andacpmux send --pr N | --thread T.Not inbox items; they run as before: images, uploads, slash commands, sub-session family messages, Web prompts.
Wire contract (for the GUI, which is a separate PR):
_acpmux/inbox_threads {sessionId}→{threads: [{threadId, key, label, from[], unread, new, count, newestAt, newestPreview, class}]}, newest first;_acpmux/inbox_thread {sessionId, threadId, markRead?}→{threadId, label, messages: [{promptId, from, fromSession, class, kind, at, text, read}]};_acpmux/inbox_reply {sessionId, threadId, text};inbox,inbox_noticed,inbox_answered,inbox_read,inbox_removed; the notice prompt's provenance is{kind: "inbox_notice", noticeId, threads}.docs/acpmux-schema.json;spec/acpmux-protocol.jsonlists the methods.Not the forum: base's
hub/forum.rsis rules announcements, not a message store, so the inbox does not reuse it.No capture: no visible change. The only webview file is the generated protocol method list (
methods.ts, three untyped methods), and the GUI for threads is a separate PR.Tests
tests/hub_integration/agent_inbox.rs:Red: https://github.com/manaflow-ai/cmux/actions/runs/40973826419 (3 of 3 fail). Green at the head: https://github.com/manaflow-ai/cmux/actions/runs/41053769339 (fmt, protocol IR, clippy, lib tests, every integration test binary; only the base reds below).
Base reds, each compared per test against base 7365192, where it fails the same way:
hermetic_daemon_guard::every_test_binary_runs_in_the_hermetic_environment;send_async::overlapping_direct_and_queued_sends_of_one_prompt_id_record_one_outgoing_copy, a peer-link send, so on the queue path. It is flaky: 6 of 8 fail on base, 1 of 8 at the head;help_examples/session.rs:179), release-notary's What's New highlights (chief-name-in-agent-chats.md), the test-execution registry (test_cmux_tui_owned_mini_caches.py), and the resource API boundary (crates/cmux-tui/src/cli.rs, which this PR does not touch).Earlier runs against older bases also showed the
remote_guard_modes,send_relay_cli,peer_state_origin,auto_resume::retryandtranscript_eventsreds; base has fixed them since. The intent-review warning, the gallery advisory and the PR-checks link jobs failed on GitHub's installation API rate limit.Merge with base. Base's
acpmux session inbox NAME --digest [--dry-run](the queue digest) and this PR's--thread [--mark-read]share one subcommand:--digest/--dry-rundrain the queue, and--threadreads inbox threads; the two conflict. The digest drains exactly the kinds that still queue until A2. Base's new queue tests (inbox digest, stop, session queue CLI, peer person stop) pinagentInbox: false.recover_message_eventsalso scansinboxrecords, so a message the inbox took has its message projection repaired after a restart, as a queued one does.finish_turnkeepsinbox_turn_endedon base's new Stop-before-prompt path. The inbox flags are flattened clapArgs, because inline they overflowed libtest's 2 MiB thread in a debug build.Risk
High, for every chat: other agents' messages no longer steer in or queue as turns.
Agent session: agent session
🤖 Generated with Claude Code