Skip to content

acpmux: agents start, list, read and message other sessions (cmux-sessions MCP) - #21858

Closed
teamleaderleo wants to merge 27 commits into
feat-cmux-nextfrom
leo/agent-powers
Closed

teamleaderleo wants to merge 27 commits into
feat-cmux-nextfrom
leo/agent-powers

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

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 the agent.suggestedTasks Debug toggle, default off). Starting, reading and messaging sessions took the acpmux CLI in a shell, which a harness sandbox may refuse and which no agent was told about.

What agents get

The cmux-sessions MCP server (acpmux offer-mcp, already injected into every local session's mcpServers / Claude --mcp-config) gains four tools in cmux-tui/crates/acpmux/src/session_mcp.rs:

Tool Daemon request Notes
start_session {prompt, title?, harness?, model?, host?, cwd?, worktree?} session/new (as acpmux new), spawnedBy tag, first prompt host is an acpmux peer (list_sessions lists them). worktree makes ~/.cmux/worktrees/<project>/<name> on cmux/<name> with branch.<b>.cmuxBase, the layout git.worktree.create uses, so Apply/Remove in the app work on it.
list_sessions {status?, mine?, limit?} _acpmux/sessions, _acpmux/peers, _acpmux/harnesses Leaves out the caller; returns hosts and harness names for start_session. Another chat's title and last message are left out.
read_session {session, replies?, wait_seconds?} _acpmux/wait, _acpmux/attach (visible: false, maxBytes) This chat's family only (see Limits). One call waits at most 50 s and returns timedOut: true with 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/prompt with an agent_message provenance (deliver.rs, as cmux agent message) The receiver shows it as this agent's message, not the person's.

The server now sends MCP instructions once at initialize: 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 the cmux mcp serve tools that cover the rest of the brief: workspace_open_url and tab_create_browser to open a URL, notification_create to tell the user something needs them, and agent_report to 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 existing acpmux CLI guide as the cmux:acpmux skill, 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.rs already 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 MCP instructions instead. 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's ccd_session (spawn_task chip, 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:

  • A new session never asks for less than the caller, on two checks:
    • The acpmux permission policy. If the child's effective policy is laxer, it gets the caller's (_acpmux/set_policy) before its first prompt. A caller policy acpmux doesn't know counts as ask.
    • The harness's own mode and sandbox. A harness profile whose command runs in full access (bypassPermissions, --dangerously-*, --yolo, Codex danger-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.
    • Nothing else is inherited: model, harness and MCP servers are the child's own.
  • A new session's folder must be inside the caller's own folder, or under a folder the user trusts: the nearest folder with a trust level, itself or an ancestor, is 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.
  • "This chat's family" is the sessions it started (their spawnedBy tag), the session it hangs under (its spawnedBy, or parentId for a fork or side session) and that session's other children. send_to_session and read_session reach only the family. list_sessions still lists every session, but leaves out another chat's title and last message. Messaging any chat stays with agents_chat_send (cmux-next: agents_chat_send and agents_chat_read, one agent prompts another chat (cx-ncc.96) #21647), which has workspace scoping.
  • Starting, reading or messaging a session never clears its unread mark. The server attaches with visible: false on the same connection before it creates or prompts a session.
  • A failed spawnedBy tag is retried once, then named in the result, since the family depends on it.
  • Two starts with the same title don't race for the worktree name: a name git says already exists takes the next suffix.
  • The worktree's git runs with core.hooksPath=/dev/null, core.fsmonitor=false, submodule.recurse=false and GIT_CONFIG_NOSYSTEM=1. A repository whose own config sets a checkout filter gets no worktree.
  • A peer drops a prompt's provenance (remote_guard.rs). So a brief or message to another host starts with a line that names the sending session.
  • worktree is this machine only. Its folder is a reviewed keep row in state-roots-baseline.tsv: it's the user's shared worktree folder, not per-build state.
  • list_sessions and read_session mask secrets (secret_scan::mask) in titles, last messages and replies, so one chat's agent can't read a key pasted into another chat.
  • No per-turn message cap here. The ping-pong guard between agents is a separate PR.

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.

  • Local command execution: they run no command the agent supplies. The only process they start is the worktree's git, hardened as above.
  • Unowned objects: reads and sends reach only this chat's family. A new session starts only in this chat's folder or a trusted one.
  • Local-state exposure: another chat shows up in list_sessions by 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 with ACPMUX_SESSION_TOOLS=0, and the change applies at the next daemon start. Offers stay behind agent.suggestedTasks. agent_tools.rs now registers cmux-sessions when either set is on, and tells the server which sets to list. A CLI or TUI daemon gives sessions the session tools too, unless ACPMUX_SESSION_TOOLS=0. Remote-origin sessions, ACPMUX_AGENT_TOOLS=0 and --strict-mcp-config still get none.

Verification

  • Review fixes (red, then green, same commands): cargo test -p acpmux --lib session_mcp and cargo 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 with cannot lock ref ... reference already exists; the child lost its unread mark after start_session + read_session; a folder outside the chat that nobody trusted started; and one read_session call ran past the 60 s client timeout. Green locally on Linux at 8228068 (5 unit, 3 integration).
  • tests/hub_integration/session_tools.rs runs the real acpmux offer-mcp process against a daemon socket with the test agent. It covers the instructions and the tool list; start_session into a worktree (path, branch, cmuxBase, a planted post-checkout hook does not run); the brief recorded as an agent_message from the parent; read_session waiting for the reply; list_sessions {mine} without the caller; send_to_session and its reply; and refusals (own chat, a chat it didn't start, worktree with host, empty brief) that create no session and reach no chat.
    • Red: hosted focused run 39183822873 on 5f79ae8 (tests only). Linux and macOS fail at the missing instructions.
    • Green after the merge with current feat-cmux-next and the masking regression (a key sent to the child comes back as [AWS access key] from both reads): hosted focused run 39200492737 on 5058314.
    • Earlier green: hosted focused run 39186971756 on 8574576 (feat-cmux-next merged in). The test passes on Linux and macOS, and clippy and fmt pass.
  • AcpmuxSessionToolsTests.swift: the toggle defaults on, and the spawn env has ACPMUX_SESSION_TOOLS=0 only when it's off (an inherited 0 is dropped). This runs in CI; no local Swift build.
  • Local: verify-local.py --affected passes except localization, which fails with 34k parity errors in catalogs this PR doesn't touch (base). The god-file check flags nothing new.
  • Dogfood:

I ran the hosted macOS build of 8574576 as acpmux, with a daemon in its own ACPMUX_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:

The child session ran and replied.

- **Session id:** `01a151ee-8bfc-7370-80ad-2537a2277537` (name `claude-sr-1`)
- **Worktree path:** `/Users/leoli/.cmux/worktrees/proj/pong-check`
- **Branch:** `cmux/pong-check`, based on `master`
- **Reply (verbatim):** `pong from the child session`
- **list_sessions (mine: true):** returned 1 session.

One oddity: `list_sessions` shows the session's title as the full prompt text instead of "pong check". The worktree and branch names did use "pong check".

acpmux session info on the child: tags.spawnedBy = the parent, and cwd ~/.cmux/worktrees/proj/pong-check on cmux/pong-check. Its first user_message carries _meta.provenance {kind: agent_message, from: <parent>}. The "oddity" the agent noticed was the titler: it hadn't named the child yet when list_sessions ran (later: "Child Session Ping Response Test"). The tool description now says title names the worktree. Plain -m claude refused because it has no sign-in under ~/.claude on this Mac, which is unrelated to this change. Not dogfooded: the tools inside the cmux NEXT app pane (needs a DEV app build; add dev-build if someone will dogfood it there), Codex/OpenCode harnesses, and host on 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

teamleaderleo and others added 8 commits October 18, 2026 18:06
…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>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 19, 2026 01:47 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Receipt for 8228068 (this PR's head; a new push replaces it)

  • Visual review: HQ feed card · gallery before/after runs
  • Checks: this head
  • App of this head (app.zip, kept 1 day): gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr21858 -f ref=leo/agent-powers -f platform=macos -f backend_mode=local

@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 953746dc-b509-4cf9-9837-4ce7f31eae68

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

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 8228068f64; ci-status is green without them.

CI passes on 8228068f64 (run 39213369075 attempt 1).
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped (the macos job needs both to succeed). Re-run CI once they pass, before merging.

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-iba4r created for this PR by the cmux coordinator.

@blacksmith-sh

This comment has been minimized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@teamleaderleo
teamleaderleo marked this pull request as ready for review October 19, 2026 02:13
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 19, 2026 02:13 — with GitHub Actions Inactive
@teamleaderleo

teamleaderleo commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator Author

Visual preview of 6cae0ed: in the maintainers' private review feed.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Not verified yet: the Swift half (the agent.sessionTools tunable, AcpmuxEnvironment.sessionTools, the launcher env, AcpmuxSessionToolsTests) hasn't compiled anywhere. PRs into feat-cmux-next run only the Linux checks. My cmux-next.yml dispatch on this branch (39188995748) skipped every Mac job, because swift-test only runs on dispatches from feat-cmux-next or next-batch/*, or on a full-ci PR. The Rust half is verified by the hosted focused run 39186971756 and the dogfood in the description. Before merge, the Swift side needs a next-batch or full-ci run.

teamleaderleo and others added 3 commits October 18, 2026 21:34
…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>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 19, 2026 04:38 — with GitHub Actions Inactive
…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>
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 19, 2026 06:37 — with GitHub Actions Inactive
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Rescue lane 2 (taking over the dead agent-powers session) here. Pushed the smudge-filter fix and merged over c9bcc59 without force: head ca1bf33.

  • 6cae0ed: test first. Hosted red on run 39207823986 (the repository's smudge filter ran outside the agent's sandbox).
  • 5f/fix commit: refuse_own_filters. start_session makes no worktree when the repository's own config (local, worktree, or an include of them) sets a smudge/process filter. Global filters like Git LFS stay allowed. Git also runs with submodule.recurse=false.
  • Merged current feat-cmux-next. The earlier cmux-next checks red was two base reds (ErrorJournalService literal state root + ErrorJournal sleeps, same on feat-cmux-next d9e1b51 locally) plus the motion check, which cmux-next: mark the sidebar card's Core Animation objects as reviewed motion exceptions #21951 fixed on base.

Running verify-cmux-tui-hosted.sh --full on ca1bf33 (run 39211049440). Adding full-ci because the Swift half (agent.sessionTools tunable, launcher env, AcpmuxSessionToolsTests) has never compiled: PRs into feat-cmux-next skip the Mac jobs. To whoever else is on this branch: please don't push while that runs. I'll merge when it and the Mac jobs are green.

@teamleaderleo teamleaderleo added the full-ci EXPENSIVE: full macOS tests/builds; overrides selective PR routing. Not needed for normal checks. label Oct 19, 2026
…owers

# Conflicts:
#	Packages/macOS/CmuxNext/Sources/CmuxNextAgentPane/AgentPaneTunables.swift
@teamleaderleo
teamleaderleo temporarily deployed to cmux-tui-merge-tree October 19, 2026 06:50 — with GitHub Actions Inactive
teamleaderleo added a commit that referenced this pull request Oct 19, 2026
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>
@blacksmith-sh

blacksmith-sh Bot commented Oct 19, 2026

Copy link
Copy Markdown

Found 2 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp_4l9dy78/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmph7rokn9x/summary-fail.json (live):/summary-fail.json (live): View Logs

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

@teamleaderleo

teamleaderleo commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator Author

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: --lib session_mcp 5/5, --test hub_integration session_tools 3/3. No push this round: the full-ci run on this head is still going. The kinds filter on attach is still left out, as noted before.

teamleaderleo added a commit that referenced this pull request Oct 19, 2026
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>
teamleaderleo added a commit that referenced this pull request Oct 19, 2026
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>
teamleaderleo added a commit that referenced this pull request Oct 19, 2026
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>
teamleaderleo added a commit that referenced this pull request Oct 19, 2026
…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>
teamleaderleo added a commit that referenced this pull request Oct 20, 2026
* 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>
teamleaderleo added a commit that referenced this pull request Oct 20, 2026
…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>
teamleaderleo added a commit that referenced this pull request Oct 20, 2026
* 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>
teamleaderleo added a commit that referenced this pull request Oct 20, 2026
#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>
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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 agent_tools.rs, offer_mcp.rs, SKILL.md and the state-roots baseline. Resolving them would give every session two servers, cmux-sessions and cmux-subsessions, each with its own start_session and list_sessions. That's a regression, so I'm not landing this branch.

Not carried over, and should be redone on the daemon design if we still want them:

  • list_sessions across every chat (base lists only your own sub-sessions).
  • host to start on an acpmux peer.
  • The agent.sessionTools Debug toggle (base uses the agents.subsessions.enabled setting).

teamleaderleo added a commit that referenced this pull request Oct 20, 2026
…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>
teamleaderleo added a commit that referenced this pull request Oct 21, 2026
* 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>

This branch was previously deployed

2 inactive (1 outdated) deployments
cmux-tui-merge-tree — 8228068f Deployed Oct 19, 2026 by manaflow-cmux-pr-autofix[bot]
artifacts — 6cae0ed4 Deployed Oct 19, 2026 by teamleaderleo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

full-ci EXPENSIVE: full macOS tests/builds; overrides selective PR routing. Not needed for normal checks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant