summaryrefslogtreecommitdiffhomepage
path: root/packages/wire/src
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-12 14:58:33 +0900
committerAdam Malczewski <[email protected]>2026-06-12 14:58:33 +0900
commitb3d270803f95db2467e20bb742aa42faf6867f91 (patch)
tree6abac5ec8ef76bcb95a4b9e93891cd3c87f451a6 /packages/wire/src
parent86b5137c4f7f2bcc08f0580f1edaa05d14015e63 (diff)
downloaddispatch-b3d270803f95db2467e20bb742aa42faf6867f91.tar.gz
dispatch-b3d270803f95db2467e20bb742aa42faf6867f91.zip
fix(turns): emit user prompt on the turn event stream (CR-3)
A pure watcher (subscribed but not the sender) couldn't see the user prompt until the turn sealed: the user message was only persisted at seal and never entered the live/replayable stream. Add an additive TurnInputEvent {type:"user-message", conversationId, turnId, text} to the AgentEvent union and emit it via the broadcast/buffer path as the first event of every turn, so it is replayed to all subscribers (live + late-join) and on the HTTP path. Persistence and metrics unchanged; the union widening breaks no exhaustive switch. - @dispatch/wire 0.5.0->0.6.0; @dispatch/transport-contract 0.7.0->0.8.0 (re-export) - session-orchestrator: emit user-message at runTurnDetached start; +3 tests, 3 Wave-1 tests updated (user-message precedes turn-start) - FE courier: frontend-cr3-user-message-handoff.md Live-verified vs flash: watcher receives user-message (correct text) as its first chat.delta before turn-sealed. 894 vitest + transport bun green; tsc -b EXIT 0.
Diffstat (limited to 'packages/wire/src')
-rw-r--r--packages/wire/src/index.ts20
1 files changed, 20 insertions, 0 deletions
diff --git a/packages/wire/src/index.ts b/packages/wire/src/index.ts
index 52662ef..28aab87 100644
--- a/packages/wire/src/index.ts
+++ b/packages/wire/src/index.ts
@@ -213,6 +213,7 @@ export interface TurnMetrics {
export type AgentEvent =
| StatusEvent
| TurnStartEvent
+ | TurnInputEvent
| TurnTextDeltaEvent
| TurnReasoningDeltaEvent
| TurnToolCallEvent
@@ -238,6 +239,25 @@ export interface TurnStartEvent {
readonly turnId: string;
}
+/**
+ * The user prompt that opened this turn, surfaced INTO the turn's outward event
+ * stream. The user message is persisted only when the turn seals (atomically with
+ * the assistant reply), so without this event a client that is merely WATCHING a
+ * conversation (subscribed but not the sender) has no source for the prompt text
+ * mid-turn — it would see the streaming reply with no preceding user bubble until
+ * seal. Emitted once, as the FIRST event of the turn (before `turn-start`), so it
+ * is buffered and replayed to every subscriber — live and late-join — exactly like
+ * the rest of the turn. The sender already echoes its own prompt optimistically, so
+ * a consumer should de-dup against that (e.g. by text); a pure watcher renders it
+ * directly. Carries the raw prompt `text` (the same text passed to the provider).
+ */
+export interface TurnInputEvent {
+ readonly type: "user-message";
+ readonly conversationId: string;
+ readonly turnId: string;
+ readonly text: string;
+}
+
/** Incremental text content from the model during a turn. */
export interface TurnTextDeltaEvent {
readonly type: "text-delta";