Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions apps/webapp/test/replay-after-crash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,19 @@ function textTurn(id: string, text: string): UIMessageChunk[] {
* via the webapp's real `generatePresignedUrl` (so snapshot reads
* hit a real S3-compatible backend).
* - `readSessionStreamRecords` returns the canonical
* `{ records: [{ data, id, seqNum }] }` shape — `data` is the
* JSON-encoded chunk body, mirroring the webapp's S2 record shape.
* `{ records: [{ data, id, seqNum }] }` shape. `data` is the parsed
* chunk OBJECT — the SDK writer puts the chunk object directly into
* the record envelope and the webapp route forwards it as-is, so
* the schema now declares `data: z.unknown()` and consumers use it
* without an extra `JSON.parse` step.
*/
function stubApiClient(opts: {
projectRef: string;
envSlug: string;
sessionOutChunks: unknown[];
}) {
const records = opts.sessionOutChunks.map((chunk, i) => ({
data: typeof chunk === "string" ? chunk : JSON.stringify(chunk),
data: chunk,
id: `evt-${i + 1}`,
seqNum: i + 1,
}));
Expand Down
15 changes: 11 additions & 4 deletions packages/core/src/v3/schemas/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1995,14 +1995,21 @@ export type SendInputStreamResponseBody = z.infer<typeof SendInputStreamResponse
* Response body for `GET /realtime/v1/sessions/:id/:io/records`. A non-SSE,
* `wait=0` drain of a session channel — used at run boot for snapshot
* replay where the SSE long-poll tax (~1s on empty streams) was the
* dominant cost. The shape mirrors the webapp's internal `StreamRecord`
* type (`apps/webapp/app/services/realtime/types.ts`); each record's
* `data` is a JSON-encoded chunk body that callers parse client-side.
* dominant cost.
*
* `data` is the parsed chunk body (the SDK writer puts the chunk object
* directly into the S2 record envelope; the route unwraps the envelope
* and forwards the inner object as-is). Callers use it directly — no
* additional JSON.parse step. Schema is `z.unknown()` because chunk
* shape varies by `chunk.type` (the AI SDK's `UIMessageChunk`
* discriminated union plus Trigger control records); consumers
* already runtime-check on the discriminator and tolerate malformed
* records by skipping them.
*/
export const ReadSessionStreamRecordsResponseBody = z.object({
records: z.array(
z.object({
data: z.string(),
data: z.unknown(),
Comment thread
ericallam marked this conversation as resolved.
id: z.string(),
seqNum: z.number(),
})
Expand Down
Loading
Loading