Skip to content

cmux NEXT agent pane: a click on a sub-session opens its chat (chat.offer.open) - #23159

Merged
teamleaderleo merged 42 commits into
feat-cmux-nextfrom
cmux/subsession-visibility-followups
Oct 21, 2026
Merged

teamleaderleo merged 42 commits into
feat-cmux-nextfrom
cmux/subsession-visibility-followups

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Follows #22827, now merged; the diff is only this PR's Mac handler, family check and tests.

#22827's page asks the Mac app to open a sub-session's chat with chat.offer.open, but nothing on the Mac side handled it. Clicking a sub-session card, the Subagents summary, or a received message's sender did nothing.

  • A click on a sub-session opens its chat. If a tab already shows that session, it is shown and focused. Otherwise the chat opens in the side pane beside the chat, the same way side chats open. Nothing opens without a click; sub-sessions still don't auto-open.
  • Only the chat's family can be opened. That is a session the pane started, a sub-session at any depth, or the session that started this one. The chat itself never counts. The turn-state watch now keeps acpmux's startedBy links, which this check uses.
  • Cloud VM chats work too. Their family comes from that machine's daemon (RemoteAgentSessionLinks), as chat.sideChat already used. A Cloud VM chat's sub-sessions open, and so does the session that started it.
  • Tests: AgentPaneOfferOpenTests, AgentSubsessionFamilyTests.

No capture: nothing new is drawn. The existing cards, Subagents rows and sender names now open a chat when clicked, and the tests above cover which chats open.

This is the useful part of the dead subsession-visibility lane's uncommitted WIP (branches orphan/subsession-visibility-mac-wip and orphan/subsession-visibility-web2-wip). I did not port:

🤖 Generated with Claude Code


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


Summary by cubic

Clicking a sub-session now opens its chat on the Mac: the transcript's sub-session cards, the Subagents summary rows, and a received message's sender all open that session's chat beside the current one. Nothing opened before—the page asked for it with chat.offer.open, but no Mac host answered.

Agent pane

  • The tab that already shows the session is brought up and focused; otherwise the chat opens in the side pane beside the current chat, focused because the person asked. Nothing opens without a click.
  • Only a session this pane started, one of the chat's family (a sub-session at any depth, or the session that started it), or one chat.sideChat may open can open. The turn-state watch now mirrors acpmux's startedBy links, which the check uses.
  • Cloud VM chats open through that machine's daemon links (RemoteAgentSessionLinks): a remote chat's children and the session that started it open, as chat.sideChat opens them.
  • The page asks the app first and falls back to chat.sideChat only when the host does not support chat.offer.open; a chat.offer.refused answer is final, so a session outside the family never opens.

Written for commit bca0b18. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 30 commits October 20, 2026 16:55
…(failing)

send_message always queued behind the child's running turn, so a
parent's correction could sit unread for the whole turn, and the tool
said only "Sent.". get_session said "running" but not what the child
was doing, and start_session's reply carried shown:false, which a
parent read as "the user cannot see it".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
…step

- send_message delivers into the child's running turn by default
  (deliver: next_step, read at its next tool step, the way Claude Code
  reads a person's mid-turn message); after_turn queues, interrupt
  stops the turn. An agent that takes no prompt mid-turn gets it when
  the turn ends. The result says how it went (delivery, position,
  queueLength, steering), and the tool's text says when it is read.
- The running turn records what it does: its latest tool call's title
  and its plan's step in progress (turn.activity, turn.planStep).
  get_session/list_sessions rows carry state, stateSince and a doing
  line, masked like the other text.
- start_session answers with the row get_session reads, not a session
  summary whose shown:false meant only that no client had it open.
- A family message's provenance says who the sender is to the
  receiver (fromRole parent, subsession, ancestor, descendant) and its
  short name, for the receiver chat's label.
- send_message moves to hub/subsession_send.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
get_session and list_sessions rows gain waitingOn (question or
permission), and doing names the question. Only the person answers
either, in the app; the tool text tells the parent agent to relay it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
A parent's message to its sub-session (and the child's first prompt, and a
report from a child) arrives with provenance kind subsession_message. The
chat now draws it as an incoming message card from its sender: its short
name (else its full name), who it is to this chat ("Parent · coordinator",
"Sub-session · tests"), and an Open that opens that chat beside this one.

The catalog also gains the strings the sub-session cards and summary rows
use, in all 21 pane locales.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
Calls to the cmux-subsessions tools draw as their own cards instead of the
generic MCP row (a new "subsession" tool group, checked before the MCP one):
start_session as "Started sub-session tests" with its live state from the
session list (working with what it does now, needs input with what it waits
on, failed, done, N queued); send_message as "Sent to tests: <first line>"
with how it was delivered; wait_for_sessions as each sub-session's reply or
state. Running calls are one pending line; refused calls and the server's
other tools keep the generic row. A sub-session's name opens its chat
beside this one through chat.offer.open.

