agent chat: other sessions' messages are one inline line each, whole when opened

Merged
TE
6d ago
+1337-1790

Another agent's message in the agent chat is now one quiet disclosure line where it arrived, never a card cut short and never one group at the bottom. Opening the line shows the whole message under it, and the line stays where it was.

Leo, 2026-10-29:

  1. "why the f*** did you guys ff***ing ellipsis EVERYTHING WITH NO WAY TO EXPAND WHAT WAS SAID TO SEE"
  2. "AND IT'S SO F***ING AWKWARD TO SEE THEM THERE AT THE BOTTOM SINCE WHEN I EXPAND THEM IT JUMPS ME DOWN AND WHEN I COLLAPSE IT JUMPS ME UP"
  3. "LOOK AT THESE / LEARN FROM CLAUDE CODE"
  4. "the "read after this turn" stuff gets a bit redundant and also can't these be batched to you?"

Copied from Claude Code: inline placement, the one-line disclosure, the full text on expand, and quiet gray/white type. Kept: our colours and glass. Message delivery is unchanged.

What changed

  • Placement: each message is its own row where it arrived. Delivered messages sit between the replies they came between. A steer the agent hasn't read sits in the running turn. A message queued behind the turn sits after it. The "N messages from M sessions" group card is removed (WaitingMessagesCard, waitingMessages.ts, the WAITING_MESSAGES row kind, and Find's member path).
  • The line: the shared DisclosureRow from #27262 shows the sender's mark, the sender, the message's first line muted, and a chevron. On hover: Open chat (its tooltip names the chat and its machine), Reply, and the existing actions.
  • Expand: a light panel shows "From: ", then the whole message as Markdown, wrapped and never cut, then images, Details (the envelope) and the reply editor. The eight-line cut, Show more and Minimal Mode's separate card are gone.
  • No jumps: the transcript holds the line on its pixel while the panel opens or closes (useAnchoredCollapse / foldAnchor). The browser test measures 0 px of movement mid-view and at the pinned end.
  • Queue state: "Read after this turn" shows once, above the first queued message, not on each. A steer keeps its own small "Read after this step" tag, since its state differs.
  • State: open/closed is page-lived per chat and row (incomingOpened.ts, which the row estimate reads), and Details uses useTranscriptDisclosure, so scrolling a row out of the virtualized transcript keeps both.
  • DisclosureRow: additive, optional props: open/onOpenChange, animates, toggleLabel, actions, className, openTitle, fadeIn. The defaults are unchanged for the sub-session card.

On "can't these be batched": acpmux already delivers messages queued behind a busy turn as one turn (hub/digest.rs take_joined / digest_blocks; test tests/hub_integration/agent_digest.rs, 12/12 at 9d8337eb: https://github.com/manaflow-ai/cmux/actions/runs/40838848815). The redundancy was only in the UI, so there is no daemon change.

Supersedes the received-message parts of #26373 and #25683 (same area, open, owner unreachable; left for their owner to close).

Takes back #26749: Leo asked for messages inline where they arrived ("AND IT'S SO F***ING AWKWARD TO SEE THEM THERE AT THE BOTTOM SINCE WHEN I EXPAND THEM IT JUMPS ME DOWN AND WHEN I COLLAPSE IT JUMPS ME UP"), so the one card for several waiting messages at the bottom is removed.
Takes back #26980: the fold of consecutive received messages is replaced by one disclosure line per message, for the same reason.

DisclosureRow came from #27262, which has now landed, and this branch merges base in. The shared row keeps #27262's state, which lasts across virtualization (useTranscriptDisclosure), and its Find reveal. An incoming message passes open/onOpenChange in place of disclosureKey, because the row estimate reads its state (incomingOpened.ts).

Gallery

agent-pane.incoming-message-disclosure:

  • A (ships): the sender, then the first line muted.
  • B: Claude Code's "Received a message from" wording.
  • C: A plus a line count.
  • D: the sender alone.

A also has an opened state. B, C and D are drawn only in the gallery. The before/after stills are in the gallery advisory run's artifact on this PR (the status comment links it). They stay out of the PR text, as the repo does for every gallery run.

Tests

  • New: test/incoming-message-disclosure-browser.test.ts (real pane, Chromium):
    • (a) no truncation without expand: a 40-paragraph message is one 26 px line; opened, all 40 paragraphs show and nothing is clipped (scrollHeight == clientHeight);
    • (b) no scroll jump: the line's top is 363.5 → 363.5 → 363.5 mid-view and 535.5 → 535.5 → 535.5 when it is the last row and the view is pinned;
    • (c) inline placement: three senders give three rows between two replies, and a queued run shows one "Read after this turn".
  • Updated: the old-DOM tests across 16 files, now matching the new rows. 31 of them fail on a62e77b8 before the update.
  • Before the merge: 220 pass, 0 fail across 17 files: https://github.com/manaflow-ai/cmux/actions/runs/40838311501
  • After merging base (736f54c2):
  • Deleted, because their subject is gone: the eight-line cut and Show more tests, and the Minimal Mode-specific card tests.

Observed / Not run

  • Observed: the runs above. An independent review of the merge resolution (a different model) returned SHIP.
  • Not run:
    • test/gallery-coverage.test.ts, which fails on stale entries for files this PR doesn't touch;
    • no native build is needed (webview-only).
  • Known edge: a scroll made by script within about 150 ms after a fold ends is put back by the hold. A wheel or touch releases the hold at once, so readers don't hit it.

Changelog

Changed: Messages from other agents and sessions show as one line where they arrived and open to the full message without moving the view.

Agent session: agent session

šŸ¤– Generated with Claude Code