feat(desktop): the message box under a coding agent's tab, steering or queueing (O2)

Merged
DA
11d ago
+2085-162

What

A composer pinned to the bottom of each coding agent's tab, built on AI Elements' PromptInput and Queue (copied into apps/desktop/src/renderer/ai-elements/ and restyled to Luke's tokens), over O1's POST /api/agents/{id}/messages.

  • The box: an autosizing textarea (one line, growing to about eight, then scrolling). Placeholder "Message the agent…" while it runs, "Ask for changes or a follow-up…" when it has finished. Enter sends, Shift+Enter is a new line, Escape leaves the box. Focus lands in the box as the tab opens unless the developer is typing elsewhere.
  • While a turn runs: Enter steers (the agent sees it at its next step); ⌥Enter queues it for after the turn, as does the menu on the send's chevron ("Send now" / "Queue for after this turn"). A one-line keycap hint under the box says the two keys.
  • Queued messages stack above the box as compact rows marked "Queued", in order, read off metadata.delivery (O1's awaitedDeliveryOf), until their turn takes them and they join the transcript. No ✕: eve 0.74 can't withdraw a queued line.
  • Sent messages join the transcript at once as the developer's bubble and are reconciled against the service's own row when it lands (the POST answers no row id, so a line is read back by the first developer's row it did not know holding its words). On failure the words come back into the box with the reason and Retry; agent-retired closes the box with the host's reason in its place.
  • Idle: a send opens a new turn; the status goes back to Running and the Stop comes with it.
  • One Stop: the header's Stop is gone; the composer's square Stop stands beside the send while a turn runs (O4's pull-request pill and ⋯ menu stay in the header).
  • The plumbing: CODING_AGENTS_MESSAGE act → codingAgents.message gateway method → HostedCodingAgentClient.message → the route, with the three new refusals (message-too-long, agent-not-ready, agent-retired) as call failures with their own words.
  • O5's notice ledger is keyed by (agent, turnId), as the orchestrator asked: an ended turn read running again under the same id (a message awaiting its turn) is the old run, announces nothing, and keeps the plan watched until the next turn's id arrives, whose end is the next notice. O4's "opened #N" wiring is kept.

Tests

Through the public components (agent-composer.test.ts mounts AgentTabView over useAgentComposer): Enter sends steer while running; ⌥Enter and the menu queue; idle sends open a turn; Shift+Enter is left to the browser; queued rows show and clear; optimistic line then reconcile; failure keeps the text with Retry; one Stop; focus and Escape. Plus the model's pure functions, onMessage on the agents control, the ledger's two-turn test (finish, message, new id, finish → two notices), and each plumbing layer (client, host composer, act rows).

Goldens: the view's failure enum grew by the three new slugs; one new golden for codingAgentMessageParamsSchema.

Verification

./scripts/check.sh exits 0. CI builds nothing for the Mac and could not verify the UI; Dean waived ./scripts/verify.sh for this rollout, so no visual evidence is attached.

🤖 Generated with Claude Code


Open workspace in Conductor


Devin Review