The summary card's Subagents section also lists the chat's sub-sessions and
theirs, indented, with state, unread dot, queue count and the doing or
waiting line; the card shows itself for a chat with sub-sessions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
SessionSummary gains waitingOn (question or permission) and
waitingPrompt (a question's first prompt), so the Summary panel and
sub-session cards say what a child needs without a tool call.
get_session's doing line reads them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
…next_step is opt-in

A steered prompt can cancel the tool call the receiving agent is running (the coordinator's
chat, 2026-10-20), so a sub-session message waits for the turn to end unless the sender passes
deliver: next_step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
… as its sender

Failing test first: today the daemon records no provenance for it, so a lane's
`acpmux send` report draws as the person's own prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
…n's message

When a connection proved it runs inside an acpmux session (its caller token)
and sends a prompt to a different session with no provenance of its own, the
daemon records {kind: session_message, from, fromName, to}. The person's
connections, prompts to the sender's own session and peer-forwarded prompts
stay unmarked, so the TUI and a shell's `acpmux send` remain the person's.
The agent pane draws it as a received message from that session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
…sion-visibility

# Conflicts:
#	webviews/src/agent-session/acpmux/App.tsx
#	webviews/src/agent-session/acpmux/summary/SummaryCard.tsx
The merge of feat-cmux-next lost compaction.failedWithError and reordered the
catalog. The catalog is now the base's text with this branch's 21 new strings
added at the end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
…sion-visibility

# Conflicts:
#	cmux-tui/crates/acpmux/src/hub/subsessions.rs
Failing test first: the agent pair guard counts agent_message and
subsession_message, not the new session_message, so a lane repeating the same
report to another session is never paused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
…hild never steers into its parent

- hub/agent_pairs.rs: a prompt a session sends from inside itself
  (session_message) counts toward its pair like any agent message.
- hub/subsession_send.rs: a child's deliver: next_step to its parent waits for
  the parent's turn to end instead, since a steer can cancel the parent's tool
  call; the tool text says so.
- hub/subsessions.rs: a blank line no longer splits KeptWorktree's doc comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15ba9-9614-7832-8e39-d8ca0407c193&host=cosmic-gray
…sion-visibility

# Conflicts:
#	webviews/src/agent-session/acpmux/App.tsx
#	webviews/src/agent-session/acpmux/conversation/IncomingMessageCard.gallery.ts
#	webviews/src/agent-session/acpmux/conversation/IncomingMessageCard.tsx
Conflict only in IncomingMessageCard.tsx header comment: kept both the
Details note from #22895 and the sub-session relation note.

Agent-Session: 01a15ba9-9614-7832-8e39-d8ca0407c193
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict only in App.tsx: base #22915 added queueWithdrawn to
VirtualTranscript, which this branch wraps in SubsessionContext. Kept
the wrapper and added the prop.

Agent-Session: 01a15ba9-9614-7832-8e39-d8ca0407c193
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sub-session card imports its own sheet, and build-agent-pane-web.sh
refuses a pane import it does not concatenate into the page styles.

Agent-Session: 01a15ba9-9614-7832-8e39-d8ca0407c193
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sion-visibility

# Conflicts:
#	cmux-tui/crates/acpmux/src/hub/mod.rs
The local key-wise merge driver had reordered the catalog, so GitHub's
text merge conflicted with every base change near it.

Agent-Session: 01a15ba9-9614-7832-8e39-d8ca0407c193
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
App.tsx: keep the SubsessionContext wrapper and take base's fallbackRepository prop.
Localizable.xcstrings: base's catalog as is, plus this PR's 21 new keys appended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray
Base #23044 lists the chats an agent started in the summary card
(ChildSessionRows). The card now lists this PR's sub-session rows, then
base's started-chat rows for chats that are not sub-sessions, so a chat
shows once; the counts take both.
App.tsx: base's transcript block (RunInBackgroundContext, drawnRows) inside
SubsessionContext; both subsessions and base's background/sessions props.
fake_agent.py: both new prompt notes. Localizable.xcstrings: base's catalog
plus this PR's 21 keys appended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray
…d waiting prompt masked

Review of #22827: turn.activity is a tool call's title, often a command
with its arguments, and goes to every peer in the session list, as do
turn.planStep and waitingPrompt. Mask them as turn.prompt is (hq#2509).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-663f-7650-b9f7-64d198c6b88b&host=cosmic-gray
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Default changes

No defaults change at bca0b18a09ed.

chat.offer.open refused a remote chat's children: its guard read only the
pane's scope and the app's mirrored startedBy links, while a remote chat's
links live in RemoteAgentSessionLinks. Use AgentPaneSideChat.holds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-aabc-7850-af29-f0655b1d2a1a&host=cosmic-gray
teamleaderleo and others added 2 commits October 21, 2026 09:40
On base the page fell back to opening it in the chat's tab; with the
refusal it opened nothing. The remote links know the parent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-aabc-7850-af29-f0655b1d2a1a&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (independent subagent, correctness first): SHIP at bca0b18

  • The first review BLOCKed 5737b81: on a Cloud VM chat, clicking a sub-session was refused, because the guard read only the app's mirrored links. a229875 fixed it by using AgentPaneSideChat.holds (the remote links), with a test.
  • bca0b18 also lets a Cloud VM chat open the session that started it; on base that click opened it in the chat's tab.
  • The reviewer checked the exhaustive switches, the hook init order, isolation, and that the tests aren't vacuous; all fine.

@teamleaderleo
teamleaderleo merged commit 106b611 into feat-cmux-next Oct 21, 2026
@teamleaderleo
teamleaderleo deleted the cmux/subsession-visibility-followups branch October 21, 2026 17:48
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Merged at bca0b18 after the independent review (SHIP at that head). Fleet: AgentPaneOfferOpenTests, AgentSubsessionFamilyTests, AgentSideSessionPaneTests all pass (cmux-ci step 9a9dde039001c4ae77bca849). CI: 79 passed; the only reds are gallery health (0/1/3), which are red on base too.

@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39647136624 at 106b611 failed: cmux-next checks (god files, concurrency, crash safety, l10n).
Those jobs last passed at 871cab6. This pull request is one of 2 merged since: #22683, #23159.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39647231050 at 6b26794 failed: cmux-next checks (god files, concurrency, crash safety, l10n).
Those jobs last passed at 871cab6. This pull request is one of 3 merged since: #22683, #23159, #23601.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39647649905 at d881a7e failed: cmux-next checks (god files, concurrency, crash safety, l10n).
Those jobs last passed at 871cab6. This pull request is one of 4 merged since: #22683, #23159, #23601, #22762.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39647862464 at 5818254 failed: cmux-next checks (god files, concurrency, crash safety, l10n).
Those jobs last passed at 8a06461. This pull request is one of 5 merged since: #22965, #23159, #23601, #22762, #22336.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

merge-audit: #23159 merged at bca0b18 without no green compile (changes Swift: its cmux-next Release compile (Xcode 26) was cancelled on this head; waits for a rerun (one base merge reruns it)); no risk tier label. #23665 reverts it. To keep this change, add the missing evidence for bca0b18 here; the next audit pass then closes #23665. Otherwise review and land #23665.

@github-actions

Copy link
Copy Markdown
Contributor

Shipped in:

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (subagent, correctness first): SHIP at bca0b18

Evidence for merge-audit's revert #23665. This merge's cmux-next Release compile was cancelled, not failed.

  • Compile: cmux-ci compile-check --ref bca0b18a09ed301386e4f60c90b132028a4239c2 --app passed on the build fleet in 68 s (job b2d2b58372792c505e263a93). This stands in for a Release compile with no verdict.
  • Review: an independent subagent with fresh context read the diff from the mirror and ran no builds. The squash (106b611) matches the head exactly: 15 files, +305/-15. It checked request decoding, the chat.offer.refused path back to the page, the fallback on older hosts, the safe description (it logs no session id), MainActor and weak captures, the 64-step cycle bound on the family walk, and tests that run through the real decode and respond path. It found no blocker.
    • Follow-up, should-fix: isFamily accepts the direct parent but refuses a grandparent (ancestor). A click on a grandparent's chip is now a quiet dead click, where before it opened that chat in place.
    • Nit: before the turn-state watch sends its first page, a click on a child that isn't in transport.sessions gets no feedback.
  • Still sound on current feat-cmux-next (81f78a6): later commits build on this code. cmux NEXT: a click on a chat its agent made with acpmux new opens it #23764 extends startedBy with a spawnedBy fallback, CmuxNext: delete a chat's uploads with it, and any upload after 30 days #23580 adds deleted to the feed change case, and cmux NEXT Needs you: a jump opens the waiting chat in its own tab, never replacing the chat on screen #23849's AgentPaneAskOpen calls header.showSession. A revert would conflict with them, so the merge should stay.
  • Risk tier: risk:normal, already set.

Agent session: Air-Blue

This branch was previously deployed

4 inactive (1 outdated) deployments
artifacts — bca0b18a Deployed Oct 21, 2026 by teamleaderleo
sentry-dsyms — bca0b18a Deployed Oct 21, 2026 by teamleaderleo
ci-routing-agent — bca0b18a Deployed Oct 21, 2026 by teamleaderleo
cmux-tui-merge-tree — a2298753 Deployed Oct 21, 2026 by teamleaderleo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant