Progress and streams
Report typed progress and consume bounded live output.
Report bounded progress from a task so callers can follow the run without holding its execution open.
Progress is an observation channel, not a second source of truth. Define its shape and emit snapshots from the task:
/** Produces demonstration CSV through the ordinary order helper and emits validated progress around the durable export. */
import { defineTask } from "@relkit/app/tasks";
import { z } from "@relkit/app/schema";
import { exportOrderCsv } from "../helpers/fake-business.js";
const exportOrders = defineTask({
id: "orders.export-orders",
version: "1",
execution: "durable",
input: z.object({ orderIds: z.array(z.string()) }),
output: z.object({ csv: z.string(), exported: z.number().int().nonnegative() }),
progress: z.object({
completed: z.number().int().nonnegative(),
total: z.number().int().positive(),
}),
handler: async ({ orderIds }, context) => {
await context.progress.emit({ completed: 0, total: orderIds.length });
const csv = exportOrderCsv(orderIds);
await context.progress.emit({ completed: orderIds.length, total: orderIds.length });
return { csv, exported: orderIds.length };
},
});
export default exportOrders;
Progress and stream items are limited to 64 KiB. Keep payloads small, include a sequence or cursor when consumers need ordering, and store durable results in the task output or application database.
Streams may be live, durable, or unavailable depending on the binding. A reconnect can observe a gap when retention has expired; reset the UI from the latest run snapshot instead of replaying untrusted client state. Provider capability output is authoritative for the selected environment.
Progress does not authorize a client to read a private run. Apply the same ownership and field-level redaction rules to progress and stream payloads as to the final output.