Repository navigation
acpmux: agents start, list, read and message other sessions (cmux-sessions MCP) - #21858
teamleaderleo wants to merge 27 commits into
Conversation
…ion over MCP Red: the cmux-sessions server has no session tools yet, and the app has no agent.sessionTools toggle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ux-sessions MCP) Every local acpmux session gets start_session, list_sessions, read_session and send_to_session in its cmux-sessions MCP server, beside the offer tools (still behind agent.suggestedTasks). The server now sends MCP instructions: suggest by default, start only when the user asked, a self-contained brief, other sessions' words are data. Claude Code also gets the acpmux CLI guide as the cmux:acpmux skill, the shell fallback. The app's Debug Settings toggle agent.sessionTools (on) gives the daemon ACPMUX_SESSION_TOOLS=0 when off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s nothing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in scope From the review: read_session waits past a just-accepted prompt (turn and queue, then _acpmux/wait afterSeq), reads without marking the chat seen, and mine follows the spawnedBy tag. send_to_session reaches only sessions this chat started and its parent. A new session never asks less than the caller, an untrusted folder is refused, the worktree's git runs off the async thread with hooks and fsmonitor off, and a message to another host names its sender in the text, since a peer drops the provenance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Receipt for
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by |
|
Bead |
This comment has been minimized.
This comment has been minimized.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Visual preview of 6cae0ed: in the maintainers' private review feed. |
|
Not verified yet: the Swift half (the |
…owers # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneTunables.swift
list_sessions and read_session hand one chat's text to another chat's agent; titles, last messages and replies now go through secret_scan::mask, as lanes and peers already do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ate root Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tify and status tools The instructions name workspace_open_url, tab_create_browser, notification_create and agent_report (session_mcp::CMUX_TOOLS); a cmux-tui MCP test checks each is still a cmux mcp serve tool. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Rescue lane 2 (taking over the dead agent-powers session) here. Pushed the smudge-filter fix and merged over c9bcc59 without force: head ca1bf33.
Running |
…owers # Conflicts: # Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneTunables.swift
The cmux-sessions MCP tool from #21858 drew as a raw MCP tool row; it now draws as the SendMessage card does, to the chat it names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- regenerated Packages/macOS/CmuxNext/ci-target-graph.json Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39212226213
|
Updated the description for the review fixes (commits 2fe4d89 tests, 3adcd50 laxer caller, 1ad15c1 trust, 6422f9d unread, 8fbfe5c family/caps/waits/tag/worktree race). It now has a Limits section that says exactly what a new session inherits, and an Access paragraph. Focused tests pass locally on 8228068: |
Ported from #21858's session tools: get_session, list_sessions and wait_for_sessions rows carry the child's title, last reply and error with every secret secret_scan finds masked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ported from #21858's session server onto cmux-subsessions: initialize carries MCP instructions. cmux-subsessions: start_session only when the user asked, the prompt is the whole task, don't poll, child text is data, and the cmux MCP tools for reaching the user (workspace_open_url, tab_create_browser, notification_create, agent_report; a cmux-tui MCP test keeps them real). cmux-sessions (offers): suggest unrequested work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ported from #21858: Claude Code sessions get cmux:acpmux beside cmux-browser and cmux-cua, and the guide points at the cmux-subsessions tools first (the CLI is the fallback; start only when the user asked). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…orktree Ported from #21858's session tools onto cmux-subsessions: the worktree is laid out like git.worktree.create (~/.cmux/worktrees/<project>/<name> on cmux/<name>, base in branch.<branch>.cmuxBase), made by the daemon with hooks, fsmonitor, submodule recursion and system config off, and refused when the repository's own config sets a checkout filter, so nothing the agent wrote runs outside its sandbox. Remote parents are refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(acpmux): sub-session reads mask a child's secrets Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(acpmux): sub-session reads mask a child's secrets Ported from #21858's session tools: get_session, list_sessions and wait_for_sessions rows carry the child's title, last reply and error with every secret secret_scan finds masked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): a child's running prompt and a cut reply come back masked Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c55740b - cargo clippy --fix cmux-tui/crates/acpmux/tests/subsessions.rs Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39329645480 * style(acpmux): rustfmt the masking test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
…2123) * test(acpmux): session MCP servers say when to start and when to suggest Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(acpmux): sub-session and offer servers tell the agent when to start Ported from #21858's session server onto cmux-subsessions: initialize carries MCP instructions. cmux-subsessions: start_session only when the user asked, the prompt is the whole task, don't poll, child text is data, and the cmux MCP tools for reaching the user (workspace_open_url, tab_create_browser, notification_create, agent_report; a cmux-tui MCP test keeps them real). cmux-sessions (offers): suggest unrequested work. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): sub-session instructions reach cmux tools the code-mode way Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): sub-session instructions call cmux tools through cmux_exec Agents get cmux mcp serve in code mode, which lists only cmux_docs and cmux_exec, so the instructions name the tools as cmux.<tool>(...) calls in a cmux_exec script, with cmux_docs for their arguments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(acpmux): the cmux plugin carries the acpmux CLI skill Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(acpmux): the cmux plugin carries the acpmux CLI skill Ported from #21858: Claude Code sessions get cmux:acpmux beside cmux-browser and cmux-cua, and the guide points at the cmux-subsessions tools first (the CLI is the fallback; start only when the user asked). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#22196) * test(acpmux): start_session worktree:true runs nothing the agent wrote The child gets its own worktree laid out like git.worktree.create; the repository's hooks never run, and a checkout filter in its own config refuses the worktree before the filter can run outside the sandbox. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(acpmux): start_session worktree:true starts a child in its own worktree Ported from #21858's session tools onto cmux-subsessions: the worktree is laid out like git.worktree.create (~/.cmux/worktrees/<project>/<name> on cmux/<name>, base in branch.<branch>.cmuxBase), made by the daemon with hooks, fsmonitor, submodule recursion and system config off, and refused when the repository's own config sets a checkout filter, so nothing the agent wrote runs outside its sandbox. Remote parents are refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci(cmux-tui): the tree key covers the embedded sub-session settings rows hub/subsessions.rs embeds schemas/settings/source/subsessions.json; the repository tree-key check failed every cmux-next checks run without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * style(acpmux): rustfmt the worktree test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): a worktree start checks files out without the repository's filters, in the same subfolder Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): a worktree start checks files out without the repository's config The worktree is added with --no-checkout, and its files are written by a throwaway git directory that borrows the repository's objects but none of its config or the user's. No filter, hook, fsmonitor, transport, credential helper or lazy fetch named by the agent's repository runs in the daemon, and a config change during the start changes nothing (the earlier refusal of repositories with their own filters had a check-then-act race and missed LFS transports). All git runs with GIT_NO_LAZY_FETCH, GIT_ALLOW_PROTOCOL=none, GIT_SSH_COMMAND=false and LC_ALL=C; the branch starts from the resolved commit; a child started from a subfolder starts in that subfolder of its worktree; a start that fails after the worktree is made removes it and its branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): a worktree start fills a partial clone's missing files A blobless clone cloned without a checkout lacks the commit's files; the worktree start must fetch them from the promisor remote without running the clone's own config (its upload-pack here would leave a marker). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): worktree start: subfolder cwd, partial clones, no panics - The child's folder in its worktree is found after the checkout, so a child started from repo/web starts in the worktree's web/, not its root. - A partial clone's missing files are fetched from its promisor remote's URL first, through a config-less git directory (the user's and system's config, not the repository's; file, git, http, https, ssh only). - The cleanup guard has no expect(), and is kept until after the parent-closed check, so a child closed there leaves no worktree behind. - The worktree tool description no longer says filtered repos are refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(acpmux): a partial clone's fetch runs no maintenance on its objects Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(acpmux): worktree start: no maintenance, guard owned by the blocking task - The partial-clone fetch writes into the repository's objects from a throwaway git directory whose refs reach none of them; its automatic maintenance (repack --cruft) would prune the user's unpushed objects. It now runs with --no-auto-maintenance, and every git call here with maintenance.auto=false and gc.auto=0. - The blocking task that makes the worktree also makes its cleanup guard, so a start dropped while it runs still removes the worktree. - ~/.cmux/worktrees is the app's git.worktree.create root; it gets a `keep` row in the state-roots baseline, as git_ops/worktree.rs has. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
Closing: feat-cmux-next now has this feature, built in the daemon instead of in this MCP server.
Merging base into this branch hit conflicts in Not carried over, and should be redone on the daemon design if we still want them:
|
…ot your prompt (#22013) * Agent pane: test that other agents' messages draw as theirs, not raw prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: other agents' messages draw as cards from their sender A prompt acpmux delivered from another agent (agent_message provenance: send_to_session, cmux agent message) drew as the person's own bubble, and an adopted Claude session's <cross-session-message>, <task-notification> and <system-reminder> prompts drew as raw XML. They now draw as a message card from the sender (Open beside for a chat here; the raw envelope once opened), a quiet task line, or nothing, and stay out of the prompt history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: test that send_to_session calls draw as message cards Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: send_to_session calls draw as message cards The cmux-sessions MCP tool from #21858 drew as a raw MCP tool row; it now draws as the SendMessage card does, to the chat it names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: read Claude Code envelopes only in adopted history; no row for bookkeeping Review fixes: a reminder before a typed prompt hid the prompt; it now draws without the reminder. Bookkeeping alone gets no row instead of an empty, focusable 34px one. Envelopes are read only from adopted Claude history (client: adopt), so a live prompt can't pass itself off as another agent's. Rows keep the sender's words as their text, so copy and the minimap don't show raw XML. Numeric entities decode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 3d13829 - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/README.md - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39212708861 * Agent pane: a reminder-only prompt keeps its turn; Retry, recall, images and steer for incoming messages A Claude Code prompt that is only bookkeeping keeps an empty row that draws nothing, so the answer it brought stays its own turn instead of joining the previous prompt's. Retry on another agent's message sends the prompt as the agent got it, sender and envelope included. A task's result never comes back with the up arrow, whether suggested tasks show or not. Another agent's message card shows the images it sent and a steer's read state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: test that a reminder's turn offers no Retry of the earlier prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: gallery entry for messages from other agents and task notices Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix agent message cards: open by session id, show the sender words A message cmux delivers to an acpmux chat carries the sender address as provenance from: acp:<session id> for a chat, term_... for a terminal agent, cli for a person at a shell. The card used it verbatim as the chat to open, so Open asked the host for acp:<id>. Strip acp: for the session id, and give no Open for any other address. The prompt text is cmux rendered message (agent_message_prompt.rs render): a header, a delivery note, ---, the body, and a closing line naming the message id. The card now shows the body, read only when the whole render matches and the closing line names provenance msgId, and keeps the full render as raw. Any other text shows whole. A task notification turn no longer offers Retry, which resent the notification XML as the person prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
* Agent pane: test that other agents' messages draw as theirs, not raw prompts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: other agents' messages draw as cards from their sender A prompt acpmux delivered from another agent (agent_message provenance: send_to_session, cmux agent message) drew as the person's own bubble, and an adopted Claude session's <cross-session-message>, <task-notification> and <system-reminder> prompts drew as raw XML. They now draw as a message card from the sender (Open beside for a chat here; the raw envelope once opened), a quiet task line, or nothing, and stay out of the prompt history. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: test that send_to_session calls draw as message cards Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: send_to_session calls draw as message cards The cmux-sessions MCP tool from #21858 drew as a raw MCP tool row; it now draws as the SendMessage card does, to the chat it names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: read Claude Code envelopes only in adopted history; no row for bookkeeping Review fixes: a reminder before a typed prompt hid the prompt; it now draws without the reminder. Bookkeeping alone gets no row instead of an empty, focusable 34px one. Envelopes are read only from adopted Claude history (client: adopt), so a live prompt can't pass itself off as another agent's. Rows keep the sender's words as their text, so copy and the minimap don't show raw XML. Numeric entities decode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at 3d13829 - vp fmt webviews/src/agent-session/acpmux/replyHref.ts - vp fmt webviews/src/pages/markdown/README.md - vp fmt webviews/src/pages/markdown/links.ts - vp fmt webviews/test/markdown-cmux-app-links.test.ts Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39212708861 * Agent pane: Reply in place on another agent's message card An incoming agent message card (IncomingMessageCard, cx-eibkk) gets a Reply action (button, or R with the card focused). The answer goes to the sending chat as send_to_session sends a message: a session/prompt to that chat, attached hidden, with an agent_message provenance naming this chat, the message's threadId and replyTo the message, so it lands in the sender's chat as a threaded reply. acpmux took provenance only over the unix socket. The app pane (LocalApp) may now send one only as a reply: replyTo set, from another chat of this daemon, to the prompt's chat. Web and peers are still refused. The pane relay allows `provenance` in session/prompt's _meta.acpmux. Follow-up to cx-eibkk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: let the app pane's reply past the origin guard, only for a received message The remote guard refused any provenance from LocalApp before the reply rule ran. It now leaves LocalApp to prompt_provenance, and Hub::app_reply_fits requires the replying chat's log to hold the agent message (`replyTo`) the target chat sent it. The shared pane policy lists `provenance` as the Swift table does. A `cmux agent message` sender (`acp:<id>`) is answered by its id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: move the app reply check out of requests.rs (god-file budget); rustfmt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: a reminder-only prompt keeps its turn; Retry, recall, images and steer for incoming messages A Claude Code prompt that is only bookkeeping keeps an empty row that draws nothing, so the answer it brought stays its own turn instead of joining the previous prompt's. Retry on another agent's message sends the prompt as the agent got it, sender and envelope included. A task's result never comes back with the up arrow, whether suggested tasks show or not. Another agent's message card shows the images it sent and a steer's read state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: test that a reminder's turn offers no Retry of the earlier prompt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: gallery entry for the incoming message card and its Reply Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(next): PR autofix at c426d09 - webviews/src/agent-session/acpmux/conversation/IncomingMessageCard.tsx Autofix-Run: https://github.com/manaflow-ai/cmux/actions/runs/39359952105 * Agent pane: gallery entry for messages from other agents and task notices Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix agent message cards: open by session id, show the sender words A message cmux delivers to an acpmux chat carries the sender address as provenance from: acp:<session id> for a chat, term_... for a terminal agent, cli for a person at a shell. The card used it verbatim as the chat to open, so Open asked the host for acp:<id>. Strip acp: for the session id, and give no Open for any other address. The prompt text is cmux rendered message (agent_message_prompt.rs render): a header, a delivery note, ---, the body, and a closing line naming the message id. The card now shows the body, read only when the whole render matches and the closing line names provenance msgId, and keeps the full render as raw. Any other text shows whole. A task notification turn no longer offers Retry, which resent the notification XML as the person prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(agent-pane): the reply tests follow #22013's sender address and card fields #22013 opens and answers only a chat's cmux address (acp:<id>) and the card now carries the message's msgId and threadId, so the fixtures send from acp:child-3 and the incomingPrompt expectations include both ids. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=c1a6da4a-9c34-4cea-864c-8b045b51ac46&host=huge-blue * test(acpmux): an app pane reply signs only as a chat the pane shows, in its thread, under its name (red) The app pane's reply in place (Origin::LocalApp) is refused unless its from chat is one this connection shows (attached and not held unseen), its threadId is the answered message's thread, and its fromName is that chat's title, name or id. Today app_reply_fits checks only that from got the message, so the pane could sign as any chat that did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=c1a6da4a-9c34-4cea-864c-8b045b51ac46&host=huge-blue * fix(acpmux): an app pane reply signs only as a chat the pane shows, in its thread, under its name Hub::app_reply_fits is default deny for what the app pane signs: from must be a chat this connection shows (attached, not held unseen), the reply's threadId must be the answered message's thread (its threadId, else its msgId), and a fromName must be the chat's own title, name or id as the pane writes one. Before, any chat that got the message could be named as the sender under any name and thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=c1a6da4a-9c34-4cea-864c-8b045b51ac46&host=huge-blue * style(acpmux): rustfmt the app reply check Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=c1a6da4a-9c34-4cea-864c-8b045b51ac46&host=huge-blue * Agent pane: Reply on the received-message line uses the type tokens; the catalog keeps base key order Follows the adoption review: - The Reply key hint and its sent/failed note use --text-caption and --text-detail instead of literal font sizes (check-type-scale). - The App.tsx props are one per line (oxfmt). - Localizable.xcstrings is base plus the four message.repl* keys, in base order, instead of a reordered rewrite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Agent pane: Reply's test opens it with its button, not the removed R key; the sent note wraps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: keep the strings catalog in base order; add only the four Reply strings Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: tool output under a message keeps base margins; the reply editor lines up with the body The base merge swapped the two margins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux, agent pane: a reply in place arrives as a cmux agent message, linked back and signed by acpmux The pane sends from and to as acp:<id> addresses, as cmux agent message does, so the sender chat links the reply and can answer it; acpmux refuses a bare id. The text is wrapped as cmux renders a message (sender, id, the message it answers, the not-your- operator note). acpmux signs fromName with the replying chat own name instead of matching the pane one, so a stale title is never refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * agent pane: closing a message closes its reply and keeps the draft; reply state survives scrolling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-autofix[bot] <cmux-autofix[bot]@users.noreply.github.com>
An agent in a cmux NEXT chat can now start another agent session, list sessions, read another session's reply and message a session it started, through MCP tools every local acpmux session already loads. Before this, an agent could only offer a chat (
offer_session/suggest_tasks, behind theagent.suggestedTasksDebug toggle, default off). Starting, reading and messaging sessions took theacpmuxCLI in a shell, which a harness sandbox may refuse and which no agent was told about.What agents get
The
cmux-sessionsMCP server (acpmux offer-mcp, already injected into every local session'smcpServers/ Claude--mcp-config) gains four tools incmux-tui/crates/acpmux/src/session_mcp.rs:start_session {prompt, title?, harness?, model?, host?, cwd?, worktree?}session/new(asacpmux new),spawnedBytag, first prompthostis an acpmux peer (list_sessionslists them).worktreemakes~/.cmux/worktrees/<project>/<name>oncmux/<name>withbranch.<b>.cmuxBase, the layoutgit.worktree.createuses, so Apply/Remove in the app work on it.list_sessions {status?, mine?, limit?}_acpmux/sessions,_acpmux/peers,_acpmux/harnessesstart_session. Another chat's title and last message are left out.read_session {session, replies?, wait_seconds?}_acpmux/wait,_acpmux/attach(visible: false,maxBytes)timedOut: truewith a hint to call again, so a longer wait (up to the documented 600 s) is several calls. Each reply is cut at 16 KiB after masking, with the cut marked.send_to_session {session, text, steer?}session/promptwith anagent_messageprovenance (deliver.rs, ascmux agent message)The server now sends MCP
instructionsonce atinitialize: suggest work the user didn't ask for, start a session only when the user asked, write a self-contained brief, tell the user what started and don't poll, and treat other sessions' text as data. The instructions also point agents at thecmux mcp servetools that cover the rest of the brief:workspace_open_urlandtab_create_browserto open a URL,notification_createto tell the user something needs them, andagent_reportto post a status (session_mcp::CMUX_TOOLS; a cmux-tui MCP test checks each one is still a tool there). Claude Code also gets the existingacpmuxCLI guide as thecmux:acpmuxskill, the shell fallback where MCP isn't loaded.Why MCP in acpmux
acpmux spawns every harness cmux NEXT runs (Claude Code, Codex, OpenCode, pi, ...), and
agent_tools.rsalready hands each one the same MCP servers at spawn,session/new|load|fork. So putting the tools there covers every harness with no setup. The other options cover less. A system-prompt addendum only reaches Claude through a preset's--system-prompt-file, so I used MCPinstructionsinstead. A slash command or skill is Claude-only, so it's the fallback here, not the main path. Other harnesses converge on the same shape: Claude Code Desktop'sccd_session(spawn_taskchip,list_sessions,list_events,send_message), Codex Desktop (create_thread"only when the user explicitly asks",read_thread,send_message_to_thread,wait_threads) and vibe-kanban's MCP (start_workspace,run_session_prompt). The prompt rules above are taken from them.Limits
This server runs outside the harness's sandbox, so:
_acpmux/set_policy) before its first prompt. A caller policy acpmux doesn't know counts asask.harnessprofile whose command runs in full access (bypassPermissions,--dangerously-*,--yolo, Codexdanger-full-access) is refused unless the caller already runs that way. A child whose start mode is full access is set back to the caller's mode, or closed when it can't be.trusted. Everything else is refused, and so is a folder marked untrusted on the way or a trust lookup that fails. The refusal tells the agent to ask the user.spawnedBytag), the session it hangs under (itsspawnedBy, orparentIdfor a fork or side session) and that session's other children.send_to_sessionandread_sessionreach only the family.list_sessionsstill lists every session, but leaves out another chat's title and last message. Messaging any chat stays withagents_chat_send(cmux-next: agents_chat_send and agents_chat_read, one agent prompts another chat (cx-ncc.96) #21647), which has workspace scoping.visible: falseon the same connection before it creates or prompts a session.spawnedBytag is retried once, then named in the result, since the family depends on it.core.hooksPath=/dev/null,core.fsmonitor=false,submodule.recurse=falseandGIT_CONFIG_NOSYSTEM=1. A repository whose own config sets a checkout filter gets no worktree.remote_guard.rs). So a brief or message to another host starts with a line that names the sending session.worktreeis this machine only. Its folder is a reviewedkeeprow instate-roots-baseline.tsv: it's the user's shared worktree folder, not per-build state.list_sessionsandread_sessionmask secrets (secret_scan::mask) in titles, last messages and replies, so one chat's agent can't read a key pasted into another chat.Access
The tools run in the local acpmux daemon's MCP server, not over the remote CLI relay. They add no v2 socket method and no relay allowlist entry.
list_sessionsby name, harness, folder and status only. Everything returned is masked.Gate
The session tools are on by default. The Debug Settings toggle
agent.sessionTools("Session tools for agents") turns them off: the app then starts acpmux withACPMUX_SESSION_TOOLS=0, and the change applies at the next daemon start. Offers stay behindagent.suggestedTasks.agent_tools.rsnow registerscmux-sessionswhen either set is on, and tells the server which sets to list. A CLI or TUI daemon gives sessions the session tools too, unlessACPMUX_SESSION_TOOLS=0. Remote-origin sessions,ACPMUX_AGENT_TOOLS=0and--strict-mcp-configstill get none.Verification
cargo test -p acpmux --lib session_mcpandcargo test -p acpmux --test hub_integration session_tools. Tests went in first in 2fe4d89. Red: the unknown caller policy gave no policy; parallel starts failed withcannot lock ref ... reference already exists; the child lost its unread mark afterstart_session+read_session; a folder outside the chat that nobody trusted started; and oneread_sessioncall ran past the 60 s client timeout. Green locally on Linux at 8228068 (5 unit, 3 integration).tests/hub_integration/session_tools.rsruns the realacpmux offer-mcpprocess against a daemon socket with the test agent. It covers the instructions and the tool list;start_sessioninto a worktree (path, branch,cmuxBase, a plantedpost-checkouthook does not run); the brief recorded as anagent_messagefrom the parent;read_sessionwaiting for the reply;list_sessions {mine}without the caller;send_to_sessionand its reply; and refusals (own chat, a chat it didn't start, worktree withhost, empty brief) that create no session and reach no chat.instructions.[AWS access key]from both reads): hosted focused run 39200492737 on 5058314.AcpmuxSessionToolsTests.swift: the toggle defaults on, and the spawn env hasACPMUX_SESSION_TOOLS=0only when it's off (an inherited0is dropped). This runs in CI; no local Swift build.verify-local.py --affectedpasses exceptlocalization, which fails with 34k parity errors in catalogs this PR doesn't touch (base). The god-file check flags nothing new.I ran the hosted macOS build of 8574576 as
acpmux, with a daemon in its ownACPMUX_HOME(not the app's), on Leo's Mac. A real Claude Code session (-m claude-sr, the subrouter profile) got this prompt: "Use your cmux-sessions MCP tools, not a shell: start_session (harness claude-sr, title "pong check", worktree true, prompt "Reply with exactly the words: pong from the child session"), then read_session with wait_seconds 180, then list_sessions mine." Its reply:acpmux session infoon the child:tags.spawnedBy= the parent, and cwd~/.cmux/worktrees/proj/pong-checkoncmux/pong-check. Its firstuser_messagecarries_meta.provenance {kind: agent_message, from: <parent>}. The "oddity" the agent noticed was the titler: it hadn't named the child yet whenlist_sessionsran (later: "Child Session Ping Response Test"). The tool description now saystitlenames the worktree. Plain-m clauderefused because it has no sign-in under~/.claudeon this Mac, which is unrelated to this change. Not dogfooded: the tools inside the cmux NEXT app pane (needs a DEV app build; adddev-buildif someone will dogfood it there), Codex/OpenCode harnesses, andhoston a real peer.Changelog
Added: agents in cmux NEXT can start, list, read and message other agent sessions through the cmux-sessions MCP tools (Debug toggle "Session tools for agents", on).
🤖 Generated with Claude Code