import { z } from "zod"; export const UI_STREAM_HOST_CONTEXT_KEY = "pi-mcp-adapter/stream"; export const UI_STREAM_REQUEST_META_KEY = "pi-mcp-adapter/stream-token"; export const UI_STREAM_RESULT_PATCH_METHOD = "notifications/pi-mcp-adapter/ui-result-patch"; export const SERVER_STREAM_RESULT_PATCH_METHOD = "notifications/pi-mcp-adapter/result-patch"; export const UI_STREAM_STRUCTURED_CONTENT_KEY = "pi-mcp-adapter/stream"; export const uiStreamModeSchema = z.enum(["eager", "stream-first"]); export type UiStreamMode = z.infer; export const visualizationStreamPhaseSchema = z.enum(["shell", "narrative", "structure", "detail", "settled"]); export type VisualizationStreamPhase = z.infer; export const visualizationStreamFrameTypeSchema = z.enum(["patch", "checkpoint", "final"]); export type VisualizationStreamFrameType = z.infer; export const visualizationStreamStatusSchema = z.enum(["ok", "error"]); export type VisualizationStreamStatus = z.infer; const looseRecordSchema = z.record(z.string(), z.unknown()); const looseArraySchema = z.array(z.unknown()); export const uiStreamHostContextSchema = z.object({ mode: uiStreamModeSchema, streamId: z.string().min(1), intermediateResultPatches: z.boolean(), partialInput: z.boolean(), }); export type UiStreamHostContext = z.infer; export const visualizationStreamEnvelopeSchema = z.object({ streamId: z.string().min(1), sequence: z.number().int().nonnegative(), frameType: visualizationStreamFrameTypeSchema, phase: visualizationStreamPhaseSchema, status: visualizationStreamStatusSchema, message: z.string().optional(), spec: looseRecordSchema.optional(), checkpoint: looseRecordSchema.optional(), }); export type VisualizationStreamEnvelope = z.infer; export const uiStreamCallToolResultSchema = z.object({ content: looseArraySchema.optional(), structuredContent: looseRecordSchema.optional(), isError: z.boolean().optional(), _meta: looseRecordSchema.optional(), }).passthrough(); export type UiStreamCallToolResult = z.infer; export const uiStreamResultPatchNotificationSchema = z.object({ method: z.literal(UI_STREAM_RESULT_PATCH_METHOD), params: uiStreamCallToolResultSchema, }); export type UiStreamResultPatchNotification = z.infer; export const serverStreamResultPatchNotificationSchema = z.object({ method: z.literal(SERVER_STREAM_RESULT_PATCH_METHOD), params: z.object({ streamToken: z.string().min(1), result: uiStreamCallToolResultSchema, }), }); export type ServerStreamResultPatchNotification = z.infer; export interface UiStreamSummary { streamId: string; mode: UiStreamMode; frames: number; phases: VisualizationStreamPhase[]; finalStatus?: VisualizationStreamStatus; lastMessage?: string; } export function getUiStreamHostContext(hostContext: Record | undefined): UiStreamHostContext | undefined { const candidate = hostContext?.[UI_STREAM_HOST_CONTEXT_KEY]; const parsed = uiStreamHostContextSchema.safeParse(candidate); return parsed.success ? parsed.data : undefined; } export function getVisualizationStreamEnvelope(structuredContent: unknown): VisualizationStreamEnvelope | undefined { if (!structuredContent || typeof structuredContent !== "object" || Array.isArray(structuredContent)) { return undefined; } const candidate = (structuredContent as Record)[UI_STREAM_STRUCTURED_CONTENT_KEY]; const parsed = visualizationStreamEnvelopeSchema.safeParse(candidate); return parsed.success ? parsed.data : undefined; }