diff options
| author | Adam Malczewski <[email protected]> | 2026-06-12 14:58:33 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-12 14:58:33 +0900 |
| commit | b3d270803f95db2467e20bb742aa42faf6867f91 (patch) | |
| tree | 6abac5ec8ef76bcb95a4b9e93891cd3c87f451a6 /packages/wire/src | |
| parent | 86b5137c4f7f2bcc08f0580f1edaa05d14015e63 (diff) | |
| download | dispatch-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.ts | 20 |
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"; |
