Transcript: a quiet turn the agent ran on its own is one muted line

Merged
TE
4d ago
+597-4

Leo, about a chat message that said only "Nothing new for you yet.":

why do you guys keep sending me this/get pinged to send this

Offered "a turn you didn't start that has nothing new to tell you shouldn't show up as a message or ping you", he said "yeah".

This is the webview half. It pairs with the acpmux PR that emits the background_turn records, #30228; until that lands, no log has these records and nothing changes.

Record contract (acpmux writes it, the pane reads it)

  • dir: "mux", kind: "background_turn", msg: {"state":"started","id":"<string>","trigger":"task_notification"|"scheduled"|"system"}
  • the turn's own session/update records
  • {"state":"ended","id":<same>,"trigger":..., "quiet": bool, "reason":"result"|"prompt"|"exit"}. With reason:"prompt" the end record comes before the prompt's user_message/turn_started; everything after belongs to that real turn as today.
  • quiet is decided in Rust; the pane never decides it again. A started with no ended is still running. Logs without the records draw exactly as before.

What changed

  • direct.ts: a background_turn started record makes one backgroundTurn row (its line); OrderedRows tags every row an event between started and ended made with inBackground: <id> (by the creating event's seq, so later updates keep it; prompts and local rows never). The ended record updates the line (running: false, quiet, reason). An older page whose ended record is heard before its started record keeps how it ended. The assistant segment is ended at both records so text never runs across the boundary.
  • conversation/backgroundTurns.ts (new): foldBackgroundTurns draws a quiet or still-running turn as its line alone while closed, the line then its rows while open; a non-quiet turn draws no line and its rows as today. Applied in turns.ts to each turn's rows (background turns sit in the previous turn's tail, after its summary) and to rows before the first prompt; the shaped-turn cache keys on which lines are open.
  • conversation/BackgroundTurnLine.tsx (new): the muted line on TranscriptActivityRow ("Background task finished" / "Scheduled check" / "Background update"), the working Spinner while running, a clock once ended, chevron and aria-expanded. Open state is the transcript's own fold set (onToggleActivity, kept per chat like "Worked for" and subagent groups), so it survives virtualization. Row estimate 37px like other one-line notes.
  • No "Worked for", footer or turn summary for a background turn (it has no prompt row, so it never opens a turn, anchors a fold or becomes the Retry prompt). Turns the person started are untouched; no component removed or switched off.
  • Three new strings in Localizable.xcstrings, all locales.

Tests

  • conversation/backgroundTurns.test.tsx: quiet line closed and opening to its rows (rendered closed, aria-expanded="false"), non-quiet rows as they came with no line, running line with the spinner, no-op without records.
  • test/agent-pane-background-turns.test.ts (through AcpmuxDirectClient on a fake daemon): quiet turn tagged and folded with the person's turn unchanged, non-quiet turn with no extra footer or Worked for, running turn then ended quiet, reason:"prompt" end followed by a normal turn, old log with no records unchanged.
  • Gallery: transcript / background-turns (a person turn, a quiet background turn, a non-quiet one; play opens the quiet line).
  • Fleet: the new tests plus transcript-completed-fold, agent-pane-compaction-rows, TurnRows, incomingMessage, rowEstimates, messageLiveLine: 148 pass, 0 fail; vp check typecheck clean (cmux-ci job 1fdcf47b5998d63cef5406e8).

Design spec

Surface chat-transcript: leo-workspace design/chat-transcript.md; this PR adds its rule (a quiet turn the agent ran on its own is one muted, closed line).

Agent session: agent session

šŸ¤– 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

Turns the agent ran on its own draw as one muted line instead of a chat message when they have nothing new to say. #30228 makes them stop pinging. Until the paired acpmux PR lands, no log has these records and nothing changes.

acpmux brackets each promptless turn with background_turn started/ended records and decides whether it was quiet. The pane folds a quiet or still-running turn into a single "Background task finished" / "Scheduled check" / "Background update" line that opens to reveal its rows; a non-quiet turn draws as today.

  • The line's open state lives in the transcript's per-chat fold set, so it survives virtualization.
  • Turns the person started, and logs from before these records, draw exactly as before.
  • When a prompt interrupts a running background turn, everything after the prompt is a normal turn, as today.