diff options
| author | Adam Malczewski <[email protected]> | 2026-06-21 02:19:54 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-21 02:19:54 +0900 |
| commit | d98a63ce17519983dcf58c27432723e2f4b96e75 (patch) | |
| tree | 21a4e043d040984aa62fd2ba81ca3349ce01f5c4 /backend-handoff.md | |
| parent | 9c90105b6cfede0f3327169718300c649bb0531a (diff) | |
| download | dispatch-web-d98a63ce17519983dcf58c27432723e2f4b96e75.tar.gz dispatch-web-d98a63ce17519983dcf58c27432723e2f4b96e75.zip | |
feat(chat): message queue + steering — mid-turn injection at tool-result boundaries
Consume the message-queue + steering handoff ([email protected], [email protected]).
Re-pinned file: deps + re-mirrored .dispatch/*.reference.md.
- fold steering AgentEvent into the transcript as a provisional user bubble
(after the tool-result it followed; no de-dup — the queue surface carried it)
- add rendererId: "message-queue" custom renderer (pure parser + MessageQueueList)
rendered as a compact panel above the Composer (hidden when queue is empty)
- add ChatStore.queueMessage / AppStore.queueMessage — sends chat.queue WS op
(trim/validate non-empty; auto-starts a turn if idle)
- Composer switches to chat.queue while generating (button → Queue, placeholder
→ Steer the conversation...)
- exhaustiveness guards updated for steering + chat.queue
- carry-to-new-turn needs no special handling (normal new turn)
664 tests green.
Diffstat (limited to 'backend-handoff.md')
| -rw-r--r-- | backend-handoff.md | 42 |
1 files changed, 32 insertions, 10 deletions
diff --git a/backend-handoff.md b/backend-handoff.md index 7c7da05..5b54f2d 100644 --- a/backend-handoff.md +++ b/backend-handoff.md @@ -5,18 +5,38 @@ > **From:** dispatch-web orchestrator · **To:** arch-rewrite orchestrator · **Courier:** the user. > `lsp` does NOT span the repos (AGENTS.md § Backend seam) — every cross-repo ask flows through here. -_Last updated: 2026-06-12 (reasoning-effort handoff consumed). **FE is current on +_Last updated: 2026-06-21 (message-queue + steering handoff consumed). **FE is current on consumed: surfaces + WS, conversation transcript/metrics, tabs + model selector, cache-warming (incl. authoritative timer + retention + cache-rate fix + the CR-4 lifecycle below), **per-conversation cwd + LSP status**, **context size**, **turn continuity + multi-client live -view**, the **chat limit + CR-5 history windowing**, and the **reasoning effort -(thinking-depth knob)** (below). +view**, the **chat limit + CR-5 history windowing**, the **reasoning effort +(thinking-depth knob)**, and the **message queue + steering** (below). **Open asks: NONE.** CR-1/CR-2/CR-4/CR-5 all RESOLVED ✅ (see §2); §3 lists likely next asks. **CR-3 (watcher couldn't see the USER prompt until seal) → RESOLVED ✅** — backend shipped the `user-message` turn event; FE re-pinned + consumption live. The cwd/LSP draft-path verification (`backend-handoff-cwd-lsp.md`) came back **all ✅ confirmed**._ +**Message-queue + steering handoff (`frontend-message-queue-handoff.md`) → CONSUMED ✅.** +Re-pinned `[email protected]→0.8.0` + `[email protected]→0.12.0` (`ui-contract` unchanged — +the queue uses the existing `custom` surface field kind); re-mirrored both +`.dispatch/*.reference.md`; added "message queue" + "steering" to FE `GLOSSARY.md`. FE work: +(a) `core/chunks/reducer.ts` folds the new `steering` `AgentEvent` into the transcript as a +provisional user bubble (after the tool-result it followed; no de-dup — the queue surface, not +the transcript, carried the pending message); `core/wire/conformance.ts` exhaustiveness guards +updated for `steering` + `chat.queue`; (b) a `rendererId: "message-queue"` custom renderer +(`surface-host/logic/message-queue.ts` pure parser + `MessageQueueList.svelte`) renders +`QueuePayload.messages` (`QueuedMessage[]`); (c) the `message-queue` surface is pulled out of +the generic Extensions sidebar list and rendered as a compact panel above the Composer (only +when the queue is non-empty — an idle queue is hidden); (d) `ChatStore.queueMessage(text)` + +`AppStore.queueMessage(text)` send `chat.queue { conversationId, text }` (trim/validate +non-empty client-side too); (e) the Composer switches to `chat.queue` while `generating` +(button label → "Queue", placeholder → "Steer the conversation..."). Carry-to-new-turn needs +no special handling (surfaces as a normal new turn). **NOT yet live-probed** — the handoff +flags the live end-to-end steering flow (a real `chat.queue` → tool-call → `steering` event +against a tool-calling model) as not yet exercised; worth a live smoke. 664 tests green. NO +new backend ask._ + **Reasoning-effort handoff (`frontend-reasoning-effort-handoff.md`) → CONSUMED ✅ (curl-probed live: GET null on unseen id · PUT `xhigh` → echo + sticky GET · bad level → 400 listing the ladder · CORS preflight allows PUT).** Re-pinned `[email protected]→0.7.0` + @@ -81,13 +101,13 @@ backend ask — but the max-limit denominator is now a live FE need; see §3. ## 1. Pinned backend contracts (consumed by the FE) | Package | Used for | |---|---| | `@dispatch/ui-contract` | surfaces + surface WS protocol | -| `@dispatch/wire` | `Chunk`/`StoredChunk`(+`seq`)/`ChatMessage`/`AgentEvent`/`TurnSealedEvent`/`Usage`/`StepId` + metrics: `StepMetrics`/`TurnMetrics`, `usage.stepId`, `step-complete`, `done.durationMs`/`done.usage`, `tool-result.durationMs`, **`done.contextSize`/`TurnMetrics.contextSize`**, **`ReasoningEffort`** | -| `@dispatch/transport-contract` | `ChatRequest`(+`reasoningEffort`)/`ModelsResponse`/`ConversationHistoryResponse`/`ConversationMetricsResponse` + `WarmRequest`/`WarmResponse` + `CwdResponse`/`SetCwdRequest` + `ReasoningEffortResponse`/`SetReasoningEffortRequest` + LSP (`LspStatusResponse`/`LspServerInfo`/`LspServerState`) + WS chat ops + `WsClientMessage`/`WsServerMessage` | +| `@dispatch/wire` | `Chunk`/`StoredChunk`(+`seq`)/`ChatMessage`/`AgentEvent`/`TurnSealedEvent`/`Usage`/`StepId` + metrics: `StepMetrics`/`TurnMetrics`, `usage.stepId`, `step-complete`, `done.durationMs`/`done.usage`, `tool-result.durationMs`, **`done.contextSize`/`TurnMetrics.contextSize`**, **`ReasoningEffort`**, **`QueuedMessage`/`QueuePayload`/`TurnSteeringEvent`** | +| `@dispatch/transport-contract` | `ChatRequest`(+`reasoningEffort`)/`ModelsResponse`/`ConversationHistoryResponse`/`ConversationMetricsResponse` + `WarmRequest`/`WarmResponse` + `CwdResponse`/`SetCwdRequest` + `ReasoningEffortResponse`/`SetReasoningEffortRequest` + **`QueueRequest`/`QueueResponse`/`ChatQueueMessage`** + LSP (`LspStatusResponse`/`LspServerInfo`/`LspServerState`) + WS chat ops + `WsClientMessage`/`WsServerMessage` | Endpoints in use (HTTP **24203**, WS **24205**, CORS `*` incl. `PUT`): `POST /chat` (NDJSON) · `GET /models` · @@ -95,12 +115,14 @@ Endpoints in use (HTTP **24203**, WS **24205**, CORS `*` incl. `PUT`): `GET /conversations/:id/metrics` · `GET`/`PUT /conversations/:id/cwd` · `GET`/`PUT /conversations/:id/reasoning-effort` (sticky thinking-depth; `null` ⇒ default `high`) · `GET /conversations/:id/lsp` · `POST /chat/warm` · `POST /conversations/:id/close` (explicit -tab-close: abort turn + stop/disable warming) · WS `chat.send`→`chat.delta` · -WS `chat.subscribe`/`chat.unsubscribe` (watch a conversation's turns without sending; replay + live). +tab-close: abort turn + stop/disable warming) · **`POST /conversations/:id/queue`** (enqueue +steering message; auto-starts a turn if idle) · WS `chat.send`→`chat.delta` · +WS `chat.subscribe`/`chat.unsubscribe` (watch a conversation's turns without sending; replay + live) · +**WS `chat.queue`** (enqueue steering; fire-and-forget — surface updates on success). Mirrored in-repo for headless agents: `.dispatch/{ui-contract,wire,transport-contract}.reference.md` (regenerate on any contract bump; all current as of `[email protected]` / -`[email protected]` / `[email protected]`). +`[email protected]` / `[email protected]`). ## 2. Open asks FOR THE BACKEND |
