From 830579ab5099ec2aad864080185f4fb6844b7ff2 Mon Sep 17 00:00:00 2001 From: ZiyaZhang <199893125+ZiyaZhang@users.noreply.github.com> Date: Mon, 20 Jul 2026 01:59:23 -0700 Subject: [PATCH] feat(desktop): per-session mid-turn message queue (enqueue-on-inflight, drain-once-per-turn, strip UI) --- examples/desktop/README.md | 11 + .../qa-msg-queue/01-inflight-two-queued.png | Bin 0 -> 76473 bytes .../02-after-turn-end-one-left.png | Bin 0 -> 76168 bytes .../scripts/qa-cdp-shoot-msg-queue.mjs | 243 ++++++++++++++++++ examples/desktop/src/renderer/index.html | 8 + .../desktop/src/renderer/msg-queue-model.js | 142 ++++++++++ examples/desktop/src/renderer/renderer.js | 236 ++++++++++++++++- examples/desktop/src/renderer/style.css | 72 ++++++ examples/desktop/test/msg-queue-model.test.js | 145 +++++++++++ .../desktop/test/renderer-collisions.test.js | 6 + examples/desktop/test/renderer-harness.js | 1 + .../desktop/test/renderer-msg-queue.test.js | 196 ++++++++++++++ 12 files changed, 1052 insertions(+), 8 deletions(-) create mode 100644 examples/desktop/docs/qa-msg-queue/01-inflight-two-queued.png create mode 100644 examples/desktop/docs/qa-msg-queue/02-after-turn-end-one-left.png create mode 100644 examples/desktop/scripts/qa-cdp-shoot-msg-queue.mjs create mode 100644 examples/desktop/src/renderer/msg-queue-model.js create mode 100644 examples/desktop/test/msg-queue-model.test.js create mode 100644 examples/desktop/test/renderer-msg-queue.test.js diff --git a/examples/desktop/README.md b/examples/desktop/README.md index 42250e4796..712645211d 100644 --- a/examples/desktop/README.md +++ b/examples/desktop/README.md @@ -104,6 +104,17 @@ a toggle), and `context/message` / `steering/message` events surface as and why. A `Cancel` button appears mid-turn and cuts the stream via `session/cancel`. +The wire accepts only one in-flight prompt per session, so pressing Enter +while a turn is running **queues** your message instead of erroring: a strip +above the composer shows one chip per queued message with a `queued N` +counter, and each chip lets you edit the text inline, delete it, or bump it to +the front (**send next**). When the turn ends, the head of the queue is sent +automatically — one message per turn completion — so a burst of follow-ups +plays out in order without you babysitting each turn. The queue is +per-session (switching sessions shows that session's queue), survives a +cancelled turn, and is cleared with a notice if the runtime restarts or you +switch profiles. + ![](docs/readme-shots/03-chat.png) #### Session Tree diff --git a/examples/desktop/docs/qa-msg-queue/01-inflight-two-queued.png b/examples/desktop/docs/qa-msg-queue/01-inflight-two-queued.png new file mode 100644 index 0000000000000000000000000000000000000000..605b4d848051921230e14357153f0e44806d20d8 GIT binary patch literal 76473 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 fe*5 literal 0 HcmV?d00001 diff --git a/examples/desktop/docs/qa-msg-queue/02-after-turn-end-one-left.png b/examples/desktop/docs/qa-msg-queue/02-after-turn-end-one-left.png new file mode 100644 index 0000000000000000000000000000000000000000..5b22de61dbd004fd46e6121776b5acb5fadab9a2 GIT binary patch literal 76168 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%0Adz?y>ZadO*>3w2KhDHZW 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 uq; isolates Chromium userdata. +// 2. DSH_DESKTOP_HOME= isolates the main-process config root so we +// never write into ~/.dsh-desktop. +// 3. own CDP port (≥9290) so a lingering Electron helper from another +// lane's shoot can't hijack our DevTools endpoint. +// +// Why drive via the seam and not a real prompt: the message-queue behaviour +// is a pure renderer concern (enqueue-on-inflight + auto-drain on turn/end). +// `window.__dshRenderer` exposes send() + onSessionEvent() + listMsgQueue() +// ungated, so we can create an in-flight turn, enqueue two messages, and step +// the turn to completion deterministically — no daemon round-trip, no model +// key. The keyless daemon-echo include still boots the runtime so the shell +// is in its normal chat state. + +import { spawn } from 'node:child_process' +import { existsSync, mkdirSync, writeFileSync, rmSync, statSync } from 'node:fs' +import { resolve, join } from 'node:path' +import { setTimeout as sleep } from 'node:timers/promises' +import { tmpdir } from 'node:os' + +const WORKTREE = resolve(process.env.DSH_WORKTREE || process.cwd()) +const PARENT = resolve(process.env.DSH_REPO || '/Users/ziya/harness/dsh-desktop-demo') +const ELECTRON = join(PARENT, 'node_modules/.bin/electron') +const CDP_PORT = Number(process.env.DSH_MSG_QUEUE_PORT || 9290) +const OUTDIR = join(WORKTREE, 'docs/qa-msg-queue') + +if (!existsSync(ELECTRON)) { + console.error(`electron binary not found at ${ELECTRON}`) + process.exit(2) +} +mkdirSync(OUTDIR, { recursive: true }) + +function seedHome(dshHome) { + // Minimal keyless overlay + config + onboarded sentinel so the first-run + // modal doesn't fire and steal the window / rewrite our seed. daemon-echo + // is the keyless demo runtime; we never send a real prompt through it. + const seedOverlay = [ + '# QA msg-queue shoot seed overlay (tmp, per-run).', + 'plugins:', + ` - "@cordisjs/plugin-include":`, + ` path: ${join(WORKTREE, 'config/daemon-echo.yml')}`, + '', + ].join('\n') + writeFileSync(join(dshHome, 'user-overlay.cordis.yml'), seedOverlay) + writeFileSync(join(dshHome, 'config.json'), JSON.stringify({ role: 'coding', approvalMode: 'never' }, null, 2)) + writeFileSync(join(dshHome, '.onboarded'), new Date().toISOString()) +} + +async function bootElectron(dshHome, userData, port) { + const child = spawn(ELECTRON, [ + `--remote-debugging-port=${port}`, + `--user-data-dir=${userData}`, + '--disable-gpu', + '--no-sandbox', + '.', + ], { + cwd: WORKTREE, + env: { ...process.env, DSH_DESKTOP_HOME: dshHome, DSH_MAXIMIZE: '1' }, + stdio: ['ignore', 'pipe', 'pipe'], + }) + const logs = [] + child.stdout.on('data', d => logs.push(String(d))) + child.stderr.on('data', d => logs.push(String(d))) + for (let i = 0; i < 40; i++) { + await sleep(500) + try { + const r = await fetch(`http://localhost:${port}/json/list`) + if (r.ok) return { child, logs } + } catch {} + } + child.kill('SIGKILL') + console.error('electron CDP did not come up in 20s. logs:\n' + logs.join('')) + process.exit(3) +} + +async function newCdp(port) { + const targets = await (await fetch(`http://localhost:${port}/json/list`)).json() + const target = targets.find(t => t.type === 'page') + if (!target) throw new Error('no page target on port ' + port) + const ws = new WebSocket(target.webSocketDebuggerUrl) + await new Promise((ok, err) => { ws.onopen = ok; ws.onerror = e => err(e) }) + let id = 1 + const pending = new Map() + ws.onmessage = ev => { + const data = typeof ev.data === 'string' ? ev.data : String(ev.data) + let msg + try { msg = JSON.parse(data) } catch { return } + if (msg.id != null && pending.has(msg.id)) { + const [ok, err] = pending.get(msg.id); pending.delete(msg.id) + if (msg.error) err(new Error(msg.error.message)); else ok(msg.result) + } + } + const call = (m, p = {}, ms = 15000) => new Promise((ok, err) => { + const _id = id++ + const t = setTimeout(() => { pending.delete(_id); err(new Error('cdp timeout: ' + m)) }, ms) + pending.set(_id, [v => { clearTimeout(t); ok(v) }, e => { clearTimeout(t); err(e) }]) + ws.send(JSON.stringify({ id: _id, method: m, params: p })) + }) + const evj = async expr => { + const r = await call('Runtime.evaluate', { + expression: `(async()=>{try{return (${expr})}catch(e){return {__err:String(e)}}})()`, + returnByValue: true, awaitPromise: true, + }) + if (r.exceptionDetails) throw new Error(r.exceptionDetails.exception?.description || r.exceptionDetails.text) + return r.result?.value + } + return { ws, call, evj } +} + +// Clip to the composer footer so the strip is unmistakably in frame (not a +// giant empty chat pane). Falls back to a full-page shot if the selector +// isn't found. +async function shoot(call, evj, name) { + const clip = await evj(` + (() => { + const f = document.querySelector('.composer.composer-shell') + if (!f) return null + const r = f.getBoundingClientRect() + // pad upward so a freshly-sent bubble just above the composer is caught. + const top = Math.max(0, r.y - 220) + return { x: r.x, y: top, width: r.width, height: (r.bottom - top) + 12, scale: 1 } + })() + `) + const shotArgs = { format: 'png', captureBeyondViewport: true } + if (clip) shotArgs.clip = clip + const shot = await call('Page.captureScreenshot', shotArgs, 30000) + if (!shot || !shot.data) throw new Error('captureScreenshot returned no data') + const outPath = join(OUTDIR, name) + writeFileSync(outPath, Buffer.from(shot.data, 'base64')) + const bytes = statSync(outPath).size + console.log(` wrote ${outPath} (${bytes} bytes)`) + if (bytes < 20000) throw new Error(`screenshot ${name} is only ${bytes} bytes (<20KB) — likely blank`) + return { path: outPath, bytes } +} + +async function main() { + const dshHome = join(tmpdir(), 'dsh-msg-queue-home') + const userData = join(tmpdir(), 'dsh-msg-queue-userdata') + for (const dir of [dshHome, userData]) { + try { rmSync(dir, { recursive: true, force: true }) } catch {} + mkdirSync(dir, { recursive: true }) + } + seedHome(dshHome) + console.log(`booting on CDP :${CDP_PORT}`) + const { child } = await bootElectron(dshHome, userData, CDP_PORT) + try { + await sleep(1500) + const { call, evj } = await newCdp(CDP_PORT) + await call('Page.enable') + + // Step 1: create a session, make it active, and open an in-flight turn. + // We drive the renderer directly through the always-exposed seam. + const setup = await evj(` + (async () => { + const R = window.__dshRenderer + if (!R) return { __err: 'no __dshRenderer seam' } + R.ensureSession('qa-mq', { title: 'Message queue demo', header: {}, hasUserMessage: true }) + await R.selectSession('qa-mq') + R.onSessionEvent('qa-mq', { type: 'turn/start', seq: 1 }) + // Enqueue two follow-ups via the real composer send() path. + const input = document.getElementById('input') + input.value = 'Summarise the three failing tests' + await R.send() + input.value = 'Then open a PR with the fix' + await R.send() + return { active: R.getActiveSessionId(), queued: R.listMsgQueue('qa-mq').map(x => x.text) } + })() + `) + console.log(' setup:', JSON.stringify(setup)) + if (setup && setup.__err) throw new Error(setup.__err) + if (!setup || setup.queued.length !== 2) throw new Error('expected 2 queued messages, got ' + JSON.stringify(setup)) + + // Assert the strip is actually visible with two chips before shooting. + const stripA = await evj(` + (() => { + const s = document.getElementById('msg-queue-strip') + return { + hidden: s.hidden, + chips: s.querySelectorAll('.msg-queue-chip').length, + counter: (s.querySelector('.msg-queue-count') || {}).textContent || '', + } + })() + `) + console.log(' strip (inflight):', JSON.stringify(stripA)) + if (stripA.hidden || stripA.chips !== 2) throw new Error('strip not showing 2 chips: ' + JSON.stringify(stripA)) + await sleep(300) + const shotA = await shoot(call, evj, '01-inflight-two-queued.png') + + // Step 2: end the turn. turn/end auto-drains exactly one item — the head + // renders as an optimistic user bubble and the strip drops to one chip. + const drained = await evj(` + (async () => { + const R = window.__dshRenderer + R.onSessionEvent('qa-mq', { type: 'turn/end', seq: 2 }) + await new Promise(r => setTimeout(r, 60)) + const bubbles = Array.from(document.querySelectorAll('#stream .msg.user .role-label')).length + return { remaining: R.listMsgQueue('qa-mq').map(x => x.text), userBubbles: bubbles } + })() + `) + console.log(' after turn/end:', JSON.stringify(drained)) + if (!drained || drained.remaining.length !== 1) throw new Error('expected 1 remaining, got ' + JSON.stringify(drained)) + + const stripB = await evj(` + (() => { + const s = document.getElementById('msg-queue-strip') + return { + hidden: s.hidden, + chips: s.querySelectorAll('.msg-queue-chip').length, + counter: (s.querySelector('.msg-queue-count') || {}).textContent || '', + } + })() + `) + console.log(' strip (after drain):', JSON.stringify(stripB)) + if (stripB.hidden || stripB.chips !== 1) throw new Error('strip not showing 1 chip after drain: ' + JSON.stringify(stripB)) + await sleep(300) + const shotB = await shoot(call, evj, '02-after-turn-end-one-left.png') + + console.log('\n--- SUMMARY ---') + console.log(`inflight (2 queued): ${shotA.path} (${shotA.bytes} bytes)`) + console.log(`after drain (1 left): ${shotB.path} (${shotB.bytes} bytes)`) + } finally { + try { child.kill('SIGKILL') } catch {} + for (let i = 0; i < 20; i++) { + await sleep(500) + try { await fetch(`http://localhost:${CDP_PORT}/json/list`) } catch { break } + } + } +} + +main().catch(err => { console.error(err); process.exit(1) }) diff --git a/examples/desktop/src/renderer/index.html b/examples/desktop/src/renderer/index.html index 993ca81f5a..cadcddd1e3 100644 --- a/examples/desktop/src/renderer/index.html +++ b/examples/desktop/src/renderer/index.html @@ -517,6 +517,13 @@ matching the widget action catalog (see next-actions.js). Empty by default; hidden entirely when no suggestions land. -->

+ +
@@ -1487,6 +1494,7 @@ drill-down view models. Loaded before subagent-view.js so buildInlineSubagentTrace can pick up the drill-down helpers. --> + diff --git a/examples/desktop/src/renderer/msg-queue-model.js b/examples/desktop/src/renderer/msg-queue-model.js new file mode 100644 index 0000000000..1dc44640da --- /dev/null +++ b/examples/desktop/src/renderer/msg-queue-model.js @@ -0,0 +1,142 @@ +// Pure model for the composer message queue (lane-msg-queue). +// +// The DSH wire accepts exactly ONE in-flight prompt per session — a second +// session/prompt while a turn is running fails on the wire. So when the user +// hits Enter mid-turn we don't drop the text and we don't error: we park it +// in a per-session FIFO and auto-send the head when the turn completes. +// +// This module is the data structure only — no DOM, no wire. The renderer +// owns the "when to enqueue vs send" decision and the "drain on turn end" +// timing; this file just holds the queues and enforces their invariants so +// the ordering/isolation semantics can be locked in `node --test` without an +// Electron harness. +// +// Shape: +// queues: Map> +// Item ids are monotonic across the whole module (not per-session) so a +// chip's id is globally unique — the UI keys DOM nodes on it and never has +// to disambiguate by session. +// +// Invariants the renderer relies on: +// - FIFO: enqueue appends to the tail; drain pops the head. +// - promote(id) moves that item to the head (the "send next" affordance), +// so the very next drain sends it regardless of arrival order. +// - Per-session isolation: no operation on session A can read, mutate, or +// drain session B. A drain of an empty/unknown session returns null. +// - Empty / whitespace-only text never enqueues (returns null) — a blank +// chip is a trap the user can't tell apart from a real queued message. + +'use strict' + +function createMsgQueue() { + /** @type {Map>} */ + const queues = new Map() + let _seq = 0 + + function nextId() { _seq += 1; return _seq } + + function ensure(sessionId) { + let q = queues.get(sessionId) + if (!q) { q = []; queues.set(sessionId, q) } + return q + } + + // Append `text` to sessionId's queue. Returns the new item id, or null if + // the text is empty/whitespace-only (never enqueue a blank). + function enqueue(sessionId, text) { + if (sessionId == null) return null + const trimmed = typeof text === 'string' ? text.trim() : '' + if (!trimmed) return null + const id = nextId() + ensure(sessionId).push({ id, text: trimmed }) + return id + } + + // Drop the item with `id` from sessionId's queue. Returns true if removed. + function remove(sessionId, id) { + const q = queues.get(sessionId) + if (!q) return false + const i = q.findIndex((it) => it.id === id) + if (i < 0) return false + q.splice(i, 1) + return true + } + + // Rewrite the text of an existing queued item. Empty/whitespace text is + // rejected (returns false) — the caller should treat that as "delete" if + // it wants blank edits to remove the chip. Returns true on success. + function update(sessionId, id, text) { + const q = queues.get(sessionId) + if (!q) return false + const trimmed = typeof text === 'string' ? text.trim() : '' + if (!trimmed) return false + const it = q.find((x) => x.id === id) + if (!it) return false + it.text = trimmed + return true + } + + // Move `id` to the head of its session queue (the "send next" affordance). + // No-op returning false if the item isn't found; a single-item or + // already-head move succeeds (idempotent) and returns true. + function promote(sessionId, id) { + const q = queues.get(sessionId) + if (!q) return false + const i = q.findIndex((it) => it.id === id) + if (i < 0) return false + if (i === 0) return true + const [it] = q.splice(i, 1) + q.unshift(it) + return true + } + + // Pop and return the head item ({id,text}) of sessionId's queue, or null + // when the queue is empty/unknown. This is the single "send one on turn + // end" primitive — the renderer calls it exactly once per turn completion. + function drain(sessionId) { + const q = queues.get(sessionId) + if (!q || q.length === 0) return null + return q.shift() + } + + // Return a shallow copy of sessionId's queue (safe to render from; callers + // can't mutate the live array). Empty array for an unknown session. + function list(sessionId) { + const q = queues.get(sessionId) + return q ? q.map((it) => ({ id: it.id, text: it.text })) : [] + } + + function size(sessionId) { + const q = queues.get(sessionId) + return q ? q.length : 0 + } + + // Empty one session's queue. Returns the number of items dropped. + function clear(sessionId) { + const q = queues.get(sessionId) + if (!q) return 0 + const n = q.length + queues.delete(sessionId) + return n + } + + // Empty every session's queue (runtime crash / profile switch — the old + // session ids are a different namespace against the restarted daemon, so + // holding their queued text would send it into the void). Returns total + // items dropped across all sessions. + function clearAll() { + let n = 0 + for (const q of queues.values()) n += q.length + queues.clear() + return n + } + + return { enqueue, remove, update, promote, drain, list, size, clear, clearAll } +} + +if (typeof module !== 'undefined' && module.exports) { + module.exports = { createMsgQueue } +} +if (typeof window !== 'undefined') { + window.__dshMsgQueueModel = { createMsgQueue } +} diff --git a/examples/desktop/src/renderer/renderer.js b/examples/desktop/src/renderer/renderer.js index 08fa96bccc..2d22edba58 100644 --- a/examples/desktop/src/renderer/renderer.js +++ b/examples/desktop/src/renderer/renderer.js @@ -99,6 +99,19 @@ const statusText = document.getElementById('status-text') const modelBadge = document.getElementById('model-badge') const profileSelect = document.getElementById('profile') const newSessionBtn = document.getElementById('new-session') +const msgQueueStripEl = document.getElementById('msg-queue-strip') + +// Composer message queue (lane-msg-queue). The wire accepts one in-flight +// prompt per session; a mid-turn Enter would otherwise fire a second +// session/prompt that the daemon rejects. Instead we park the text in a +// per-session FIFO (msg-queue-model.js) and auto-drain the head when the +// turn completes. The model is a pure data structure; all DOM + wire timing +// lives in this file (see §"message queue"). Guarded so a stripped build +// without the module script simply disables queueing (send stays direct). +const msgQueue = (typeof window !== 'undefined' && window.__dshMsgQueueModel + && typeof window.__dshMsgQueueModel.createMsgQueue === 'function') + ? window.__dshMsgQueueModel.createMsgQueue() + : null const state = { activeSessionId: null, @@ -586,6 +599,9 @@ async function selectSession(id) { // their seed boundary. During live streaming we grow the same map when // subagent.started fires. installKnownForkMarkers(id) + // Per-session queue: repaint the strip for the session we just switched to. + // Strict isolation — the strip only ever shows the active session's queue. + renderMsgQueueStrip() } async function replayHistory(id) { @@ -4856,6 +4872,11 @@ function onSessionEvent(sessionId, event) { // without a badge instead of one from the previous turn. const startData = event.data || event meta.currentTurnTrigger = (startData && startData.trigger) || null + // Arm the once-per-turn queue drain. turn/end and session.finished both + // signal completion (a clean turn emits turn/end; an errored/cancelled + // turn may arrive only as session.finished). Whichever fires first + // consumes this flag so the queue drains exactly one item per turn. + meta._turnDrainPending = true if (sessionId === state.activeSessionId) { state.inflightTurn = true; updateCancelButton(); updateCompactButton(); updateForkButtons() } renderSessionList() } @@ -4864,6 +4885,11 @@ function onSessionEvent(sessionId, event) { meta.currentTurnTrigger = null if (sessionId === state.activeSessionId) { state.inflightTurn = false; updateCancelButton(); updateCompactButton(); updateForkButtons() } renderSessionList() + // Auto-drain one queued message for this session now that its turn is + // done. Fires for the exact turn-completion condition that re-enables + // Cancel; a cancelled turn ends with turn/end too, so the queue drains + // there just like a clean completion (queue survives Cancel by design). + void drainMsgQueueOnce(sessionId) } if (event.time && event.time > meta.lastEventTime) meta.lastEventTime = event.time // §1.1 trace bucket: non-boundary events get bucketed into @@ -6071,9 +6097,133 @@ function onInterruptInvalidate({ interruptId, reason }) { // -- send / cancel ----------------------------------------------------------- +// -- message queue ---------------------------------------------------------- +// +// Renders the strip above the composer for the ACTIVE session's queue. The +// strip is hidden when the queue is empty; each queued message is one chip +// with truncated text (click to inline-edit), a "send next" (promote) button, +// and a delete (×). A "queued N" counter badge leads the row. Per-session: +// selectSession + every mutation calls this, so switching sessions shows that +// session's queue and nothing bleeds across sessions. + +const MSG_QUEUE_CHIP_MAX = 48 + +// Truncate for the chip label. Kept in sync with MSG_QUEUE_CHIP_MAX; the full +// text lives on the chip's title attribute so hover reveals the whole thing. +function truncateQueueText(text) { + const t = String(text || '') + return t.length > MSG_QUEUE_CHIP_MAX ? t.slice(0, MSG_QUEUE_CHIP_MAX - 1) + '…' : t +} + +// Swap a chip's static label for an inline seeded with the full text. +// Enter / blur commits via msgQueue.update (blank ⇒ delete, matching the +// model's "never keep a blank" rule); Escape re-renders to cancel. +function beginQueueChipEdit(sessionId, id, chipEl, fullText) { + if (!chipEl) return + const input = document.createElement('input') + input.type = 'text' + input.className = 'msg-queue-chip-edit' + input.value = fullText + let done = false + const commit = (save) => { + if (done) return + done = true + if (save) { + const v = input.value.trim() + if (!v) msgQueue.remove(sessionId, id) + else msgQueue.update(sessionId, id, v) + } + renderMsgQueueStrip() + } + input.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { e.preventDefault(); commit(true) } + else if (e.key === 'Escape') { e.preventDefault(); commit(false) } + }) + input.addEventListener('blur', () => commit(true)) + chipEl.textContent = '' + chipEl.appendChild(input) + input.focus() +} + +function makeQueueChip(sessionId, item) { + const chip = document.createElement('span') + chip.className = 'msg-queue-chip' + chip.dataset.queueId = String(item.id) + + const label = document.createElement('button') + label.type = 'button' + label.className = 'msg-queue-chip-text' + label.textContent = truncateQueueText(item.text) + label.title = item.text + '\n(click to edit)' + label.addEventListener('click', () => beginQueueChipEdit(sessionId, item.id, chip, item.text)) + + // "send next" — promote to head so the next drain sends this item first. + const promoteBtn = document.createElement('button') + promoteBtn.type = 'button' + promoteBtn.className = 'msg-queue-chip-btn msg-queue-chip-promote' + promoteBtn.title = 'Send this one next' + promoteBtn.setAttribute('aria-label', 'Send this message next') + promoteBtn.textContent = '↑' + promoteBtn.addEventListener('click', () => { msgQueue.promote(sessionId, item.id); renderMsgQueueStrip() }) + + const delBtn = document.createElement('button') + delBtn.type = 'button' + delBtn.className = 'msg-queue-chip-btn msg-queue-chip-del' + delBtn.title = 'Remove from queue' + delBtn.setAttribute('aria-label', 'Remove queued message') + delBtn.textContent = '×' + delBtn.addEventListener('click', () => { msgQueue.remove(sessionId, item.id); renderMsgQueueStrip() }) + + chip.append(label, promoteBtn, delBtn) + return chip +} + +function renderMsgQueueStrip() { + if (!msgQueueStripEl || !msgQueue) return + const sid = state.activeSessionId + const items = sid ? msgQueue.list(sid) : [] + msgQueueStripEl.textContent = '' + if (items.length === 0) { + msgQueueStripEl.hidden = true + return + } + msgQueueStripEl.hidden = false + + const badge = document.createElement('span') + badge.className = 'msg-queue-count' + badge.textContent = `queued ${items.length}` + msgQueueStripEl.appendChild(badge) + + // Only the head is a promote target worth showing an active arrow on, but + // we render the affordance on every non-head chip; the head chip's arrow is + // greyed (already next) via the .is-head class. + for (let i = 0; i < items.length; i++) { + const chip = makeQueueChip(sid, items[i]) + if (i === 0) chip.classList.add('is-head') + msgQueueStripEl.appendChild(chip) + } +} + async function send() { const text = inputEl.value.trim() if (!text) return + // Mid-turn Enter: the active session already has a prompt in flight (the + // wire accepts only one at a time). Park this text in the session's queue + // and clear the composer as if sent — it auto-drains on turn/end. Only + // applies when there's an active session AND a turn is running; a fresh + // "+" session or an idle session falls through to the normal send path. + if (msgQueue && state.activeSessionId && state.inflightTurn) { + const sid = state.activeSessionId + const meta = state.sessions.get(sid) + if (meta && !meta.hasUserMessage) { + meta.hasUserMessage = true + if (!meta.title) meta.title = text.slice(0, 40) + } + msgQueue.enqueue(sid, text) + inputEl.value = '' + renderMsgQueueStrip() + return + } if (!state.activeSessionId) { const { id } = await window.dsh.newSession() ensureSession(id, { title: text.slice(0, 40), hasUserMessage: true }) @@ -6091,22 +6241,66 @@ async function send() { } } const sid = state.activeSessionId - // Optimistic bubble — the server echoes user/message shortly after, and the - // event handler adopts this element in place instead of re-rendering. - appendMessage({ role: 'user', text, optimistic: true }) - updateEmptyStateVisibility() - inputEl.value = '' - sendBtn.disabled = true - state.inflightTurn = true; updateCancelButton(); updateCompactButton(); updateForkButtons() + await dispatchPrompt(sid, text, { clearComposer: true }) +} + +// The shared "actually send a prompt" tail: optimistic bubble + inflight +// flag + sendPrompt, with the existing error surface. Extracted from send() +// so the queue's auto-drain sends a parked message through the exact same +// path a live Enter takes. `opts.clearComposer` empties the textarea (live +// send); the drain path leaves it alone (the user may be mid-typing the next +// message). The optimistic bubble + inflight flag only apply when `sid` is +// the active session — a drain firing for a background session's turn/end +// must not paint into the foreground stream (the echoed user/message renders +// when that session is next viewed). +async function dispatchPrompt(sid, text, opts = {}) { + const isActive = sid === state.activeSessionId + if (isActive) { + // Optimistic bubble — the server echoes user/message shortly after, and + // the event handler adopts this element in place instead of re-rendering. + appendMessage({ role: 'user', text, optimistic: true }) + updateEmptyStateVisibility() + if (opts.clearComposer) inputEl.value = '' + sendBtn.disabled = true + state.inflightTurn = true; updateCancelButton(); updateCompactButton(); updateForkButtons() + } + const meta = state.sessions.get(sid) + if (meta) meta.running = true try { await window.dsh.sendPrompt(sid, text) } catch (err) { + // Surface the error on the stream (matches the live-send path). We do + // NOT clear the rest of the queue — a failed drain leaves the remaining + // items parked so the user can retry / edit rather than losing them. appendSystem(`error: ${err.message}`) } finally { - sendBtn.disabled = false + if (isActive) sendBtn.disabled = false } } +// Drain exactly one queued message for `sessionId` and send it — guarded so +// it fires at most once per turn even when both turn/end and +// session.finished arrive for the same turn. The `_turnDrainPending` flag is +// armed on turn/start and consumed here. ONE item per turn completion, so +// each queued follow-up waits for its own turn to finish (the head's +// turn/end drains the next head, and so on). Runs after the completion +// handler has flipped inflightTurn off, so dispatchPrompt re-arms it for the +// drained send. +async function drainMsgQueueOnce(sessionId) { + if (!msgQueue) return + const meta = state.sessions.get(sessionId) + // Guard: only the first completion signal for this turn drains. Absent a + // meta (defensive) we still allow one drain. + if (meta) { + if (!meta._turnDrainPending) return + meta._turnDrainPending = false + } + const next = msgQueue.drain(sessionId) + renderMsgQueueStrip() + if (!next) return + await dispatchPrompt(sessionId, next.text, { clearComposer: false }) +} + async function cancel() { if (!state.activeSessionId) return try { @@ -6171,6 +6365,11 @@ window.dsh.onNotify(({ method, params }) => { if (params.sessionId === state.activeSessionId) { state.inflightTurn = false; updateCancelButton(); updateForkButtons() } // Refresh listing so title/lastEventTime catch up. void refreshSessionList() + // Drain-once: if this session's turn ended via session.finished without a + // clean turn/end (error/cancel paths), the guard fires the single drain + // here. If turn/end already drained, `_turnDrainPending` is false and this + // is a no-op. + void drainMsgQueueOnce(params.sessionId) } else if (method === 'subagent.started') { // Grow the tree eagerly: register the child session with a placeholder // parent link, refresh the sidebar, and drop a fork marker on the parent @@ -6615,6 +6814,19 @@ window.dsh.onInitialized((info) => { titleEl.textContent = 'New chat' updateEmptyStateVisibility() updateCancelButton() + // Runtime crash / profile switch = a fresh session namespace (the old + // session ids can never come back). Any messages still queued against the + // dead runtime's sessions would send into the void, so wipe every queue + // and — if we actually dropped anything — drop a one-line notice so the + // user knows their parked follow-ups didn't survive the restart. Mirrors + // the interrupt-invalidate posture (stale-against-restart state is a trap). + if (msgQueue) { + const dropped = msgQueue.clearAll() + if (dropped > 0) { + appendSystem(`runtime restarted — cleared ${dropped} queued message${dropped === 1 ? '' : 's'}`) + } + renderMsgQueueStrip() + } // New runtime = unknown compact support; button falls back to greyed // until the first successful call proves the daemon accepts the method // (or a MethodNotFound flips it to `false`). @@ -8211,6 +8423,14 @@ window.__dshRenderer = { sessionIds: Array.from(state.sessions.keys()), replayingId: state.replayingId, }), + // Message queue (lane-msg-queue) seam: expose the send entry point, the + // pure queue handle, and the strip renderer so renderer-level tests and QA + // scripts can walk the enqueue-on-inflight + auto-drain wiring without a + // real wire round-trip. `getMsgQueue` returns the live model instance. + send, + getMsgQueue: () => msgQueue, + listMsgQueue: (sid) => (msgQueue ? msgQueue.list(sid) : []), + renderMsgQueueStrip, } // `⌘.` (Ctrl-. on non-Mac) toggles every trace-event-row diff --git a/examples/desktop/src/renderer/style.css b/examples/desktop/src/renderer/style.css index 2e4dd202bb..ea7dc5ec3c 100644 --- a/examples/desktop/src/renderer/style.css +++ b/examples/desktop/src/renderer/style.css @@ -3673,6 +3673,78 @@ textarea:focus-visible { cursor: pointer; } .composer-model-warn a:hover { color: var(--accent); } + +/* Message queue strip (lane-msg-queue). Sits above the composer frame, shown + only when the active session has queued mid-turn messages. One chip per + queued message + a leading "queued N" counter. Chip grammar mirrors the + composer chip pills (999px radius, muted surface, 11.5px label) so it reads + as part of the composer, not a separate widget. */ +.msg-queue-strip { + display: flex; align-items: center; flex-wrap: wrap; + gap: 6px; + margin: 0 4px 6px 4px; + padding: 4px 2px; +} +.msg-queue-count { + display: inline-flex; align-items: center; + padding: 2px 8px; + border-radius: 999px; + background: var(--accent-soft); + color: var(--accent); + font-size: 10.5px; font-weight: 600; + letter-spacing: 0.03em; + text-transform: uppercase; + flex: 0 0 auto; +} +.msg-queue-chip { + display: inline-flex; align-items: center; gap: 2px; + padding: 2px 4px 2px 8px; + border-radius: 999px; + background: var(--surface-2); + border: 1px solid var(--border); + max-width: 280px; + transition: border-color 120ms ease, background 120ms ease; +} +.msg-queue-chip:hover { border-color: var(--accent); } +.msg-queue-chip-text { + border: none; background: transparent; + padding: 0; margin: 0; + color: var(--text-secondary); + font: inherit; font-size: 11.5px; line-height: 1.4; + max-width: 200px; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + cursor: text; +} +.msg-queue-chip-text:hover { color: var(--text); } +.msg-queue-chip-edit { + border: 1px solid var(--accent); + border-radius: var(--radius-sm); + background: var(--surface); + color: var(--text); + font: inherit; font-size: 11.5px; line-height: 1.4; + padding: 1px 6px; + min-width: 180px; + outline: none; +} +.msg-queue-chip-btn { + display: inline-flex; align-items: center; justify-content: center; + width: 18px; height: 18px; + padding: 0; border: none; + border-radius: 999px; + background: transparent; + color: var(--text-tertiary); + font-size: 13px; line-height: 1; + cursor: pointer; + transition: background 120ms ease, color 120ms ease; +} +.msg-queue-chip-btn:hover { background: var(--surface-hover); color: var(--text); } +.msg-queue-chip-del:hover { color: var(--err); } +/* The head chip is already "next" — dim its promote arrow so the affordance + reads as inert without removing it (keeps chip layout stable). */ +.msg-queue-chip.is-head .msg-queue-chip-promote { + opacity: 0.35; + pointer-events: none; +} .composer-chip--mode { color: var(--text-secondary); } diff --git a/examples/desktop/test/msg-queue-model.test.js b/examples/desktop/test/msg-queue-model.test.js new file mode 100644 index 0000000000..a35021a711 --- /dev/null +++ b/examples/desktop/test/msg-queue-model.test.js @@ -0,0 +1,145 @@ +// Unit tests for msg-queue-model.js — the per-session FIFO backing the +// composer's mid-turn message queue. Pure data structure, no DOM. + +'use strict' + +const test = require('node:test') +const assert = require('node:assert/strict') + +const { createMsgQueue } = require('../src/renderer/msg-queue-model.js') + +test('enqueue returns a monotonic id and list preserves FIFO order', () => { + const q = createMsgQueue() + const a = q.enqueue('s1', 'first') + const b = q.enqueue('s1', 'second') + const c = q.enqueue('s1', 'third') + assert.ok(a < b && b < c, 'ids are monotonic') + assert.deepEqual(q.list('s1').map((x) => x.text), ['first', 'second', 'third']) +}) + +test('enqueue trims text and rejects empty / whitespace-only', () => { + const q = createMsgQueue() + assert.equal(q.enqueue('s1', ''), null) + assert.equal(q.enqueue('s1', ' '), null) + assert.equal(q.enqueue('s1', '\n\t '), null) + const id = q.enqueue('s1', ' hi ') + assert.ok(id) + assert.equal(q.list('s1')[0].text, 'hi', 'stored text is trimmed') + assert.equal(q.size('s1'), 1, 'rejected blanks never entered the queue') +}) + +test('enqueue with null sessionId is a no-op', () => { + const q = createMsgQueue() + assert.equal(q.enqueue(null, 'x'), null) + assert.equal(q.enqueue(undefined, 'x'), null) +}) + +test('drain pops the head and returns null when empty/unknown', () => { + const q = createMsgQueue() + assert.equal(q.drain('s1'), null, 'unknown session drains to null') + q.enqueue('s1', 'one') + q.enqueue('s1', 'two') + assert.equal(q.drain('s1').text, 'one', 'head first') + assert.equal(q.drain('s1').text, 'two') + assert.equal(q.drain('s1'), null, 'emptied queue drains to null') +}) + +test('drain-once semantics: one call removes exactly one item', () => { + const q = createMsgQueue() + q.enqueue('s1', 'a') + q.enqueue('s1', 'b') + q.enqueue('s1', 'c') + const first = q.drain('s1') + assert.equal(first.text, 'a') + assert.equal(q.size('s1'), 2, 'the other two wait for their own turn ends') + assert.deepEqual(q.list('s1').map((x) => x.text), ['b', 'c']) +}) + +test('remove drops a specific item by id; returns false when absent', () => { + const q = createMsgQueue() + const a = q.enqueue('s1', 'a') + const b = q.enqueue('s1', 'b') + assert.equal(q.remove('s1', a), true) + assert.deepEqual(q.list('s1').map((x) => x.text), ['b']) + assert.equal(q.remove('s1', a), false, 'already gone') + assert.equal(q.remove('nope', b), false, 'unknown session') +}) + +test('update rewrites text, trims, and rejects blank', () => { + const q = createMsgQueue() + const id = q.enqueue('s1', 'old') + assert.equal(q.update('s1', id, ' new '), true) + assert.equal(q.list('s1')[0].text, 'new') + assert.equal(q.update('s1', id, ' '), false, 'blank edit rejected') + assert.equal(q.list('s1')[0].text, 'new', 'text unchanged after rejected edit') + assert.equal(q.update('s1', 9999, 'x'), false, 'unknown id') +}) + +test('promote moves an item to the head so the next drain sends it', () => { + const q = createMsgQueue() + q.enqueue('s1', 'a') + const b = q.enqueue('s1', 'b') + q.enqueue('s1', 'c') + assert.equal(q.promote('s1', b), true) + assert.deepEqual(q.list('s1').map((x) => x.text), ['b', 'a', 'c']) + assert.equal(q.drain('s1').text, 'b', 'promoted item drains first') +}) + +test('promote is idempotent on the head and single-item queues', () => { + const q = createMsgQueue() + const a = q.enqueue('s1', 'a') + assert.equal(q.promote('s1', a), true, 'single item head promote succeeds') + assert.deepEqual(q.list('s1').map((x) => x.text), ['a']) + const b = q.enqueue('s1', 'b') + assert.equal(q.promote('s1', a), true, 'already-head promote succeeds') + assert.deepEqual(q.list('s1').map((x) => x.text), ['a', 'b']) + assert.equal(q.promote('s1', 4242), false, 'unknown id fails') +}) + +test('per-session isolation: operations never cross sessions', () => { + const q = createMsgQueue() + const a1 = q.enqueue('s1', 's1-a') + q.enqueue('s2', 's2-a') + q.enqueue('s2', 's2-b') + assert.equal(q.size('s1'), 1) + assert.equal(q.size('s2'), 2) + // drain s1 leaves s2 untouched + assert.equal(q.drain('s1').text, 's1-a') + assert.equal(q.size('s2'), 2, 's2 unaffected by s1 drain') + // remove/promote keyed on the wrong session is a no-op + assert.equal(q.remove('s2', a1), false, 's1 id not found in s2') + assert.equal(q.promote('s2', a1), false) + assert.deepEqual(q.list('s2').map((x) => x.text), ['s2-a', 's2-b']) +}) + +test('clear empties one session and returns the count dropped', () => { + const q = createMsgQueue() + q.enqueue('s1', 'a') + q.enqueue('s1', 'b') + q.enqueue('s2', 'c') + assert.equal(q.clear('s1'), 2) + assert.equal(q.size('s1'), 0) + assert.equal(q.size('s2'), 1, 'other session survives a targeted clear') + assert.equal(q.clear('unknown'), 0) +}) + +test('clearAll wipes every session (crash / profile switch) and totals the drop', () => { + const q = createMsgQueue() + q.enqueue('s1', 'a') + q.enqueue('s1', 'b') + q.enqueue('s2', 'c') + assert.equal(q.clearAll(), 3) + assert.equal(q.size('s1'), 0) + assert.equal(q.size('s2'), 0) + assert.deepEqual(q.list('s1'), []) +}) + +test('list returns copies — mutating the result cannot corrupt the queue', () => { + const q = createMsgQueue() + q.enqueue('s1', 'a') + const snap = q.list('s1') + snap[0].text = 'HACKED' + snap.push({ id: 999, text: 'injected' }) + assert.equal(q.list('s1')[0].text, 'a', 'internal item untouched') + assert.equal(q.size('s1'), 1, 'internal length untouched') +}) diff --git a/examples/desktop/test/renderer-collisions.test.js b/examples/desktop/test/renderer-collisions.test.js index 52f3bd3331..3923fe3edb 100644 --- a/examples/desktop/test/renderer-collisions.test.js +++ b/examples/desktop/test/renderer-collisions.test.js @@ -143,6 +143,12 @@ const NON_IIFE_ALLOWLIST = new Set([ 'intervention-timeline.js', 'compact-config-model.js', 'subagent-drilldown.js', + // lane-msg-queue (2026-07-20) composer message queue: dual-exported pure + // FIFO model — module.exports for node --test, window.__dshMsgQueueModel + // for the renderer. Same shape as compact-config-model.js; preloadPure + // require()s it so it must not be IIFE-wrapped. Sole top-level binding is + // `function createMsgQueue`, unique across the shared scope. + 'msg-queue-model.js', ]) function listRendererScripts() { diff --git a/examples/desktop/test/renderer-harness.js b/examples/desktop/test/renderer-harness.js index f7a45c0991..eb1998a7ae 100644 --- a/examples/desktop/test/renderer-harness.js +++ b/examples/desktop/test/renderer-harness.js @@ -419,6 +419,7 @@ async function loadRenderer(customStubs = {}, options = {}) { ['tool-cards.js', '__dshToolCards'], ['widgets.js', '__dshWidgets'], ['capabilities.js', '__dshCapabilities'], + ['msg-queue-model.js', '__dshMsgQueueModel'], ] for (const [file, key] of preloadPure) { const p = path.join(__dirname, '..', 'src', 'renderer', file) diff --git a/examples/desktop/test/renderer-msg-queue.test.js b/examples/desktop/test/renderer-msg-queue.test.js new file mode 100644 index 0000000000..76fcc23983 --- /dev/null +++ b/examples/desktop/test/renderer-msg-queue.test.js @@ -0,0 +1,196 @@ +// Renderer-level tests for the composer message queue (lane-msg-queue). +// +// Covers the wiring the pure msg-queue-model.js tests can't reach: +// - send() enqueues instead of sendPrompt when a turn is in flight +// - the queue strip renders/hides per the active session's queue +// - turn/end auto-drains exactly ONE queued item through sendPrompt +// - session.finished drains once too, and doesn't double-drain with turn/end +// - per-session isolation: a background session's turn/end never touches +// the foreground session's composer +// - runtime restart (onInitialized) clears every queue +// +// The harness loads the whole renderer against a DOM stub and exposes the +// send entry point + queue handle via window.__dshRenderer. + +'use strict' + +const test = require('node:test') +const assert = require('node:assert/strict') +const { loadRenderer } = require('./renderer-harness.js') + +// Drive an in-flight turn on `sid` and make it active, returning refs. +async function activeInflight(renderer, document, sid = 's1') { + renderer.ensureSession(sid, { title: 'sess', header: {}, hasUserMessage: true }) + await renderer.selectSession(sid) + renderer.onSessionEvent(sid, { type: 'turn/start', seq: 1 }) + return { input: document.getElementById('input') } +} + +function sendCalls(dsh) { + return dsh.__calls.filter((c) => c[0] === 'sendPrompt') +} + +test('send() enqueues instead of sendPrompt while a turn is in flight', async () => { + const { renderer, document, dsh } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + const before = sendCalls(dsh).length + input.value = 'queued follow-up' + await renderer.send() + assert.equal(sendCalls(dsh).length, before, 'no sendPrompt fired mid-turn') + assert.deepEqual(renderer.listMsgQueue('s1').map((x) => x.text), ['queued follow-up']) + assert.equal(input.value, '', 'composer cleared as if sent') +}) + +test('whitespace-only mid-turn input never enqueues', async () => { + const { renderer, document } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + input.value = ' ' + await renderer.send() + assert.equal(renderer.listMsgQueue('s1').length, 0) +}) + +test('queue strip renders one chip per queued message + counter, hides when empty', async () => { + const { renderer, document } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + const strip = document.getElementById('msg-queue-strip') + assert.equal(strip.hidden, true, 'hidden with an empty queue') + input.value = 'first'; await renderer.send() + input.value = 'second'; await renderer.send() + assert.equal(strip.hidden, false, 'shown once queued') + assert.equal(strip.querySelectorAll('.msg-queue-chip').length, 2) + const badge = strip.querySelector('.msg-queue-count') + assert.match(badge.textContent, /queued 2/) +}) + +test('turn/end auto-drains exactly one queued item through sendPrompt', async () => { + const { renderer, document, dsh } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + input.value = 'one'; await renderer.send() + input.value = 'two'; await renderer.send() + const before = sendCalls(dsh).length + renderer.onSessionEvent('s1', { type: 'turn/end', seq: 2 }) + // drain is async (dispatchPrompt awaits sendPrompt) — let microtasks settle. + await new Promise((r) => setTimeout(r, 5)) + const after = sendCalls(dsh) + assert.equal(after.length - before, 1, 'exactly one drained send') + assert.equal(after[after.length - 1][2], 'one', 'FIFO: head sent first') + assert.deepEqual(renderer.listMsgQueue('s1').map((x) => x.text), ['two'], + 'the second item waits for its own turn to finish') +}) + +test('a second turn/end drains the next item (one per completion)', async () => { + const { renderer, document, dsh } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + input.value = 'one'; await renderer.send() + input.value = 'two'; await renderer.send() + renderer.onSessionEvent('s1', { type: 'turn/end', seq: 2 }) + await new Promise((r) => setTimeout(r, 5)) + // the drained 'one' send flipped inflightTurn back on; simulate its turn. + renderer.onSessionEvent('s1', { type: 'turn/start', seq: 3 }) + renderer.onSessionEvent('s1', { type: 'turn/end', seq: 4 }) + await new Promise((r) => setTimeout(r, 5)) + const sent = sendCalls(dsh).map((c) => c[2]) + assert.deepEqual(sent, ['one', 'two'], 'both drained in FIFO order across two turns') + assert.equal(renderer.listMsgQueue('s1').length, 0) +}) + +test('turn/end + session.finished for one turn drains only once', async () => { + const { renderer, document, dsh, listeners } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + input.value = 'one'; await renderer.send() + input.value = 'two'; await renderer.send() + const before = sendCalls(dsh).length + // Some daemons emit both boundaries for the same turn. + renderer.onSessionEvent('s1', { type: 'turn/end', seq: 2 }) + await new Promise((r) => setTimeout(r, 5)) + listeners.onNotify({ method: 'session.finished', params: { sessionId: 's1', status: 'ok' } }) + await new Promise((r) => setTimeout(r, 5)) + assert.equal(sendCalls(dsh).length - before, 1, 'drain-once guard held across both signals') + assert.deepEqual(renderer.listMsgQueue('s1').map((x) => x.text), ['two']) +}) + +test('session.finished alone (no turn/end) still drains once', async () => { + const { renderer, document, dsh, listeners } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + input.value = 'only'; await renderer.send() + const before = sendCalls(dsh).length + listeners.onNotify({ method: 'session.finished', params: { sessionId: 's1', status: 'error' } }) + await new Promise((r) => setTimeout(r, 5)) + assert.equal(sendCalls(dsh).length - before, 1, 'error-path completion drained the head') +}) + +test('cancelled turn (turn/end after cancel) still drains — queue survives Cancel', async () => { + const { renderer, document, dsh } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + input.value = 'after cancel'; await renderer.send() + // User cancels; the wire still closes the turn with turn/end. + const before = sendCalls(dsh).length + renderer.onSessionEvent('s1', { type: 'turn/end', seq: 2 }) + await new Promise((r) => setTimeout(r, 5)) + assert.equal(sendCalls(dsh).length - before, 1, 'queued follow-up sent on the cancelled turn end') +}) + +test('per-session isolation: a background turn/end never touches the active composer', async () => { + const { renderer, document, dsh } = await loadRenderer() + // s1 active + in flight, one queued item. + const { input } = await activeInflight(renderer, document, 's1') + input.value = 's1-queued'; await renderer.send() + // s2 exists, has its own in-flight turn + queue, but is NOT active. + renderer.ensureSession('s2', { title: 's2', header: {}, hasUserMessage: true, running: true }) + renderer.getMsgQueue().enqueue('s2', 's2-queued') + // Arm s2's drain flag via a background turn/start (not the active session, + // so it doesn't touch the composer), then end it. + renderer.onSessionEvent('s2', { type: 'turn/start', seq: 8 }) + const before = sendCalls(dsh).length + renderer.onSessionEvent('s2', { type: 'turn/end', seq: 9 }) + await new Promise((r) => setTimeout(r, 5)) + const sent = sendCalls(dsh).slice(before).map((c) => [c[1], c[2]]) + assert.deepEqual(sent, [['s2', 's2-queued']], 'only s2 drained, sent against s2') + // s1's queue + composer untouched. + assert.deepEqual(renderer.listMsgQueue('s1').map((x) => x.text), ['s1-queued']) + const strip = document.getElementById('msg-queue-strip') + assert.equal(strip.querySelectorAll('.msg-queue-chip').length, 1, 'active strip still shows s1') +}) + +test('switching sessions shows the target session\'s queue (strict isolation)', async () => { + const { renderer, document } = await loadRenderer() + const { input } = await activeInflight(renderer, document, 's1') + input.value = 's1-only'; await renderer.send() + const strip = document.getElementById('msg-queue-strip') + assert.equal(strip.querySelectorAll('.msg-queue-chip').length, 1) + // Switch to a fresh idle session — its queue is empty, strip hides. + renderer.ensureSession('s2', { title: 's2', header: {}, hasUserMessage: true }) + await renderer.selectSession('s2') + assert.equal(strip.hidden, true, 'empty queue on s2 hides the strip') + // Switch back — s1's queue is intact. + await renderer.selectSession('s1') + assert.equal(strip.querySelectorAll('.msg-queue-chip').length, 1) +}) + +test('runtime restart (onInitialized) clears every queue and posts a notice', async () => { + const { renderer, document, listeners } = await loadRenderer() + const { input } = await activeInflight(renderer, document, 's1') + input.value = 'doomed'; await renderer.send() + renderer.getMsgQueue().enqueue('s2', 'also doomed') + // Fire the initialize handshake (new daemon / profile switch). + listeners.onInitialized({ serverInfo: { name: 'echo', version: '1' }, protocolVersion: 1 }) + await new Promise((r) => setTimeout(r, 5)) + assert.equal(renderer.getMsgQueue().size('s1'), 0) + assert.equal(renderer.getMsgQueue().size('s2'), 0) + assert.match(renderer.getStreamText(), /cleared 2 queued messages/) +}) + +test('drained send re-arms inflightTurn so a follow-up Enter queues again', async () => { + const { renderer, document, dsh } = await loadRenderer() + const { input } = await activeInflight(renderer, document) + input.value = 'one'; await renderer.send() + input.value = 'two'; await renderer.send() + renderer.onSessionEvent('s1', { type: 'turn/end', seq: 2 }) + await new Promise((r) => setTimeout(r, 5)) + // 'one' is now in flight (dispatchPrompt set inflightTurn). A new Enter + // should queue behind 'two', not fire a second concurrent sendPrompt. + const before = sendCalls(dsh).length + input.value = 'three'; await renderer.send() + assert.equal(sendCalls(dsh).length, before, 'follow-up queued, not sent concurrently') + assert.deepEqual(renderer.listMsgQueue('s1').map((x) => x.text), ['two', 'three']) +})