@ai-matrx/agents 0.2.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../projection/workflow.ts"],"names":[],"mappings":"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","file":"workflow.cjs","sourcesContent":["import type { AgentProjectionRenderBlock } from \"./request\";\n\nexport interface WorkflowNodeStreamEvent {\n run_id: string;\n node_id?: string | null;\n kind:\n | \"chunk\"\n | \"reasoning\"\n | \"phase\"\n | \"tool\"\n | \"warning\"\n | \"record_update\"\n | \"resource_changed\"\n | \"render_block\";\n delta: string;\n stream_seq: number;\n ts: string;\n chunks_received: number;\n chars_streamed: number;\n frame_id?: string | null;\n frame_index?: number;\n frame_count?: number;\n block_shadowed?: boolean;\n}\n\nexport interface WorkflowRenderFrameSet {\n frameCount: number;\n slices: Record<number, string>;\n}\n\nexport interface WorkflowNodeProjection {\n runId: string;\n nodeId: string;\n answer: string;\n reasoning: string;\n blockShadowed: boolean;\n renderBlocks: Record<string, AgentProjectionRenderBlock>;\n renderBlockOrder: string[];\n openFrames: Record<string, WorkflowRenderFrameSet>;\n chunksReceived: number;\n charsStreamed: number;\n lastStreamingTs: string | null;\n lastPhase: string | null;\n lastTool: string | null;\n lastWarning: string | null;\n lastTransportSeq: number;\n}\n\nconst MAX_OPEN_FRAME_SETS = 32;\n\nexport function createWorkflowNodeProjection(input: {\n runId: string;\n nodeId: string;\n}): WorkflowNodeProjection {\n return {\n runId: input.runId,\n nodeId: input.nodeId,\n answer: \"\",\n reasoning: \"\",\n blockShadowed: false,\n renderBlocks: {},\n renderBlockOrder: [],\n openFrames: {},\n chunksReceived: 0,\n charsStreamed: 0,\n lastStreamingTs: null,\n lastPhase: null,\n lastTool: null,\n lastWarning: null,\n lastTransportSeq: 0,\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asString(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\n\nfunction asNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction toRenderBlock(value: unknown): AgentProjectionRenderBlock | null {\n const data = asRecord(value);\n if (!data) return null;\n const blockId = asString(data.blockId);\n const blockIndex = asNumber(data.blockIndex);\n const type = asString(data.type);\n if (blockId === null || blockIndex === null || type === null) return null;\n return {\n blockId,\n blockIndex,\n type,\n status:\n data.status === \"complete\" || data.status === \"error\"\n ? data.status\n : \"streaming\",\n content: asString(data.content),\n data: asRecord(data.data),\n metadata: asRecord(data.metadata),\n };\n}\n\nfunction projectRenderFrame(\n current: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n next: WorkflowNodeProjection,\n): WorkflowNodeProjection {\n const frameId = event.frame_id;\n const frameCount = event.frame_count ?? 1;\n const frameIndex = event.frame_index ?? 0;\n if (!frameId || frameCount < 1 || frameIndex < 0 || frameIndex >= frameCount) {\n return next;\n }\n const prior = current.openFrames[frameId];\n const set: WorkflowRenderFrameSet = {\n frameCount,\n slices: { ...(prior?.frameCount === frameCount ? prior.slices : {}), [frameIndex]: event.delta },\n };\n const openFrames = { ...current.openFrames, [frameId]: set };\n const keys = Object.keys(openFrames);\n const oldestKey = keys[0];\n if (keys.length > MAX_OPEN_FRAME_SETS && oldestKey !== undefined) {\n delete openFrames[oldestKey];\n }\n if (Object.keys(set.slices).length !== frameCount) return { ...next, openFrames };\n\n delete openFrames[frameId];\n let block: AgentProjectionRenderBlock | null = null;\n try {\n block = toRenderBlock(JSON.parse(\n Array.from({ length: frameCount }, (_, index) => set.slices[index] ?? \"\").join(\"\"),\n ));\n } catch {\n return { ...next, openFrames };\n }\n if (!block) return { ...next, openFrames };\n return {\n ...next,\n openFrames,\n renderBlocks: { ...current.renderBlocks, [block.blockId]: block },\n renderBlockOrder: Object.hasOwn(current.renderBlocks, block.blockId)\n ? current.renderBlockOrder\n : [...current.renderBlockOrder, block.blockId],\n };\n}\n\nexport function projectWorkflowNodeEvent(\n current: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n): WorkflowNodeProjection {\n if (event.run_id !== current.runId || (event.node_id ?? \"\") !== current.nodeId) return current;\n if (event.stream_seq <= current.lastTransportSeq) return current;\n const next: WorkflowNodeProjection = {\n ...current,\n chunksReceived: event.chunks_received,\n charsStreamed: event.chars_streamed,\n lastStreamingTs: event.ts,\n lastTransportSeq: event.stream_seq,\n blockShadowed: current.blockShadowed || Boolean(event.block_shadowed),\n };\n switch (event.kind) {\n case \"chunk\":\n return { ...next, answer: current.answer + event.delta };\n case \"reasoning\":\n return { ...next, reasoning: current.reasoning + event.delta };\n case \"phase\":\n return { ...next, lastPhase: event.delta };\n case \"tool\":\n return { ...next, lastTool: event.delta };\n case \"warning\":\n return { ...next, lastWarning: event.delta };\n case \"render_block\":\n return projectRenderFrame(current, event, next);\n default:\n return next;\n }\n}\n"]}
1
+ {"version":3,"sources":["../../projection/workflow.ts"],"names":[],"mappings":"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","file":"workflow.cjs","sourcesContent":["import type { AgentProjectionRenderBlock } from \"./request\";\n\nexport interface WorkflowNodeStreamEvent {\n run_id: string;\n node_id?: string | null;\n kind:\n | \"chunk\"\n | \"reasoning\"\n | \"phase\"\n | \"tool\"\n | \"warning\"\n | \"record_update\"\n | \"resource_changed\"\n | \"render_block\";\n delta: string;\n stream_seq: number;\n ts: string;\n chunks_received: number;\n chars_streamed: number;\n frame_id?: string | null;\n frame_index?: number;\n frame_count?: number;\n block_shadowed?: boolean;\n}\n\nexport interface WorkflowProjectionLimits {\n maxOpenFrameSets: number;\n maxFramesPerBlock: number;\n maxBytesPerBlock: number;\n}\n\nexport const DEFAULT_WORKFLOW_PROJECTION_LIMITS: Readonly<WorkflowProjectionLimits> = {\n maxOpenFrameSets: 32,\n maxFramesPerBlock: 256,\n maxBytesPerBlock: 1_048_576,\n};\n\nexport interface WorkflowRenderFrameSet {\n frameCount: number;\n slices: Record<number, string>;\n byteLength: number;\n}\n\nexport type WorkflowRenderBlockIssueCode =\n | \"invalid_frame_metadata\"\n | \"frame_count_mismatch\"\n | \"open_frame_limit_exceeded\"\n | \"frame_limit_exceeded\"\n | \"byte_limit_exceeded\"\n | \"malformed_json\"\n | \"invalid_render_block\";\n\nexport interface WorkflowRenderBlockIssue {\n code: WorkflowRenderBlockIssueCode;\n frameId: string | null;\n streamSeq: number;\n message: string;\n}\n\nexport interface WorkflowNodeProjection {\n runId: string;\n nodeId: string;\n answer: string;\n reasoning: string;\n blockShadowed: boolean;\n renderBlocks: Record<string, AgentProjectionRenderBlock>;\n renderBlockOrder: string[];\n openFrames: Record<string, WorkflowRenderFrameSet>;\n limits: WorkflowProjectionLimits;\n lastRenderBlockIssue: WorkflowRenderBlockIssue | null;\n chunksReceived: number;\n charsStreamed: number;\n lastStreamingTs: string | null;\n lastPhase: string | null;\n lastTool: string | null;\n lastWarning: string | null;\n lastTransportSeq: number;\n}\n\nfunction positiveSafeInteger(name: string, value: number): number {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new RangeError(`${name} must be a positive safe integer`);\n }\n return value;\n}\n\nfunction resolveLimits(\n limits: Partial<WorkflowProjectionLimits> | undefined,\n): WorkflowProjectionLimits {\n return {\n maxOpenFrameSets: positiveSafeInteger(\n \"maxOpenFrameSets\",\n limits?.maxOpenFrameSets ??\n DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxOpenFrameSets,\n ),\n maxFramesPerBlock: positiveSafeInteger(\n \"maxFramesPerBlock\",\n limits?.maxFramesPerBlock ??\n DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxFramesPerBlock,\n ),\n maxBytesPerBlock: positiveSafeInteger(\n \"maxBytesPerBlock\",\n limits?.maxBytesPerBlock ??\n DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxBytesPerBlock,\n ),\n };\n}\n\nexport function createWorkflowNodeProjection(input: {\n runId: string;\n nodeId: string;\n limits?: Partial<WorkflowProjectionLimits>;\n}): WorkflowNodeProjection {\n return {\n runId: input.runId,\n nodeId: input.nodeId,\n answer: \"\",\n reasoning: \"\",\n blockShadowed: false,\n renderBlocks: {},\n renderBlockOrder: [],\n openFrames: {},\n limits: resolveLimits(input.limits),\n lastRenderBlockIssue: null,\n chunksReceived: 0,\n charsStreamed: 0,\n lastStreamingTs: null,\n lastPhase: null,\n lastTool: null,\n lastWarning: null,\n lastTransportSeq: 0,\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asString(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\n\nfunction asNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction utf8ByteLength(value: string): number {\n return new TextEncoder().encode(value).byteLength;\n}\n\nfunction toRenderBlock(value: unknown): AgentProjectionRenderBlock | null {\n const data = asRecord(value);\n if (!data) return null;\n const blockId = asString(data.blockId);\n const blockIndex = asNumber(data.blockIndex);\n const type = asString(data.type);\n if (blockId === null || blockIndex === null || type === null) return null;\n return {\n blockId,\n blockIndex,\n type,\n status:\n data.status === \"complete\" || data.status === \"error\"\n ? data.status\n : \"streaming\",\n content: asString(data.content),\n data: asRecord(data.data),\n metadata: asRecord(data.metadata),\n };\n}\n\nfunction withIssue(\n next: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n code: WorkflowRenderBlockIssueCode,\n frameId: string | null,\n message: string,\n openFrames: Record<string, WorkflowRenderFrameSet> = next.openFrames,\n): WorkflowNodeProjection {\n return {\n ...next,\n openFrames,\n lastRenderBlockIssue: {\n code,\n frameId,\n streamSeq: event.stream_seq,\n message,\n },\n };\n}\n\nfunction withoutFrame(\n frames: Record<string, WorkflowRenderFrameSet>,\n frameId: string,\n): Record<string, WorkflowRenderFrameSet> {\n if (!Object.hasOwn(frames, frameId)) return frames;\n const next = { ...frames };\n delete next[frameId];\n return next;\n}\n\nfunction projectRenderFrame(\n current: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n next: WorkflowNodeProjection,\n): WorkflowNodeProjection {\n const frameId = event.frame_id;\n const frameCount = event.frame_count ?? 1;\n const frameIndex = event.frame_index ?? 0;\n if (\n !frameId ||\n !Number.isSafeInteger(frameCount) ||\n !Number.isSafeInteger(frameIndex) ||\n frameCount < 1 ||\n frameIndex < 0 ||\n frameIndex >= frameCount\n ) {\n return withIssue(\n next,\n event,\n \"invalid_frame_metadata\",\n frameId ?? null,\n \"Render block frame metadata is invalid\",\n );\n }\n if (frameCount > current.limits.maxFramesPerBlock) {\n return withIssue(\n next,\n event,\n \"frame_limit_exceeded\",\n frameId,\n `Render block declares ${frameCount} frames; limit is ${current.limits.maxFramesPerBlock}`,\n withoutFrame(current.openFrames, frameId),\n );\n }\n\n const prior = current.openFrames[frameId];\n if (prior && prior.frameCount !== frameCount) {\n return withIssue(\n next,\n event,\n \"frame_count_mismatch\",\n frameId,\n `Render block frame count changed from ${prior.frameCount} to ${frameCount}`,\n withoutFrame(current.openFrames, frameId),\n );\n }\n\n const priorSlice = prior?.slices[frameIndex];\n const byteLength =\n (prior?.byteLength ?? 0) -\n (priorSlice === undefined ? 0 : utf8ByteLength(priorSlice)) +\n utf8ByteLength(event.delta);\n if (byteLength > current.limits.maxBytesPerBlock) {\n return withIssue(\n next,\n event,\n \"byte_limit_exceeded\",\n frameId,\n `Render block exceeds ${current.limits.maxBytesPerBlock} UTF-8 bytes`,\n withoutFrame(current.openFrames, frameId),\n );\n }\n\n const set: WorkflowRenderFrameSet = {\n frameCount,\n slices: { ...(prior?.slices ?? {}), [frameIndex]: event.delta },\n byteLength,\n };\n const openFrames = { ...current.openFrames };\n let limitIssue: WorkflowRenderBlockIssue | null = null;\n if (!prior && Object.keys(openFrames).length >= current.limits.maxOpenFrameSets) {\n const oldestFrameId = Object.keys(openFrames)[0];\n if (oldestFrameId !== undefined) {\n delete openFrames[oldestFrameId];\n limitIssue = {\n code: \"open_frame_limit_exceeded\",\n frameId: oldestFrameId,\n streamSeq: event.stream_seq,\n message: `Evicted incomplete render block after reaching ${current.limits.maxOpenFrameSets} open frame sets`,\n };\n }\n }\n openFrames[frameId] = set;\n if (Object.keys(set.slices).length !== frameCount) {\n return {\n ...next,\n openFrames,\n lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue,\n };\n }\n\n delete openFrames[frameId];\n const serialized = Array.from(\n { length: frameCount },\n (_, index) => set.slices[index] ?? \"\",\n ).join(\"\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(serialized) as unknown;\n } catch (error) {\n const detail = error instanceof Error ? `: ${error.message}` : \"\";\n return withIssue(\n next,\n event,\n \"malformed_json\",\n frameId,\n `Completed render block is not valid JSON${detail}`,\n openFrames,\n );\n }\n const block = toRenderBlock(parsed);\n if (!block) {\n return withIssue(\n next,\n event,\n \"invalid_render_block\",\n frameId,\n \"Completed render block is missing blockId, blockIndex, or type\",\n openFrames,\n );\n }\n return {\n ...next,\n openFrames,\n lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue,\n renderBlocks: { ...current.renderBlocks, [block.blockId]: block },\n renderBlockOrder: Object.hasOwn(current.renderBlocks, block.blockId)\n ? current.renderBlockOrder\n : [...current.renderBlockOrder, block.blockId],\n };\n}\n\nexport function projectWorkflowNodeEvent(\n current: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n): WorkflowNodeProjection {\n if (\n event.run_id !== current.runId ||\n (event.node_id ?? \"\") !== current.nodeId\n ) {\n return current;\n }\n if (event.stream_seq <= current.lastTransportSeq) return current;\n const next: WorkflowNodeProjection = {\n ...current,\n chunksReceived: event.chunks_received,\n charsStreamed: event.chars_streamed,\n lastStreamingTs: event.ts,\n lastTransportSeq: event.stream_seq,\n blockShadowed: current.blockShadowed || Boolean(event.block_shadowed),\n };\n switch (event.kind) {\n case \"chunk\":\n return { ...next, answer: current.answer + event.delta };\n case \"reasoning\":\n return { ...next, reasoning: current.reasoning + event.delta };\n case \"phase\":\n return { ...next, lastPhase: event.delta };\n case \"tool\":\n return { ...next, lastTool: event.delta };\n case \"warning\":\n return { ...next, lastWarning: event.delta };\n case \"render_block\":\n return projectRenderFrame(current, event, next);\n default:\n return next;\n }\n}\n"]}
@@ -14,9 +14,23 @@ interface WorkflowNodeStreamEvent {
14
14
  frame_count?: number;
15
15
  block_shadowed?: boolean;
16
16
  }
