@ai-matrx/agents 0.2.1 → 0.3.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.
- package/CHANGELOG.md +19 -3
- package/README.md +30 -6
- package/dist/index.cjs +289 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +287 -50
- package/dist/index.js.map +1 -1
- package/dist/projection/request.cjs +1 -1
- package/dist/projection/request.cjs.map +1 -1
- package/dist/projection/request.js +1 -1
- package/dist/projection/request.js.map +1 -1
- package/dist/projection/workflow.cjs +145 -18
- package/dist/projection/workflow.cjs.map +1 -1
- package/dist/projection/workflow.d.cts +18 -1
- package/dist/projection/workflow.d.ts +18 -1
- package/dist/projection/workflow.js +145 -19
- package/dist/projection/workflow.js.map +1 -1
- package/dist/stream/ndjson.cjs +143 -30
- package/dist/stream/ndjson.cjs.map +1 -1
- package/dist/stream/ndjson.d.cts +42 -7
- package/dist/stream/ndjson.d.ts +42 -7
- package/dist/stream/ndjson.js +142 -31
- package/dist/stream/ndjson.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// projection/workflow.ts
|
|
4
|
-
var
|
|
4
|
+
var DEFAULT_WORKFLOW_PROJECTION_LIMITS = {
|
|
5
|
+
maxOpenFrameSets: 32,
|
|
6
|
+
maxFramesPerBlock: 256,
|
|
7
|
+
maxBytesPerBlock: 1048576
|
|
8
|
+
};
|
|
9
|
+
function positiveSafeInteger(name, value) {
|
|
10
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
11
|
+
throw new RangeError(`${name} must be a positive safe integer`);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
function resolveLimits(limits) {
|
|
16
|
+
return {
|
|
17
|
+
maxOpenFrameSets: positiveSafeInteger(
|
|
18
|
+
"maxOpenFrameSets",
|
|
19
|
+
limits?.maxOpenFrameSets ?? DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxOpenFrameSets
|
|
20
|
+
),
|
|
21
|
+
maxFramesPerBlock: positiveSafeInteger(
|
|
22
|
+
"maxFramesPerBlock",
|
|
23
|
+
limits?.maxFramesPerBlock ?? DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxFramesPerBlock
|
|
24
|
+
),
|
|
25
|
+
maxBytesPerBlock: positiveSafeInteger(
|
|
26
|
+
"maxBytesPerBlock",
|
|
27
|
+
limits?.maxBytesPerBlock ?? DEFAULT_WORKFLOW_PROJECTION_LIMITS.maxBytesPerBlock
|
|
28
|
+
)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
5
31
|
function createWorkflowNodeProjection(input) {
|
|
6
32
|
return {
|
|
7
33
|
runId: input.runId,
|
|
@@ -12,6 +38,8 @@ function createWorkflowNodeProjection(input) {
|
|
|
12
38
|
renderBlocks: {},
|
|
13
39
|
renderBlockOrder: [],
|
|
14
40
|
openFrames: {},
|
|
41
|
+
limits: resolveLimits(input.limits),
|
|
42
|
+
lastRenderBlockIssue: null,
|
|
15
43
|
chunksReceived: 0,
|
|
16
44
|
charsStreamed: 0,
|
|
17
45
|
lastStreamingTs: null,
|
|
@@ -30,6 +58,9 @@ function asString(value) {
|
|
|
30
58
|
function asNumber(value) {
|
|
31
59
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
32
60
|
}
|
|
61
|
+
function utf8ByteLength(value) {
|
|
62
|
+
return new TextEncoder().encode(value).byteLength;
|
|
63
|
+
}
|
|
33
64
|
function toRenderBlock(value) {
|
|
34
65
|
const data = asRecord(value);
|
|
35
66
|
if (!data) return null;
|
|
@@ -47,44 +78,139 @@ function toRenderBlock(value) {
|
|
|
47
78
|
metadata: asRecord(data.metadata)
|
|
48
79
|
};
|
|
49
80
|
}
|
|
81
|
+
function withIssue(next, event, code, frameId, message, openFrames = next.openFrames) {
|
|
82
|
+
return {
|
|
83
|
+
...next,
|
|
84
|
+
openFrames,
|
|
85
|
+
lastRenderBlockIssue: {
|
|
86
|
+
code,
|
|
87
|
+
frameId,
|
|
88
|
+
streamSeq: event.stream_seq,
|
|
89
|
+
message
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function withoutFrame(frames, frameId) {
|
|
94
|
+
if (!Object.hasOwn(frames, frameId)) return frames;
|
|
95
|
+
const next = { ...frames };
|
|
96
|
+
delete next[frameId];
|
|
97
|
+
return next;
|
|
98
|
+
}
|
|
50
99
|
function projectRenderFrame(current, event, next) {
|
|
51
100
|
const frameId = event.frame_id;
|
|
52
101
|
const frameCount = event.frame_count ?? 1;
|
|
53
102
|
const frameIndex = event.frame_index ?? 0;
|
|
54
|
-
if (!frameId || frameCount < 1 || frameIndex < 0 || frameIndex >= frameCount) {
|
|
55
|
-
return
|
|
103
|
+
if (!frameId || !Number.isSafeInteger(frameCount) || !Number.isSafeInteger(frameIndex) || frameCount < 1 || frameIndex < 0 || frameIndex >= frameCount) {
|
|
104
|
+
return withIssue(
|
|
105
|
+
next,
|
|
106
|
+
event,
|
|
107
|
+
"invalid_frame_metadata",
|
|
108
|
+
frameId ?? null,
|
|
109
|
+
"Render block frame metadata is invalid"
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
if (frameCount > current.limits.maxFramesPerBlock) {
|
|
113
|
+
return withIssue(
|
|
114
|
+
next,
|
|
115
|
+
event,
|
|
116
|
+
"frame_limit_exceeded",
|
|
117
|
+
frameId,
|
|
118
|
+
`Render block declares ${frameCount} frames; limit is ${current.limits.maxFramesPerBlock}`,
|
|
119
|
+
withoutFrame(current.openFrames, frameId)
|
|
120
|
+
);
|
|
56
121
|
}
|
|
57
122
|
const prior = current.openFrames[frameId];
|
|
123
|
+
if (prior && prior.frameCount !== frameCount) {
|
|
124
|
+
return withIssue(
|
|
125
|
+
next,
|
|
126
|
+
event,
|
|
127
|
+
"frame_count_mismatch",
|
|
128
|
+
frameId,
|
|
129
|
+
`Render block frame count changed from ${prior.frameCount} to ${frameCount}`,
|
|
130
|
+
withoutFrame(current.openFrames, frameId)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
const priorSlice = prior?.slices[frameIndex];
|
|
134
|
+
const byteLength = (prior?.byteLength ?? 0) - (priorSlice === void 0 ? 0 : utf8ByteLength(priorSlice)) + utf8ByteLength(event.delta);
|
|
135
|
+
if (byteLength > current.limits.maxBytesPerBlock) {
|
|
136
|
+
return withIssue(
|
|
137
|
+
next,
|
|
138
|
+
event,
|
|
139
|
+
"byte_limit_exceeded",
|
|
140
|
+
frameId,
|
|
141
|
+
`Render block exceeds ${current.limits.maxBytesPerBlock} UTF-8 bytes`,
|
|
142
|
+
withoutFrame(current.openFrames, frameId)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
58
145
|
const set = {
|
|
59
146
|
frameCount,
|
|
60
|
-
slices: { ...prior?.
|
|
147
|
+
slices: { ...prior?.slices ?? {}, [frameIndex]: event.delta },
|
|
148
|
+
byteLength
|
|
61
149
|
};
|
|
62
|
-
const openFrames = { ...current.openFrames
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
150
|
+
const openFrames = { ...current.openFrames };
|
|
151
|
+
let limitIssue = null;
|
|
152
|
+
if (!prior && Object.keys(openFrames).length >= current.limits.maxOpenFrameSets) {
|
|
153
|
+
const oldestFrameId = Object.keys(openFrames)[0];
|
|
154
|
+
if (oldestFrameId !== void 0) {
|
|
155
|
+
delete openFrames[oldestFrameId];
|
|
156
|
+
limitIssue = {
|
|
157
|
+
code: "open_frame_limit_exceeded",
|
|
158
|
+
frameId: oldestFrameId,
|
|
159
|
+
streamSeq: event.stream_seq,
|
|
160
|
+
message: `Evicted incomplete render block after reaching ${current.limits.maxOpenFrameSets} open frame sets`
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
openFrames[frameId] = set;
|
|
165
|
+
if (Object.keys(set.slices).length !== frameCount) {
|
|
166
|
+
return {
|
|
167
|
+
...next,
|
|
168
|
+
openFrames,
|
|
169
|
+
lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue
|
|
170
|
+
};
|
|
67
171
|
}
|
|
68
|
-
if (Object.keys(set.slices).length !== frameCount) return { ...next, openFrames };
|
|
69
172
|
delete openFrames[frameId];
|
|
70
|
-
|
|
173
|
+
const serialized = Array.from(
|
|
174
|
+
{ length: frameCount },
|
|
175
|
+
(_, index) => set.slices[index] ?? ""
|
|
176
|
+
).join("");
|
|
177
|
+
let parsed;
|
|
71
178
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
179
|
+
parsed = JSON.parse(serialized);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
const detail = error instanceof Error ? `: ${error.message}` : "";
|
|
182
|
+
return withIssue(
|
|
183
|
+
next,
|
|
184
|
+
event,
|
|
185
|
+
"malformed_json",
|
|
186
|
+
frameId,
|
|
187
|
+
`Completed render block is not valid JSON${detail}`,
|
|
188
|
+
openFrames
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
const block = toRenderBlock(parsed);
|
|
192
|
+
if (!block) {
|
|
193
|
+
return withIssue(
|
|
194
|
+
next,
|
|
195
|
+
event,
|
|
196
|
+
"invalid_render_block",
|
|
197
|
+
frameId,
|
|
198
|
+
"Completed render block is missing blockId, blockIndex, or type",
|
|
199
|
+
openFrames
|
|
200
|
+
);
|
|
77
201
|
}
|
|
78
|
-
if (!block) return { ...next, openFrames };
|
|
79
202
|
return {
|
|
80
203
|
...next,
|
|
81
204
|
openFrames,
|
|
205
|
+
lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue,
|
|
82
206
|
renderBlocks: { ...current.renderBlocks, [block.blockId]: block },
|
|
83
207
|
renderBlockOrder: Object.hasOwn(current.renderBlocks, block.blockId) ? current.renderBlockOrder : [...current.renderBlockOrder, block.blockId]
|
|
84
208
|
};
|
|
85
209
|
}
|
|
86
210
|
function projectWorkflowNodeEvent(current, event) {
|
|
87
|
-
if (event.run_id !== current.runId || (event.node_id ?? "") !== current.nodeId)
|
|
211
|
+
if (event.run_id !== current.runId || (event.node_id ?? "") !== current.nodeId) {
|
|
212
|
+
return current;
|
|
213
|
+
}
|
|
88
214
|
if (event.stream_seq <= current.lastTransportSeq) return current;
|
|
89
215
|
const next = {
|
|
90
216
|
...current,
|
|
@@ -112,6 +238,7 @@ function projectWorkflowNodeEvent(current, event) {
|
|
|
112
238
|
}
|
|
113
239
|
}
|
|
114
240
|
|
|
241
|
+
exports.DEFAULT_WORKFLOW_PROJECTION_LIMITS = DEFAULT_WORKFLOW_PROJECTION_LIMITS;
|
|
115
242
|
exports.createWorkflowNodeProjection = createWorkflowNodeProjection;
|
|
116
243
|
exports.projectWorkflowNodeEvent = projectWorkflowNodeEvent;
|
|
117
244
|
//# sourceMappingURL=workflow.cjs.map
|
|
@@ -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":";;;AA+BO,IAAM,kCAAA,GAAyE;AAAA,EACpF,gBAAA,EAAkB,EAAA;AAAA,EAClB,iBAAA,EAAmB,GAAA;AAAA,EACnB,gBAAA,EAAkB;AACpB;AA4CA,SAAS,mBAAA,CAAoB,MAAc,KAAA,EAAuB;AAChE,EAAA,IAAI,CAAC,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA,IAAK,QAAQ,CAAA,EAAG;AAC7C,IAAA,MAAM,IAAI,UAAA,CAAW,CAAA,EAAG,IAAI,CAAA,gCAAA,CAAkC,CAAA;AAAA,EAChE;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,cACP,MAAA,EAC0B;AAC1B,EAAA,OAAO;AAAA,IACL,gBAAA,EAAkB,mBAAA;AAAA,MAChB,kBAAA;AAAA,MACA,MAAA,EAAQ,oBACN,kCAAA,CAAmC;AAAA,KACvC;AAAA,IACA,iBAAA,EAAmB,mBAAA;AAAA,MACjB,mBAAA;AAAA,MACA,MAAA,EAAQ,qBACN,kCAAA,CAAmC;AAAA,KACvC;AAAA,IACA,gBAAA,EAAkB,mBAAA;AAAA,MAChB,kBAAA;AAAA,MACA,MAAA,EAAQ,oBACN,kCAAA,CAAmC;AAAA;AACvC,GACF;AACF;AAEO,SAAS,6BAA6B,KAAA,EAIlB;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,MAAA,EAAQ,aAAA,CAAc,KAAA,CAAM,MAAM,CAAA;AAAA,IAClC,oBAAA,EAAsB,IAAA;AAAA,IACtB,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,eAAe,KAAA,EAAuB;AAC7C,EAAA,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,UAAA;AACzC;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,SAAA,CACP,MACA,KAAA,EACA,IAAA,EACA,SACA,OAAA,EACA,UAAA,GAAqD,KAAK,UAAA,EAClC;AACxB,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,UAAA;AAAA,IACA,oBAAA,EAAsB;AAAA,MACpB,IAAA;AAAA,MACA,OAAA;AAAA,MACA,WAAW,KAAA,CAAM,UAAA;AAAA,MACjB;AAAA;AACF,GACF;AACF;AAEA,SAAS,YAAA,CACP,QACA,OAAA,EACwC;AACxC,EAAA,IAAI,CAAC,MAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,OAAO,GAAG,OAAO,MAAA;AAC5C,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,MAAA,EAAO;AACzB,EAAA,OAAO,KAAK,OAAO,CAAA;AACnB,EAAA,OAAO,IAAA;AACT;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,IACE,CAAC,OAAA,IACD,CAAC,MAAA,CAAO,aAAA,CAAc,UAAU,CAAA,IAChC,CAAC,MAAA,CAAO,aAAA,CAAc,UAAU,CAAA,IAChC,UAAA,GAAa,KACb,UAAA,GAAa,CAAA,IACb,cAAc,UAAA,EACd;AACA,IAAA,OAAO,SAAA;AAAA,MACL,IAAA;AAAA,MACA,KAAA;AAAA,MACA,wBAAA;AAAA,MACA,OAAA,IAAW,IAAA;AAAA,MACX;AAAA,KACF;AAAA,EACF;AACA,EAAA,IAAI,UAAA,GAAa,OAAA,CAAQ,MAAA,CAAO,iBAAA,EAAmB;AACjD,IAAA,OAAO,SAAA;AAAA,MACL,IAAA;AAAA,MACA,KAAA;AAAA,MACA,sBAAA;AAAA,MACA,OAAA;AAAA,MACA,CAAA,sBAAA,EAAyB,UAAU,CAAA,kBAAA,EAAqB,OAAA,CAAQ,OAAO,iBAAiB,CAAA,CAAA;AAAA,MACxF,YAAA,CAAa,OAAA,CAAQ,UAAA,EAAY,OAAO;AAAA,KAC1C;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,UAAA,CAAW,OAAO,CAAA;AACxC,EAAA,IAAI,KAAA,IAAS,KAAA,CAAM,UAAA,KAAe,UAAA,EAAY;AAC5C,IAAA,OAAO,SAAA;AAAA,MACL,IAAA;AAAA,MACA,KAAA;AAAA,MACA,sBAAA;AAAA,MACA,OAAA;AAAA,MACA,CAAA,sCAAA,EAAyC,KAAA,CAAM,UAAU,CAAA,IAAA,EAAO,UAAU,CAAA,CAAA;AAAA,MAC1E,YAAA,CAAa,OAAA,CAAQ,UAAA,EAAY,OAAO;AAAA,KAC1C;AAAA,EACF;AAEA,EAAA,MAAM,UAAA,GAAa,KAAA,EAAO,MAAA,CAAO,UAAU,CAAA;AAC3C,EAAA,MAAM,UAAA,GAAA,CACH,KAAA,EAAO,UAAA,IAAc,CAAA,KACrB,UAAA,KAAe,MAAA,GAAY,CAAA,GAAI,cAAA,CAAe,UAAU,CAAA,CAAA,GACzD,cAAA,CAAe,KAAA,CAAM,KAAK,CAAA;AAC5B,EAAA,IAAI,UAAA,GAAa,OAAA,CAAQ,MAAA,CAAO,gBAAA,EAAkB;AAChD,IAAA,OAAO,SAAA;AAAA,MACL,IAAA;AAAA,MACA,KAAA;AAAA,MACA,qBAAA;AAAA,MACA,OAAA;AAAA,MACA,CAAA,qBAAA,EAAwB,OAAA,CAAQ,MAAA,CAAO,gBAAgB,CAAA,YAAA,CAAA;AAAA,MACvD,YAAA,CAAa,OAAA,CAAQ,UAAA,EAAY,OAAO;AAAA,KAC1C;AAAA,EACF;AAEA,EAAA,MAAM,GAAA,GAA8B;AAAA,IAClC,UAAA;AAAA,IACA,MAAA,EAAQ,EAAE,GAAI,KAAA,EAAO,MAAA,IAAU,EAAC,EAAI,CAAC,UAAU,GAAG,KAAA,CAAM,KAAA,EAAM;AAAA,IAC9D;AAAA,GACF;AACA,EAAA,MAAM,UAAA,GAAa,EAAE,GAAG,OAAA,CAAQ,UAAA,EAAW;AAC3C,EAAA,IAAI,UAAA,GAA8C,IAAA;AAClD,EAAA,IAAI,CAAC,SAAS,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,CAAE,MAAA,IAAU,OAAA,CAAQ,MAAA,CAAO,gBAAA,EAAkB;AAC/E,IAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,IAAA,CAAK,UAAU,EAAE,CAAC,CAAA;AAC/C,IAAA,IAAI,kBAAkB,MAAA,EAAW;AAC/B,MAAA,OAAO,WAAW,aAAa,CAAA;AAC/B,MAAA,UAAA,GAAa;AAAA,QACX,IAAA,EAAM,2BAAA;AAAA,QACN,OAAA,EAAS,aAAA;AAAA,QACT,WAAW,KAAA,CAAM,UAAA;AAAA,QACjB,OAAA,EAAS,CAAA,+CAAA,EAAkD,OAAA,CAAQ,MAAA,CAAO,gBAAgB,CAAA,gBAAA;AAAA,OAC5F;AAAA,IACF;AAAA,EACF;AACA,EAAA,UAAA,CAAW,OAAO,CAAA,GAAI,GAAA;AACtB,EAAA,IAAI,OAAO,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,CAAE,WAAW,UAAA,EAAY;AACjD,IAAA,OAAO;AAAA,MACL,GAAG,IAAA;AAAA,MACH,UAAA;AAAA,MACA,oBAAA,EAAsB,cAAc,OAAA,CAAQ;AAAA,KAC9C;AAAA,EACF;AAEA,EAAA,OAAO,WAAW,OAAO,CAAA;AACzB,EAAA,MAAM,aAAa,KAAA,CAAM,IAAA;AAAA,IACvB,EAAE,QAAQ,UAAA,EAAW;AAAA,IACrB,CAAC,CAAA,EAAG,KAAA,KAAU,GAAA,CAAI,MAAA,CAAO,KAAK,CAAA,IAAK;AAAA,GACrC,CAAE,KAAK,EAAE,CAAA;AACT,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAA,GAAS,IAAA,CAAK,MAAM,UAAU,CAAA;AAAA,EAChC,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,SAAS,KAAA,YAAiB,KAAA,GAAQ,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAA,GAAK,EAAA;AAC/D,IAAA,OAAO,SAAA;AAAA,MACL,IAAA;AAAA,MACA,KAAA;AAAA,MACA,gBAAA;AAAA,MACA,OAAA;AAAA,MACA,2CAA2C,MAAM,CAAA,CAAA;AAAA,MACjD;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,KAAA,GAAQ,cAAc,MAAM,CAAA;AAClC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,SAAA;AAAA,MACL,IAAA;AAAA,MACA,KAAA;AAAA,MACA,sBAAA;AAAA,MACA,OAAA;AAAA,MACA,gEAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,UAAA;AAAA,IACA,oBAAA,EAAsB,cAAc,OAAA,CAAQ,oBAAA;AAAA,IAC5C,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,IACE,KAAA,CAAM,WAAW,OAAA,CAAQ,KAAA,IAAA,CACxB,MAAM,OAAA,IAAW,EAAA,MAAQ,QAAQ,MAAA,EAClC;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AACA,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.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
|
|
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
|
|
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?.
|
|
145
|
+
slices: { ...prior?.slices ?? {}, [frameIndex]: event.delta },
|
|
146
|
+
byteLength
|
|
59
147
|
};
|
|
60
|
-
const openFrames = { ...current.openFrames
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
171
|
+
const serialized = Array.from(
|
|
172
|
+
{ length: frameCount },
|
|
173
|
+
(_, index) => set.slices[index] ?? ""
|
|
174
|
+
).join("");
|
|
175
|
+
let parsed;
|
|
69
176
|
try {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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)
|
|
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":"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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 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"]}
|