feat(maestro): server-side prompt queue for structured sessions
Prompts sent while a turn is active are queued on the daemon instead of in the browser's React state. The queue is persisted in the session sidecar and drained as the next turn when the current one completes. Fixes the bug where queued messages were delivered to the wrong session when switching focus in the UI: the client-side queue captured the new sessionId in its drain closure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
cd5bbcfcb1
commit
ebe25a1a55
+17
-12
@@ -353,21 +353,26 @@ thing a callback can be attributed to, so at most one turn may ever be
|
||||
in flight, and a cancelled turn must fully settle before the next prompt is
|
||||
allowed to claim the daemon's idea of "the active turn."
|
||||
|
||||
- Web clients may collect a visible FIFO of operator messages while a turn is
|
||||
active. They submit each queued message only after the projected turn becomes
|
||||
idle; the queue is client-local and unsent messages do not survive a reload.
|
||||
- Prompts are accepted at any time: if the session is idle the daemon dispatches
|
||||
immediately (`204`); if a turn is active the prompt is appended to a
|
||||
server-side FIFO (`202 Accepted`, capped at 20 entries, `429` when full).
|
||||
When the active turn completes, all queued prompts are concatenated with
|
||||
`\n\n` and dispatched as the next turn. The queue is persisted in the session
|
||||
sidecar and survives daemon restarts — clients never queue locally.
|
||||
`ReducedState.queued_prompts` contains the current queue so every connected
|
||||
client can render the pending messages.
|
||||
- A submitted prompt is appended as a durable `User` row before it claims the turn and
|
||||
dispatches to ACP, so the transcript never depends on an adapter echoing the
|
||||
operator's input. If that append fails, the prompt is not dispatched. While a
|
||||
turn is active, a second prompt gets `409 turn_in_flight` rather than
|
||||
queuing in the daemon — one active turn per session, never a hidden
|
||||
server-side queue of prompts.
|
||||
operator's input. If that append fails, the prompt is not dispatched.
|
||||
- Cancel sends `session/cancel`, clears any pending approval as `Cancelled`,
|
||||
and ends the turn's client-visible lifecycle immediately. If the cancelled
|
||||
turn was still *dispatched* to ACP (waiting on its response), the daemon
|
||||
cannot simply forget it: ACP has not confirmed the cancel yet, and a new
|
||||
prompt claiming "the active turn" while the old one is still outstanding
|
||||
would make the next permission callback ambiguous between the two.
|
||||
and ends the turn's client-visible lifecycle immediately. The prompt queue
|
||||
is preserved: after the cancelled turn settles, the queued prompts drain as
|
||||
the next turn. If the session dies instead (settlement timeout), the queue
|
||||
is cleared. If the cancelled turn was still *dispatched* to ACP (waiting on
|
||||
its response), the daemon cannot simply forget it: ACP has not confirmed the
|
||||
cancel yet, and a new prompt claiming "the active turn" while the old one is
|
||||
still outstanding would make the next permission callback ambiguous between
|
||||
the two.
|
||||
- So a cancelled-but-still-dispatched turn must **settle within
|
||||
`CANCEL_GRACE` (500ms)** — either ACP acknowledges the prompt (however it
|
||||
resolves) or that grace period runs out. If it settles in time, the next
|
||||
|
||||
Reference in New Issue
Block a user