From d98a63ce17519983dcf58c27432723e2f4b96e75 Mon Sep 17 00:00:00 2001
From: Adam Malczewski <github@tradam.dev>
Date: Sun, 21 Jun 2026 02:19:54 +0900
Subject: feat(chat): message queue + steering — mid-turn injection at
 tool-result boundaries
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Consume the message-queue + steering handoff (wire@0.8.0, transport-contract@0.12.0).
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.
---
 .../surface-host/ui/MessageQueueList.svelte        | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 src/features/surface-host/ui/MessageQueueList.svelte

(limited to 'src/features/surface-host/ui/MessageQueueList.svelte')

diff --git a/src/features/surface-host/ui/MessageQueueList.svelte b/src/features/surface-host/ui/MessageQueueList.svelte
new file mode 100644
index 0000000..12de970
--- /dev/null
+++ b/src/features/surface-host/ui/MessageQueueList.svelte
@@ -0,0 +1,22 @@
+<script lang="ts">
+	import { parseMessageQueuePayload } from "../logic/message-queue";
+
+	let { payload }: { readonly payload: unknown } = $props();
+
+	// Parse defensively; an unparseable payload yields null → render nothing
+	// (graceful skip, per the custom-field contract).
+	const data = $derived(parseMessageQueuePayload(payload));
+</script>
+
+{#if data !== null && data.messages.length > 0}
+	<ul class="flex flex-col gap-1 text-sm">
+		{#each data.messages as msg (msg.id)}
+			<li class="rounded-box bg-base-200 px-3 py-2">
+				<p class="whitespace-pre-wrap">{msg.text}</p>
+				<time class="text-xs opacity-50" datetime={new Date(msg.queuedAt).toISOString()}>
+					{new Date(msg.queuedAt).toLocaleTimeString()}
+				</time>
+			</li>
+		{/each}
+	</ul>
+{/if}
-- 
cgit v1.2.3

