feat(desktop,host): the open plan's conversation is read on the agent tab's held read, beside its work, for the Call tab to switch to (one runtime §3.7 B)

Merged
DA
6d ago
+860-63

One-agent-runtime §3.7, PR B of the one-held-read plan. Stacked on A (#2436); merge after it. This is the desktop host side, additive and inert: nothing in the renderer reads the new field yet, so nothing user-visible changes. PR C switches the Call tab to it.

Compatibility

  • The renderer still reads planning.work, planning.workEarlier and the plan.work frame exactly as today.
  • showWork, readWork, readEarlierWork, withPage and every plan.work path are untouched.
  • No drawing file changed.

What

  • planning.conversation (optional on planningViewSchema): the open plan's planning conversation in the agent tab's own page shape (codingAgentMessagesAnswerSchema).
  • compose-planning.ts: one held read for the open plan, a FiberHandle fiber with no raw timers.
    • Start: it reads the newest page when the plan opens, starts or refreshes. A new start replaces the standing read.
    • Follow: it follows after=cursor while the last page's status, or a worker the page names, is STARTING or RUNNING.
    • Park and wake: it parks on an idle page, because the server answers an idle conversation at once. An activity frame or call end for the open plan wakes it from the cursor it holds. A wake that lands while a read is out keeps that read from parking.
    • Merging: pages merge by message id. Workers and their titles merge by call id, newest kept, because a page names only some of them.
    • Retries: a failed read retries on a capped exponential backoff (1 s doubling to 30 s). A NOT_FOUND, such as an older service, parks it.
    • Stop: switching plans, leaving the plan, deleting it or signing out stops it.
  • Earlier pages: the earlier act prepends the page before earlier without moving the forward cursor.
  • planning.messages: serves a plan's page or a planning worker's page to the caller, for the worker tab C will draw.
  • Gateway and acts: planning.conversationEarlier and planning.messages in the protocol. PLANNING_CONVERSATION_EARLIER and PLANNING_MESSAGES are new acts with router rows; they are panel-only and refused from the voice window. host-operator.ts wires them.
  • applyMessagesPage moved unchanged from the renderer's coding-agent-model.ts to @sidecar/hosted/coding-agent-wire, so the host and the agent tab share one page merge. Its test moved with it. The only renderer edits are the imports.
  • compose-host.ts: one HostedCodingAgentClient is now shared by planning and coding agents.

Tests

  • packages/host/src/compose-planning.test.ts, using a fake planner client and TestClock:
    • opening a plan publishes its newest page, and an idle conversation isn't asked again;
    • a held read rewrites an amended journal in place and appends new rows;
    • the earlier page prepends without moving the forward cursor;
    • opening another plan stops the old read;
    • a failed read retries after the backoff and not before;
    • an activity frame wakes a parked read;
    • workers and titles join by call id.
  • apps/desktop/src/main/ipc/planning-acts.test.ts: a worker's page is read from a panel and refused from the voice window.
  • The moved merge test is in packages/hosted/src/coding-agent-wire.test.ts.
  • ./scripts/check.sh exits 0.

Notes

  • Bigger planning frame: conversation rides every planning.changed frame beside work until the contract PR drops work. This is the main-to-renderer frame on the Mac, not network traffic.
  • The code read isn't wired yet: readCode stays unused until C swaps the Code tab's source.
  • Mac UI: CI cannot verify the Mac, and ./scripts/verify.sh was not run (LUKE-159). Nothing visible changes in this PR.

🤖 Generated with Claude Code


Devin Review