Skip to content

Commit 554940d

Browse files
committed
fix(webapp): parse stringified chunk envelope in peek-settled fast path
The S2 record envelope wraps the agent-written chunk as {data: <chunkAsString>, id: partId} because StreamsWriterV2 hands appendPart an already-stringified chunk. The peek-settled check treated envelope.data as an object, so typeof === 'object' always returned false and the trigger:turn-complete sentinel was never matched. Reconnect-on-reload silently degraded to the full long-poll path. Parse envelope.data once more so the type discriminator is surfaced.
1 parent e5f9dd1 commit 554940d

1 file changed

Lines changed: 11 additions & 5 deletions

File tree

apps/webapp/app/services/realtime/s2realtimeStreams.server.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -399,12 +399,18 @@ export class S2RealtimeStreams implements StreamResponder, StreamIngestor {
399399
};
400400
const record = json.records?.[0];
401401
if (!record) return null;
402-
// The record body is a JSON string `{data: <chunk>, id: partId}`
403-
// where `<chunk>` is the raw UIMessageChunk object (see
404-
// `StreamsWriterV2` — the agent-side writer serializes the chunk
405-
// object directly, not double-encoded). Unwrap the envelope and
406-
// return `data` as-is.
402+
// The record body is a JSON string `{data: <chunkAsString>, id: partId}`.
403+
// The agent-side writer (`StreamsWriterV2`) hands `appendPart` an
404+
// already-JSON-stringified chunk, so `data` round-trips as a string,
405+
// not an object. Parse it once more to surface the chunk shape.
407406
const envelope = JSON.parse(record.body) as { data: unknown; id: string };
407+
if (typeof envelope.data === "string") {
408+
try {
409+
return JSON.parse(envelope.data);
410+
} catch {
411+
return envelope.data;
412+
}
413+
}
408414
return envelope.data;
409415
} catch (err) {
410416
this.logger.warn("S2 peek last record: parse failed", { err, stream: s2Stream });

0 commit comments

Comments
 (0)