17
+ interface WorkflowProjectionLimits {
18
+ maxOpenFrameSets: number;
19
+ maxFramesPerBlock: number;
20
+ maxBytesPerBlock: number;
21
+ }
22
+ declare const DEFAULT_WORKFLOW_PROJECTION_LIMITS: Readonly<WorkflowProjectionLimits>;
17
23
  interface WorkflowRenderFrameSet {
18
24
  frameCount: number;
19
25
  slices: Record<number, string>;
26
+ byteLength: number;
27
+ }
28
+ type WorkflowRenderBlockIssueCode = "invalid_frame_metadata" | "frame_count_mismatch" | "open_frame_limit_exceeded" | "frame_limit_exceeded" | "byte_limit_exceeded" | "malformed_json" | "invalid_render_block";
29
+ interface WorkflowRenderBlockIssue {
30
+ code: WorkflowRenderBlockIssueCode;
31
+ frameId: string | null;
32
+ streamSeq: number;
33
+ message: string;
20
34
  }
21
35
  interface WorkflowNodeProjection {
22
36
  runId: string;
@@ -27,6 +41,8 @@ interface WorkflowNodeProjection {
27
41
  renderBlocks: Record<string, AgentProjectionRenderBlock>;
28
42
  renderBlockOrder: string[];
29
43
  openFrames: Record<string, WorkflowRenderFrameSet>;
44
+ limits: WorkflowProjectionLimits;
45
+ lastRenderBlockIssue: WorkflowRenderBlockIssue | null;
30
46
  chunksReceived: number;
31
47
  charsStreamed: number;
32
48
  lastStreamingTs: string | null;
@@ -38,7 +54,8 @@ interface WorkflowNodeProjection {
38
54
  declare function createWorkflowNodeProjection(input: {
39
55
  runId: string;
40
56
  nodeId: string;
57
+ limits?: Partial<WorkflowProjectionLimits>;
41
58
  }): WorkflowNodeProjection;
42
59
  declare function projectWorkflowNodeEvent(current: WorkflowNodeProjection, event: WorkflowNodeStreamEvent): WorkflowNodeProjection;
43
60
 
44
- export { type WorkflowNodeProjection, type WorkflowNodeStreamEvent, type WorkflowRenderFrameSet, createWorkflowNodeProjection, projectWorkflowNodeEvent };
61
+ export { DEFAULT_WORKFLOW_PROJECTION_LIMITS, type WorkflowNodeProjection, type WorkflowNodeStreamEvent, type WorkflowProjectionLimits, type WorkflowRenderBlockIssue, type WorkflowRenderBlockIssueCode, type WorkflowRenderFrameSet, createWorkflowNodeProjection, projectWorkflowNodeEvent };
@@ -14,9 +14,23 @@ interface WorkflowNodeStreamEvent {
14
14
  frame_count?: number;
15
15
  block_shadowed?: boolean;
16
16
  }
17
+ interface WorkflowProjectionLimits {
18
+ maxOpenFrameSets: number;
19
+ maxFramesPerBlock: number;
20
+ maxBytesPerBlock: number;
21
+ }
22
+ declare const DEFAULT_WORKFLOW_PROJECTION_LIMITS: Readonly<WorkflowProjectionLimits>;
17
23
  interface WorkflowRenderFrameSet {
18
24
  frameCount: number;
19
25
  slices: Record<number, string>;
26
+ byteLength: number;
27
+ }
28
+ type WorkflowRenderBlockIssueCode = "invalid_frame_metadata" | "frame_count_mismatch" | "open_frame_limit_exceeded" | "frame_limit_exceeded" | "byte_limit_exceeded" | "malformed_json" | "invalid_render_block";
29
+ interface WorkflowRenderBlockIssue {
30
+ code: WorkflowRenderBlockIssueCode;
31
+ frameId: string | null;
32
+ streamSeq: number;
33
+ message: string;
20
34
  }
21
35
  interface WorkflowNodeProjection {
22
36
  runId: string;
@@ -27,6 +41,8 @@ interface WorkflowNodeProjection {
27
41
  renderBlocks: Record<string, AgentProjectionRenderBlock>;
28
42
  renderBlockOrder: string[];
29
43
  openFrames: Record<string, WorkflowRenderFrameSet>;
44
+ limits: WorkflowProjectionLimits;
45
+ lastRenderBlockIssue: WorkflowRenderBlockIssue | null;
30
46
  chunksReceived: number;
31
47
  charsStreamed: number;
32
48
  lastStreamingTs: string | null;
@@ -38,7 +54,8 @@ interface WorkflowNodeProjection {
38
54
  declare function createWorkflowNodeProjection(input: {
39
55
  runId: string;
40
56
  nodeId: string;
57
+ limits?: Partial<WorkflowProjectionLimits>;
41
58
  }): WorkflowNodeProjection;
42
59
  declare function projectWorkflowNodeEvent(current: WorkflowNodeProjection, event: WorkflowNodeStreamEvent): WorkflowNodeProjection;
43
60
 
44
- export { type WorkflowNodeProjection, type WorkflowNodeStreamEvent, type WorkflowRenderFrameSet, createWorkflowNodeProjection, projectWorkflowNodeEvent };
61
+ export { DEFAULT_WORKFLOW_PROJECTION_LIMITS, type WorkflowNodeProjection, type WorkflowNodeStreamEvent, type WorkflowProjectionLimits, type WorkflowRenderBlockIssue, type WorkflowRenderBlockIssueCode, type WorkflowRenderFrameSet, createWorkflowNodeProjection, projectWorkflowNodeEvent };
@@ -1,5 +1,31 @@
1
1
  // projection/workflow.ts
2
- var MAX_OPEN_FRAME_SETS = 32;
2
+ var DEFAULT_WORKFLOW_PROJECTION_LIMITS = {
3
+ maxOpenFrameSets: 32,
4
+ maxFramesPerBlock: 256,
5
+ maxBytesPerBlock: 1048576
6
+ };
7
+ function positiveSafeInteger(name, value) {
8
+ if (!Number.isSafeInteger(value) || value < 1) {
9
+ throw new RangeError(`${name} must be a positive safe integer`);
10
+ }
11
+ return value;
12
+ }
13
+ function resolveLimits(limits) {
14
+ return {
15
+ maxOpenFrameSets: positiveSafeInteger(
16
+ "maxOpenFrameSets",
17
+ limits?.maxOpenFrameSets ?? DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxOpenFrameSets
18
+ ),
19
+ maxFramesPerBlock: positiveSafeInteger(
20
+ "maxFramesPerBlock",
21
+ limits?.maxFramesPerBlock ?? DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxFramesPerBlock
22
+ ),
23
+ maxBytesPerBlock: positiveSafeInteger(
24
+ "maxBytesPerBlock",
25
+ limits?.maxBytesPerBlock ?? DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxBytesPerBlock
26
+ )
27
+ };
28
+ }
3
29
  function createWorkflowNodeProjection(input) {
4
30
  return {
5
31
  runId: input.runId,
@@ -10,6 +36,8 @@ function createWorkflowNodeProjection(input) {
10
36
  renderBlocks: {},
11
37
  renderBlockOrder: [],
12
38
  openFrames: {},
39
+ limits: resolveLimits(input.limits),
40
+ lastRenderBlockIssue: null,
13
41
  chunksReceived: 0,
14
42
  charsStreamed: 0,
15
43
  lastStreamingTs: null,
@@ -28,6 +56,9 @@ function asString(value) {
28
56
  function asNumber(value) {
29
57
  return typeof value === "number" && Number.isFinite(value) ? value : null;
30
58
  }
59
+ function utf8ByteLength(value) {
60
+ return new TextEncoder().encode(value).byteLength;
61
+ }
31
62
  function toRenderBlock(value) {
32
63
  const data = asRecord(value);
33
64
  if (!data) return null;
@@ -45,44 +76,139 @@ function toRenderBlock(value) {
45
76
  metadata: asRecord(data.metadata)
46
77
  };
47
78
  }
79
+ function withIssue(next, event, code, frameId, message, openFrames = next.openFrames) {
80
+ return {
81
+ ...next,
82
+ openFrames,
83
+ lastRenderBlockIssue: {
84
+ code,
85
+ frameId,
86
+ streamSeq: event.stream_seq,
87
+ message
88
+ }
89
+ };
90
+ }
91
+ function withoutFrame(frames, frameId) {
92
+ if (!Object.hasOwn(frames, frameId)) return frames;
93
+ const next = { ...frames };
94
+ delete next[frameId];
95
+ return next;
96
+ }
48
97
  function projectRenderFrame(current, event, next) {
49
98
  const frameId = event.frame_id;
50
99
  const frameCount = event.frame_count ?? 1;
51
100
  const frameIndex = event.frame_index ?? 0;
52
- if (!frameId || frameCount < 1 || frameIndex < 0 || frameIndex >= frameCount) {
53
- return next;
101
+ if (!frameId || !Number.isSafeInteger(frameCount) || !Number.isSafeInteger(frameIndex) || frameCount < 1 || frameIndex < 0 || frameIndex >= frameCount) {
102
+ return withIssue(
103
+ next,
104
+ event,
105
+ "invalid_frame_metadata",
106
+ frameId ?? null,
107
+ "Render block frame metadata is invalid"
108
+ );
109
+ }
110
+ if (frameCount > current.limits.maxFramesPerBlock) {
111
+ return withIssue(
112
+ next,
113
+ event,
114
+ "frame_limit_exceeded",
115
+ frameId,
116
+ `Render block declares ${frameCount} frames; limit is ${current.limits.maxFramesPerBlock}`,
117
+ withoutFrame(current.openFrames, frameId)
118
+ );
54
119
  }
55
120
  const prior = current.openFrames[frameId];
121
+ if (prior && prior.frameCount !== frameCount) {
122
+ return withIssue(
123
+ next,
124
+ event,
125
+ "frame_count_mismatch",
126
+ frameId,
127
+ `Render block frame count changed from ${prior.frameCount} to ${frameCount}`,
128
+ withoutFrame(current.openFrames, frameId)
129
+ );
130
+ }
131
+ const priorSlice = prior?.slices[frameIndex];
132
+ const byteLength = (prior?.byteLength ?? 0) - (priorSlice === void 0 ? 0 : utf8ByteLength(priorSlice)) + utf8ByteLength(event.delta);
133
+ if (byteLength > current.limits.maxBytesPerBlock) {
134
+ return withIssue(
135
+ next,
136
+ event,
137
+ "byte_limit_exceeded",
138
+ frameId,
139
+ `Render block exceeds ${current.limits.maxBytesPerBlock} UTF-8 bytes`,
140
+ withoutFrame(current.openFrames, frameId)
141
+ );
142
+ }
56
143
  const set = {
57
144
  frameCount,
58
- slices: { ...prior?.frameCount === frameCount ? prior.slices : {}, [frameIndex]: event.delta }
145
+ slices: { ...prior?.slices ?? {}, [frameIndex]: event.delta },
146
+ byteLength
59
147
  };
60
- const openFrames = { ...current.openFrames, [frameId]: set };
61
- const keys = Object.keys(openFrames);
62
- const oldestKey = keys[0];
63
- if (keys.length > MAX_OPEN_FRAME_SETS && oldestKey !== void 0) {
64
- delete openFrames[oldestKey];
148
+ const openFrames = { ...current.openFrames };
149
+ let limitIssue = null;
150
+ if (!prior && Object.keys(openFrames).length >= current.limits.maxOpenFrameSets) {
151
+ const oldestFrameId = Object.keys(openFrames)[0];
152
+ if (oldestFrameId !== void 0) {
153
+ delete openFrames[oldestFrameId];
154
+ limitIssue = {
155
+ code: "open_frame_limit_exceeded",
156
+ frameId: oldestFrameId,
157
+ streamSeq: event.stream_seq,
158
+ message: `Evicted incomplete render block after reaching ${current.limits.maxOpenFrameSets} open frame sets`
159
+ };
160
+ }
161
+ }
162
+ openFrames[frameId] = set;
163
+ if (Object.keys(set.slices).length !== frameCount) {
164
+ return {
165
+ ...next,
166
+ openFrames,
167
+ lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue
168
+ };
65
169
  }
66
- if (Object.keys(set.slices).length !== frameCount) return { ...next, openFrames };
67
170
  delete openFrames[frameId];
68
- let block = null;
171
+ const serialized = Array.from(
172
+ { length: frameCount },
173
+ (_, index) => set.slices[index] ?? ""
174
+ ).join("");
175
+ let parsed;
69
176
  try {
70
- block = toRenderBlock(JSON.parse(
71
- Array.from({ length: frameCount }, (_, index) => set.slices[index] ?? "").join("")
72
- ));
73
- } catch {
74
- return { ...next, openFrames };
177
+ parsed = JSON.parse(serialized);
178
+ } catch (error) {
179
+ const detail = error instanceof Error ? `: ${error.message}` : "";
180
+ return withIssue(
181
+ next,
182
+ event,
183
+ "malformed_json",
184
+ frameId,
185
+ `Completed render block is not valid JSON${detail}`,
186
+ openFrames
187
+ );
188
+ }
189
+ const block = toRenderBlock(parsed);
190
+ if (!block) {
191
+ return withIssue(
192
+ next,
193
+ event,
194
+ "invalid_render_block",
195
+ frameId,
196
+ "Completed render block is missing blockId, blockIndex, or type",
197
+ openFrames
198
+ );
75
199
  }
76
- if (!block) return { ...next, openFrames };
77
200
  return {
78
201
  ...next,
79
202
  openFrames,
203
+ lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue,
80
204
  renderBlocks: { ...current.renderBlocks, [block.blockId]: block },
81
205
  renderBlockOrder: Object.hasOwn(current.renderBlocks, block.blockId) ? current.renderBlockOrder : [...current.renderBlockOrder, block.blockId]
82
206
  };
83
207
  }
84
208
  function projectWorkflowNodeEvent(current, event) {
85
- if (event.run_id !== current.runId || (event.node_id ?? "") !== current.nodeId) return current;
209
+ if (event.run_id !== current.runId || (event.node_id ?? "") !== current.nodeId) {
210
+ return current;
211
+ }
86
212
  if (event.stream_seq <= current.lastTransportSeq) return current;
87
213
  const next = {
88
214
  ...current,
@@ -110,6 +236,6 @@ function projectWorkflowNodeEvent(current, event) {
110
236
  }
111
237
  }
112
238
 
113
- export { createWorkflowNodeProjection, projectWorkflowNodeEvent };
239
+ export { DEFAULT_WORKFLOW_PROJECTION_LIMITS, createWorkflowNodeProjection, projectWorkflowNodeEvent };
114
240
  //# sourceMappingURL=workflow.js.map
115
241
  //# sourceMappingURL=workflow.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../projection/workflow.ts"],"names":[],"mappings":";AAgDA,IAAM,mBAAA,GAAsB,EAAA;AAErB,SAAS,6BAA6B,KAAA,EAGlB;AACzB,EAAA,OAAO;AAAA,IACL,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,MAAA,EAAQ,EAAA;AAAA,IACR,SAAA,EAAW,EAAA;AAAA,IACX,aAAA,EAAe,KAAA;AAAA,IACf,cAAc,EAAC;AAAA,IACf,kBAAkB,EAAC;AAAA,IACnB,YAAY,EAAC;AAAA,IACb,cAAA,EAAgB,CAAA;AAAA,IAChB,aAAA,EAAe,CAAA;AAAA,IACf,eAAA,EAAiB,IAAA;AAAA,IACjB,SAAA,EAAW,IAAA;AAAA,IACX,QAAA,EAAU,IAAA;AAAA,IACV,WAAA,EAAa,IAAA;AAAA,IACb,gBAAA,EAAkB;AAAA,GACpB;AACF;AAEA,SAAS,SAAS,KAAA,EAAgD;AAChE,EAAA,OAAO,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GACrE,KAAA,GACD,IAAA;AACN;AAEA,SAAS,SAAS,KAAA,EAA+B;AAC/C,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,IAAA;AAC7C;AAEA,SAAS,SAAS,KAAA,EAA+B;AAC/C,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,OAAO,QAAA,CAAS,KAAK,IAAI,KAAA,GAAQ,IAAA;AACvE;AAEA,SAAS,cAAc,KAAA,EAAmD;AACxE,EAAA,MAAM,IAAA,GAAO,SAAS,KAAK,CAAA;AAC3B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,IAAA,CAAK,OAAO,CAAA;AACrC,EAAA,MAAM,UAAA,GAAa,QAAA,CAAS,IAAA,CAAK,UAAU,CAAA;AAC3C,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,IAAA,CAAK,IAAI,CAAA;AAC/B,EAAA,IAAI,YAAY,IAAA,IAAQ,UAAA,KAAe,IAAA,IAAQ,IAAA,KAAS,MAAM,OAAO,IAAA;AACrE,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,UAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA,EACE,KAAK,MAAA,KAAW,UAAA,IAAc,KAAK,MAAA,KAAW,OAAA,GAC1C,KAAK,MAAA,GACL,WAAA;AAAA,IACN,OAAA,EAAS,QAAA,CAAS,IAAA,CAAK,OAAO,CAAA;AAAA,IAC9B,IAAA,EAAM,QAAA,CAAS,IAAA,CAAK,IAAI,CAAA;AAAA,IACxB,QAAA,EAAU,QAAA,CAAS,IAAA,CAAK,QAAQ;AAAA,GAClC;AACF;AAEA,SAAS,kBAAA,CACP,OAAA,EACA,KAAA,EACA,IAAA,EACwB;AACxB,EAAA,MAAM,UAAU,KAAA,CAAM,QAAA;AACtB,EAAA,MAAM,UAAA,GAAa,MAAM,WAAA,IAAe,CAAA;AACxC,EAAA,MAAM,UAAA,GAAa,MAAM,WAAA,IAAe,CAAA;AACxC,EAAA,IAAI,CAAC,OAAA,IAAW,UAAA,GAAa,KAAK,UAAA,GAAa,CAAA,IAAK,cAAc,UAAA,EAAY;AAC5E,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,UAAA,CAAW,OAAO,CAAA;AACxC,EAAA,MAAM,GAAA,GAA8B;AAAA,IAClC,UAAA;AAAA,IACA,MAAA,EAAQ,EAAE,GAAI,KAAA,EAAO,eAAe,UAAA,GAAa,KAAA,CAAM,MAAA,GAAS,EAAC,EAAI,CAAC,UAAU,GAAG,MAAM,KAAA;AAAM,GACjG;AACA,EAAA,MAAM,UAAA,GAAa,EAAE,GAAG,OAAA,CAAQ,YAAY,CAAC,OAAO,GAAG,GAAA,EAAI;AAC3D,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA;AACnC,EAAA,MAAM,SAAA,GAAY,KAAK,CAAC,CAAA;AACxB,EAAA,IAAI,IAAA,CAAK,MAAA,GAAS,mBAAA,IAAuB,SAAA,KAAc,MAAA,EAAW;AAChE,IAAA,OAAO,WAAW,SAAS,CAAA;AAAA,EAC7B;AACA,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,CAAE,MAAA,KAAW,UAAA,EAAY,OAAO,EAAE,GAAG,IAAA,EAAM,UAAA,EAAW;AAEhF,EAAA,OAAO,WAAW,OAAO,CAAA;AACzB,EAAA,IAAI,KAAA,GAA2C,IAAA;AAC/C,EAAA,IAAI;AACF,IAAA,KAAA,GAAQ,cAAc,IAAA,CAAK,KAAA;AAAA,MACzB,MAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,UAAA,IAAc,CAAC,CAAA,EAAG,KAAA,KAAU,GAAA,CAAI,OAAO,KAAK,CAAA,IAAK,EAAE,CAAA,CAAE,KAAK,EAAE;AAAA,KAClF,CAAA;AAAA,EACH,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAE,GAAG,IAAA,EAAM,UAAA,EAAW;AAAA,EAC/B;AACA,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAE,GAAG,MAAM,UAAA,EAAW;AACzC,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,UAAA;AAAA,IACA,YAAA,EAAc,EAAE,GAAG,OAAA,CAAQ,cAAc,CAAC,KAAA,CAAM,OAAO,GAAG,KAAA,EAAM;AAAA,IAChE,gBAAA,EAAkB,MAAA,CAAO,MAAA,CAAO,OAAA,CAAQ,cAAc,KAAA,CAAM,OAAO,CAAA,GAC/D,OAAA,CAAQ,mBACR,CAAC,GAAG,OAAA,CAAQ,gBAAA,EAAkB,MAAM,OAAO;AAAA,GACjD;AACF;AAEO,SAAS,wBAAA,CACd,SACA,KAAA,EACwB;AACxB,EAAA,IAAI,KAAA,CAAM,WAAW,OAAA,CAAQ,KAAA,IAAA,CAAU,MAAM,OAAA,IAAW,EAAA,MAAQ,OAAA,CAAQ,MAAA,EAAQ,OAAO,OAAA;AACvF,EAAA,IAAI,KAAA,CAAM,UAAA,IAAc,OAAA,CAAQ,gBAAA,EAAkB,OAAO,OAAA;AACzD,EAAA,MAAM,IAAA,GAA+B;AAAA,IACnC,GAAG,OAAA;AAAA,IACH,gBAAgB,KAAA,CAAM,eAAA;AAAA,IACtB,eAAe,KAAA,CAAM,cAAA;AAAA,IACrB,iBAAiB,KAAA,CAAM,EAAA;AAAA,IACvB,kBAAkB,KAAA,CAAM,UAAA;AAAA,IACxB,aAAA,EAAe,OAAA,CAAQ,aAAA,IAAiB,OAAA,CAAQ,MAAM,cAAc;AAAA,GACtE;AACA,EAAA,QAAQ,MAAM,IAAA;AAAM,IAClB,KAAK,OAAA;AACH,MAAA,OAAO,EAAE,GAAG,IAAA,EAAM,QAAQ,OAAA,CAAQ,MAAA,GAAS,MAAM,KAAA,EAAM;AAAA,IACzD,KAAK,WAAA;AACH,MAAA,OAAO,EAAE,GAAG,IAAA,EAAM,WAAW,OAAA,CAAQ,SAAA,GAAY,MAAM,KAAA,EAAM;AAAA,IAC/D,KAAK,OAAA;AACH,MAAA,OAAO,EAAE,GAAG,IAAA,EAAM,SAAA,EAAW,MAAM,KAAA,EAAM;AAAA,IAC3C,KAAK,MAAA;AACH,MAAA,OAAO,EAAE,GAAG,IAAA,EAAM,QAAA,EAAU,MAAM,KAAA,EAAM;AAAA,IAC1C,KAAK,SAAA;AACH,MAAA,OAAO,EAAE,GAAG,IAAA,EAAM,WAAA,EAAa,MAAM,KAAA,EAAM;AAAA,IAC7C,KAAK,cAAA;AACH,MAAA,OAAO,kBAAA,CAAmB,OAAA,EAAS,KAAA,EAAO,IAAI,CAAA;AAAA,IAChD;AACE,MAAA,OAAO,IAAA;AAAA;AAEb","file":"workflow.js","sourcesContent":["import type { AgentProjectionRenderBlock } from \"./request\";\n\nexport interface WorkflowNodeStreamEvent {\n run_id: string;\n node_id?: string | null;\n kind:\n | \"chunk\"\n | \"reasoning\"\n | \"phase\"\n | \"tool\"\n | \"warning\"\n | \"record_update\"\n | \"resource_changed\"\n | \"render_block\";\n delta: string;\n stream_seq: number;\n ts: string;\n chunks_received: number;\n chars_streamed: number;\n frame_id?: string | null;\n frame_index?: number;\n frame_count?: number;\n block_shadowed?: boolean;\n}\n\nexport interface WorkflowRenderFrameSet {\n frameCount: number;\n slices: Record<number, string>;\n}\n\nexport interface WorkflowNodeProjection {\n runId: string;\n nodeId: string;\n answer: string;\n reasoning: string;\n blockShadowed: boolean;\n renderBlocks: Record<string, AgentProjectionRenderBlock>;\n renderBlockOrder: string[];\n openFrames: Record<string, WorkflowRenderFrameSet>;\n chunksReceived: number;\n charsStreamed: number;\n lastStreamingTs: string | null;\n lastPhase: string | null;\n lastTool: string | null;\n lastWarning: string | null;\n lastTransportSeq: number;\n}\n\nconst MAX_OPEN_FRAME_SETS = 32;\n\nexport function createWorkflowNodeProjection(input: {\n runId: string;\n nodeId: string;\n}): WorkflowNodeProjection {\n return {\n runId: input.runId,\n nodeId: input.nodeId,\n answer: \"\",\n reasoning: \"\",\n blockShadowed: false,\n renderBlocks: {},\n renderBlockOrder: [],\n openFrames: {},\n chunksReceived: 0,\n charsStreamed: 0,\n lastStreamingTs: null,\n lastPhase: null,\n lastTool: null,\n lastWarning: null,\n lastTransportSeq: 0,\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asString(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\n\nfunction asNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction toRenderBlock(value: unknown): AgentProjectionRenderBlock | null {\n const data = asRecord(value);\n if (!data) return null;\n const blockId = asString(data.blockId);\n const blockIndex = asNumber(data.blockIndex);\n const type = asString(data.type);\n if (blockId === null || blockIndex === null || type === null) return null;\n return {\n blockId,\n blockIndex,\n type,\n status:\n data.status === \"complete\" || data.status === \"error\"\n ? data.status\n : \"streaming\",\n content: asString(data.content),\n data: asRecord(data.data),\n metadata: asRecord(data.metadata),\n };\n}\n\nfunction projectRenderFrame(\n current: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n next: WorkflowNodeProjection,\n): WorkflowNodeProjection {\n const frameId = event.frame_id;\n const frameCount = event.frame_count ?? 1;\n const frameIndex = event.frame_index ?? 0;\n if (!frameId || frameCount < 1 || frameIndex < 0 || frameIndex >= frameCount) {\n return next;\n }\n const prior = current.openFrames[frameId];\n const set: WorkflowRenderFrameSet = {\n frameCount,\n slices: { ...(prior?.frameCount === frameCount ? prior.slices : {}), [frameIndex]: event.delta },\n };\n const openFrames = { ...current.openFrames, [frameId]: set };\n const keys = Object.keys(openFrames);\n const oldestKey = keys[0];\n if (keys.length > MAX_OPEN_FRAME_SETS && oldestKey !== undefined) {\n delete openFrames[oldestKey];\n }\n if (Object.keys(set.slices).length !== frameCount) return { ...next, openFrames };\n\n delete openFrames[frameId];\n let block: AgentProjectionRenderBlock | null = null;\n try {\n block = toRenderBlock(JSON.parse(\n Array.from({ length: frameCount }, (_, index) => set.slices[index] ?? \"\").join(\"\"),\n ));\n } catch {\n return { ...next, openFrames };\n }\n if (!block) return { ...next, openFrames };\n return {\n ...next,\n openFrames,\n renderBlocks: { ...current.renderBlocks, [block.blockId]: block },\n renderBlockOrder: Object.hasOwn(current.renderBlocks, block.blockId)\n ? current.renderBlockOrder\n : [...current.renderBlockOrder, block.blockId],\n };\n}\n\nexport function projectWorkflowNodeEvent(\n current: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n): WorkflowNodeProjection {\n if (event.run_id !== current.runId || (event.node_id ?? \"\") !== current.nodeId) return current;\n if (event.stream_seq <= current.lastTransportSeq) return current;\n const next: WorkflowNodeProjection = {\n ...current,\n chunksReceived: event.chunks_received,\n charsStreamed: event.chars_streamed,\n lastStreamingTs: event.ts,\n lastTransportSeq: event.stream_seq,\n blockShadowed: current.blockShadowed || Boolean(event.block_shadowed),\n };\n switch (event.kind) {\n case \"chunk\":\n return { ...next, answer: current.answer + event.delta };\n case \"reasoning\":\n return { ...next, reasoning: current.reasoning + event.delta };\n case \"phase\":\n return { ...next, lastPhase: event.delta };\n case \"tool\":\n return { ...next, lastTool: event.delta };\n case \"warning\":\n return { ...next, lastWarning: event.delta };\n case \"render_block\":\n return projectRenderFrame(current, event, next);\n default:\n return next;\n }\n}\n"]}
1
+ {"version":3,"sources":["../../projection/workflow.ts"],"names":[],"mappings":"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","file":"workflow.js","sourcesContent":["import type { AgentProjectionRenderBlock } from \"./request\";\n\nexport interface WorkflowNodeStreamEvent {\n run_id: string;\n node_id?: string | null;\n kind:\n | \"chunk\"\n | \"reasoning\"\n | \"phase\"\n | \"tool\"\n | \"warning\"\n | \"record_update\"\n | \"resource_changed\"\n | \"render_block\";\n delta: string;\n stream_seq: number;\n ts: string;\n chunks_received: number;\n chars_streamed: number;\n frame_id?: string | null;\n frame_index?: number;\n frame_count?: number;\n block_shadowed?: boolean;\n}\n\nexport interface WorkflowProjectionLimits {\n maxOpenFrameSets: number;\n maxFramesPerBlock: number;\n maxBytesPerBlock: number;\n}\n\nexport const DEFAULT_WORKFLOW_PROJECTION_LIMITS: Readonly<WorkflowProjectionLimits> = {\n maxOpenFrameSets: 32,\n maxFramesPerBlock: 256,\n maxBytesPerBlock: 1_048_576,\n};\n\nexport interface WorkflowRenderFrameSet {\n frameCount: number;\n slices: Record<number, string>;\n byteLength: number;\n}\n\nexport type WorkflowRenderBlockIssueCode =\n | \"invalid_frame_metadata\"\n | \"frame_count_mismatch\"\n | \"open_frame_limit_exceeded\"\n | \"frame_limit_exceeded\"\n | \"byte_limit_exceeded\"\n | \"malformed_json\"\n | \"invalid_render_block\";\n\nexport interface WorkflowRenderBlockIssue {\n code: WorkflowRenderBlockIssueCode;\n frameId: string | null;\n streamSeq: number;\n message: string;\n}\n\nexport interface WorkflowNodeProjection {\n runId: string;\n nodeId: string;\n answer: string;\n reasoning: string;\n blockShadowed: boolean;\n renderBlocks: Record<string, AgentProjectionRenderBlock>;\n renderBlockOrder: string[];\n openFrames: Record<string, WorkflowRenderFrameSet>;\n limits: WorkflowProjectionLimits;\n lastRenderBlockIssue: WorkflowRenderBlockIssue | null;\n chunksReceived: number;\n charsStreamed: number;\n lastStreamingTs: string | null;\n lastPhase: string | null;\n lastTool: string | null;\n lastWarning: string | null;\n lastTransportSeq: number;\n}\n\nfunction positiveSafeInteger(name: string, value: number): number {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new RangeError(`${name} must be a positive safe integer`);\n }\n return value;\n}\n\nfunction resolveLimits(\n limits: Partial<WorkflowProjectionLimits> | undefined,\n): WorkflowProjectionLimits {\n return {\n maxOpenFrameSets: positiveSafeInteger(\n \"maxOpenFrameSets\",\n limits?.maxOpenFrameSets ??\n DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxOpenFrameSets,\n ),\n maxFramesPerBlock: positiveSafeInteger(\n \"maxFramesPerBlock\",\n limits?.maxFramesPerBlock ??\n DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxFramesPerBlock,\n ),\n maxBytesPerBlock: positiveSafeInteger(\n \"maxBytesPerBlock\",\n limits?.maxBytesPerBlock ??\n DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxBytesPerBlock,\n ),\n };\n}\n\nexport function createWorkflowNodeProjection(input: {\n runId: string;\n nodeId: string;\n limits?: Partial<WorkflowProjectionLimits>;\n}): WorkflowNodeProjection {\n return {\n runId: input.runId,\n nodeId: input.nodeId,\n answer: \"\",\n reasoning: \"\",\n blockShadowed: false,\n renderBlocks: {},\n renderBlockOrder: [],\n openFrames: {},\n limits: resolveLimits(input.limits),\n lastRenderBlockIssue: null,\n chunksReceived: 0,\n charsStreamed: 0,\n lastStreamingTs: null,\n lastPhase: null,\n lastTool: null,\n lastWarning: null,\n lastTransportSeq: 0,\n };\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asString(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\n\nfunction asNumber(value: unknown): number | null {\n return typeof value === \"number\" && Number.isFinite(value) ? value : null;\n}\n\nfunction utf8ByteLength(value: string): number {\n return new TextEncoder().encode(value).byteLength;\n}\n\nfunction toRenderBlock(value: unknown): AgentProjectionRenderBlock | null {\n const data = asRecord(value);\n if (!data) return null;\n const blockId = asString(data.blockId);\n const blockIndex = asNumber(data.blockIndex);\n const type = asString(data.type);\n if (blockId === null || blockIndex === null || type === null) return null;\n return {\n blockId,\n blockIndex,\n type,\n status:\n data.status === \"complete\" || data.status === \"error\"\n ? data.status\n : \"streaming\",\n content: asString(data.content),\n data: asRecord(data.data),\n metadata: asRecord(data.metadata),\n };\n}\n\nfunction withIssue(\n next: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n code: WorkflowRenderBlockIssueCode,\n frameId: string | null,\n message: string,\n openFrames: Record<string, WorkflowRenderFrameSet> = next.openFrames,\n): WorkflowNodeProjection {\n return {\n ...next,\n openFrames,\n lastRenderBlockIssue: {\n code,\n frameId,\n streamSeq: event.stream_seq,\n message,\n },\n };\n}\n\nfunction withoutFrame(\n frames: Record<string, WorkflowRenderFrameSet>,\n frameId: string,\n): Record<string, WorkflowRenderFrameSet> {\n if (!Object.hasOwn(frames, frameId)) return frames;\n const next = { ...frames };\n delete next[frameId];\n return next;\n}\n\nfunction projectRenderFrame(\n current: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n next: WorkflowNodeProjection,\n): WorkflowNodeProjection {\n const frameId = event.frame_id;\n const frameCount = event.frame_count ?? 1;\n const frameIndex = event.frame_index ?? 0;\n if (\n !frameId ||\n !Number.isSafeInteger(frameCount) ||\n !Number.isSafeInteger(frameIndex) ||\n frameCount < 1 ||\n frameIndex < 0 ||\n frameIndex >= frameCount\n ) {\n return withIssue(\n next,\n event,\n \"invalid_frame_metadata\",\n frameId ?? null,\n \"Render block frame metadata is invalid\",\n );\n }\n if (frameCount > current.limits.maxFramesPerBlock) {\n return withIssue(\n next,\n event,\n \"frame_limit_exceeded\",\n frameId,\n `Render block declares ${frameCount} frames; limit is ${current.limits.maxFramesPerBlock}`,\n withoutFrame(current.openFrames, frameId),\n );\n }\n\n const prior = current.openFrames[frameId];\n if (prior && prior.frameCount !== frameCount) {\n return withIssue(\n next,\n event,\n \"frame_count_mismatch\",\n frameId,\n `Render block frame count changed from ${prior.frameCount} to ${frameCount}`,\n withoutFrame(current.openFrames, frameId),\n );\n }\n\n const priorSlice = prior?.slices[frameIndex];\n const byteLength =\n (prior?.byteLength ?? 0) -\n (priorSlice === undefined ? 0 : utf8ByteLength(priorSlice)) +\n utf8ByteLength(event.delta);\n if (byteLength > current.limits.maxBytesPerBlock) {\n return withIssue(\n next,\n event,\n \"byte_limit_exceeded\",\n frameId,\n `Render block exceeds ${current.limits.maxBytesPerBlock} UTF-8 bytes`,\n withoutFrame(current.openFrames, frameId),\n );\n }\n\n const set: WorkflowRenderFrameSet = {\n frameCount,\n slices: { ...(prior?.slices ?? {}), [frameIndex]: event.delta },\n byteLength,\n };\n const openFrames = { ...current.openFrames };\n let limitIssue: WorkflowRenderBlockIssue | null = null;\n if (!prior && Object.keys(openFrames).length >= current.limits.maxOpenFrameSets) {\n const oldestFrameId = Object.keys(openFrames)[0];\n if (oldestFrameId !== undefined) {\n delete openFrames[oldestFrameId];\n limitIssue = {\n code: \"open_frame_limit_exceeded\",\n frameId: oldestFrameId,\n streamSeq: event.stream_seq,\n message: `Evicted incomplete render block after reaching ${current.limits.maxOpenFrameSets} open frame sets`,\n };\n }\n }\n openFrames[frameId] = set;\n if (Object.keys(set.slices).length !== frameCount) {\n return {\n ...next,\n openFrames,\n lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue,\n };\n }\n\n delete openFrames[frameId];\n const serialized = Array.from(\n { length: frameCount },\n (_, index) => set.slices[index] ?? \"\",\n ).join(\"\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(serialized) as unknown;\n } catch (error) {\n const detail = error instanceof Error ? `: ${error.message}` : \"\";\n return withIssue(\n next,\n event,\n \"malformed_json\",\n frameId,\n `Completed render block is not valid JSON${detail}`,\n openFrames,\n );\n }\n const block = toRenderBlock(parsed);\n if (!block) {\n return withIssue(\n next,\n event,\n \"invalid_render_block\",\n frameId,\n \"Completed render block is missing blockId, blockIndex, or type\",\n openFrames,\n );\n }\n return {\n ...next,\n openFrames,\n lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue,\n renderBlocks: { ...current.renderBlocks, [block.blockId]: block },\n renderBlockOrder: Object.hasOwn(current.renderBlocks, block.blockId)\n ? current.renderBlockOrder\n : [...current.renderBlockOrder, block.blockId],\n };\n}\n\nexport function projectWorkflowNodeEvent(\n current: WorkflowNodeProjection,\n event: WorkflowNodeStreamEvent,\n): WorkflowNodeProjection {\n if (\n event.run_id !== current.runId ||\n (event.node_id ?? \"\") !== current.nodeId\n ) {\n return current;\n }\n if (event.stream_seq <= current.lastTransportSeq) return current;\n const next: WorkflowNodeProjection = {\n ...current,\n chunksReceived: event.chunks_received,\n charsStreamed: event.chars_streamed,\n lastStreamingTs: event.ts,\n lastTransportSeq: event.stream_seq,\n blockShadowed: current.blockShadowed || Boolean(event.block_shadowed),\n };\n switch (event.kind) {\n case \"chunk\":\n return { ...next, answer: current.answer + event.delta };\n case \"reasoning\":\n return { ...next, reasoning: current.reasoning + event.delta };\n case \"phase\":\n return { ...next, lastPhase: event.delta };\n case \"tool\":\n return { ...next, lastTool: event.delta };\n case \"warning\":\n return { ...next, lastWarning: event.delta };\n case \"render_block\":\n return projectRenderFrame(current, event, next);\n default:\n return next;\n }\n}\n"]}