@ai-matrx/agents 0.2.0 → 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 +27 -0
- package/README.md +46 -5
- package/dist/index.cjs +385 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +381 -32
- 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 +245 -0
- package/dist/projection/workflow.cjs.map +1 -0
- package/dist/projection/workflow.d.cts +61 -0
- package/dist/projection/workflow.d.ts +61 -0
- package/dist/projection/workflow.js +241 -0
- package/dist/projection/workflow.js.map +1 -0
- 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 +11 -1
|
@@ -0,0 +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 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"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AgentProjectionRenderBlock } from './request.cjs';
|
|
2
|
+
|
|
3
|
+
interface WorkflowNodeStreamEvent {
|
|
4
|
+
run_id: string;
|
|
5
|
+
node_id?: string | null;
|
|
6
|
+
kind: "chunk" | "reasoning" | "phase" | "tool" | "warning" | "record_update" | "resource_changed" | "render_block";
|
|
7
|
+
delta: string;
|
|
8
|
+
stream_seq: number;
|
|
9
|
+
ts: string;
|
|
10
|
+
chunks_received: number;
|
|
11
|
+
chars_streamed: number;
|
|
12
|
+
frame_id?: string | null;
|
|
13
|
+
frame_index?: number;
|
|
14
|
+
frame_count?: number;
|
|
15
|
+
block_shadowed?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface WorkflowProjectionLimits {
|
|
18
|
+
maxOpenFrameSets: number;
|
|
19
|
+
maxFramesPerBlock: number;
|
|
20
|
+
maxBytesPerBlock: number;
|
|
21
|
+
}
|
|
22
|
+
declare const DEFAULT_WORKFLOW_PROJECTION_LIMITS: Readonly<WorkflowProjectionLimits>;
|
|
23
|
+
interface WorkflowRenderFrameSet {
|
|
24
|
+
frameCount: number;
|
|
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;
|
|
34
|
+
}
|
|
35
|
+
interface WorkflowNodeProjection {
|
|
36
|
+
runId: string;
|
|
37
|
+
nodeId: string;
|
|
38
|
+
answer: string;
|
|
39
|
+
reasoning: string;
|
|
40
|
+
blockShadowed: boolean;
|
|
41
|
+
renderBlocks: Record<string, AgentProjectionRenderBlock>;
|
|
42
|
+
renderBlockOrder: string[];
|
|
43
|
+
openFrames: Record<string, WorkflowRenderFrameSet>;
|
|
44
|
+
limits: WorkflowProjectionLimits;
|
|
45
|
+
lastRenderBlockIssue: WorkflowRenderBlockIssue | null;
|
|
46
|
+
chunksReceived: number;
|
|
47
|
+
charsStreamed: number;
|
|
48
|
+
lastStreamingTs: string | null;
|
|
49
|
+
lastPhase: string | null;
|
|
50
|
+
lastTool: string | null;
|
|
51
|
+
lastWarning: string | null;
|
|
52
|
+
lastTransportSeq: number;
|
|
53
|
+
}
|
|
54
|
+
declare function createWorkflowNodeProjection(input: {
|
|
55
|
+
runId: string;
|
|
56
|
+
nodeId: string;
|
|
57
|
+
limits?: Partial<WorkflowProjectionLimits>;
|
|
58
|
+
}): WorkflowNodeProjection;
|
|
59
|
+
declare function projectWorkflowNodeEvent(current: WorkflowNodeProjection, event: WorkflowNodeStreamEvent): WorkflowNodeProjection;
|
|
60
|
+
|
|
61
|
+
export { DEFAULT_WORKFLOW_PROJECTION_LIMITS, type WorkflowNodeProjection, type WorkflowNodeStreamEvent, type WorkflowProjectionLimits, type WorkflowRenderBlockIssue, type WorkflowRenderBlockIssueCode, type WorkflowRenderFrameSet, createWorkflowNodeProjection, projectWorkflowNodeEvent };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AgentProjectionRenderBlock } from './request.js';
|
|
2
|
+
|
|
3
|
+
interface WorkflowNodeStreamEvent {
|
|
4
|
+
run_id: string;
|
|
5
|
+
node_id?: string | null;
|
|
6
|
+
kind: "chunk" | "reasoning" | "phase" | "tool" | "warning" | "record_update" | "resource_changed" | "render_block";
|
|
7
|
+
delta: string;
|
|
8
|
+
stream_seq: number;
|
|
9
|
+
ts: string;
|
|
10
|
+
chunks_received: number;
|
|
11
|
+
chars_streamed: number;
|
|
12
|
+
frame_id?: string | null;
|
|
13
|
+
frame_index?: number;
|
|
14
|
+
frame_count?: number;
|
|
15
|
+
block_shadowed?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface WorkflowProjectionLimits {
|
|
18
|
+
maxOpenFrameSets: number;
|
|
19
|
+
maxFramesPerBlock: number;
|
|
20
|
+
maxBytesPerBlock: number;
|
|
21
|
+
}
|
|
22
|
+
declare const DEFAULT_WORKFLOW_PROJECTION_LIMITS: Readonly<WorkflowProjectionLimits>;
|
|
23
|
+
interface WorkflowRenderFrameSet {
|
|
24
|
+
frameCount: number;
|
|
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;
|
|
34
|
+
}
|
|
35
|
+
interface WorkflowNodeProjection {
|
|
36
|
+
runId: string;
|
|
37
|
+
nodeId: string;
|
|
38
|
+
answer: string;
|
|
39
|
+
reasoning: string;
|
|
40
|
+
blockShadowed: boolean;
|
|
41
|
+
renderBlocks: Record<string, AgentProjectionRenderBlock>;
|
|
42
|
+
renderBlockOrder: string[];
|
|
43
|
+
openFrames: Record<string, WorkflowRenderFrameSet>;
|
|
44
|
+
limits: WorkflowProjectionLimits;
|
|
45
|
+
lastRenderBlockIssue: WorkflowRenderBlockIssue | null;
|
|
46
|
+
chunksReceived: number;
|
|
47
|
+
charsStreamed: number;
|
|
48
|
+
lastStreamingTs: string | null;
|
|
49
|
+
lastPhase: string | null;
|
|
50
|
+
lastTool: string | null;
|
|
51
|
+
lastWarning: string | null;
|
|
52
|
+
lastTransportSeq: number;
|
|
53
|
+
}
|
|
54
|
+
declare function createWorkflowNodeProjection(input: {
|
|
55
|
+
runId: string;
|
|
56
|
+
nodeId: string;
|
|
57
|
+
limits?: Partial<WorkflowProjectionLimits>;
|
|
58
|
+
}): WorkflowNodeProjection;
|
|
59
|
+
declare function projectWorkflowNodeEvent(current: WorkflowNodeProjection, event: WorkflowNodeStreamEvent): WorkflowNodeProjection;
|
|
60
|
+
|
|
61
|
+
export { DEFAULT_WORKFLOW_PROJECTION_LIMITS, type WorkflowNodeProjection, type WorkflowNodeStreamEvent, type WorkflowProjectionLimits, type WorkflowRenderBlockIssue, type WorkflowRenderBlockIssueCode, type WorkflowRenderFrameSet, createWorkflowNodeProjection, projectWorkflowNodeEvent };
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// projection/workflow.ts
|
|
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
|
+
}
|
|
29
|
+
function createWorkflowNodeProjection(input) {
|
|
30
|
+
return {
|
|
31
|
+
runId: input.runId,
|
|
32
|
+
nodeId: input.nodeId,
|
|
33
|
+
answer: "",
|
|
34
|
+
reasoning: "",
|
|
35
|
+
blockShadowed: false,
|
|
36
|
+
renderBlocks: {},
|
|
37
|
+
renderBlockOrder: [],
|
|
38
|
+
openFrames: {},
|
|
39
|
+
limits: resolveLimits(input.limits),
|
|
40
|
+
lastRenderBlockIssue: null,
|
|
41
|
+
chunksReceived: 0,
|
|
42
|
+
charsStreamed: 0,
|
|
43
|
+
lastStreamingTs: null,
|
|
44
|
+
lastPhase: null,
|
|
45
|
+
lastTool: null,
|
|
46
|
+
lastWarning: null,
|
|
47
|
+
lastTransportSeq: 0
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function asRecord(value) {
|
|
51
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
52
|
+
}
|
|
53
|
+
function asString(value) {
|
|
54
|
+
return typeof value === "string" ? value : null;
|
|
55
|
+
}
|
|
56
|
+
function asNumber(value) {
|
|
57
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
58
|
+
}
|
|
59
|
+
function utf8ByteLength(value) {
|
|
60
|
+
return new TextEncoder().encode(value).byteLength;
|
|
61
|
+
}
|
|
62
|
+
function toRenderBlock(value) {
|
|
63
|
+
const data = asRecord(value);
|
|
64
|
+
if (!data) return null;
|
|
65
|
+
const blockId = asString(data.blockId);
|
|
66
|
+
const blockIndex = asNumber(data.blockIndex);
|
|
67
|
+
const type = asString(data.type);
|
|
68
|
+
if (blockId === null || blockIndex === null || type === null) return null;
|
|
69
|
+
return {
|
|
70
|
+
blockId,
|
|
71
|
+
blockIndex,
|
|
72
|
+
type,
|
|
73
|
+
status: data.status === "complete" || data.status === "error" ? data.status : "streaming",
|
|
74
|
+
content: asString(data.content),
|
|
75
|
+
data: asRecord(data.data),
|
|
76
|
+
metadata: asRecord(data.metadata)
|
|
77
|
+
};
|
|
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
|
+
}
|
|
97
|
+
function projectRenderFrame(current, event, next) {
|
|
98
|
+
const frameId = event.frame_id;
|
|
99
|
+
const frameCount = event.frame_count ?? 1;
|
|
100
|
+
const frameIndex = event.frame_index ?? 0;
|
|
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
|
+
);
|
|
119
|
+
}
|
|
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
|
+
}
|
|
143
|
+
const set = {
|
|
144
|
+
frameCount,
|
|
145
|
+
slices: { ...prior?.slices ?? {}, [frameIndex]: event.delta },
|
|
146
|
+
byteLength
|
|
147
|
+
};
|
|
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
|
+
};
|
|
169
|
+
}
|
|
170
|
+
delete openFrames[frameId];
|
|
171
|
+
const serialized = Array.from(
|
|
172
|
+
{ length: frameCount },
|
|
173
|
+
(_, index) => set.slices[index] ?? ""
|
|
174
|
+
).join("");
|
|
175
|
+
let parsed;
|
|
176
|
+
try {
|
|
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
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
...next,
|
|
202
|
+
openFrames,
|
|
203
|
+
lastRenderBlockIssue: limitIssue ?? current.lastRenderBlockIssue,
|
|
204
|
+
renderBlocks: { ...current.renderBlocks, [block.blockId]: block },
|
|
205
|
+
renderBlockOrder: Object.hasOwn(current.renderBlocks, block.blockId) ? current.renderBlockOrder : [...current.renderBlockOrder, block.blockId]
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function projectWorkflowNodeEvent(current, event) {
|
|
209
|
+
if (event.run_id !== current.runId || (event.node_id ?? "") !== current.nodeId) {
|
|
210
|
+
return current;
|
|
211
|
+
}
|
|
212
|
+
if (event.stream_seq <= current.lastTransportSeq) return current;
|
|
213
|
+
const next = {
|
|
214
|
+
...current,
|
|
215
|
+
chunksReceived: event.chunks_received,
|
|
216
|
+
charsStreamed: event.chars_streamed,
|
|
217
|
+
lastStreamingTs: event.ts,
|
|
218
|
+
lastTransportSeq: event.stream_seq,
|
|
219
|
+
blockShadowed: current.blockShadowed || Boolean(event.block_shadowed)
|
|
220
|
+
};
|
|
221
|
+
switch (event.kind) {
|
|
222
|
+
case "chunk":
|
|
223
|
+
return { ...next, answer: current.answer + event.delta };
|
|
224
|
+
case "reasoning":
|
|
225
|
+
return { ...next, reasoning: current.reasoning + event.delta };
|
|
226
|
+
case "phase":
|
|
227
|
+
return { ...next, lastPhase: event.delta };
|
|
228
|
+
case "tool":
|
|
229
|
+
return { ...next, lastTool: event.delta };
|
|
230
|
+
case "warning":
|
|
231
|
+
return { ...next, lastWarning: event.delta };
|
|
232
|
+
case "render_block":
|
|
233
|
+
return projectRenderFrame(current, event, next);
|
|
234
|
+
default:
|
|
235
|
+
return next;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export { DEFAULT_WORKFLOW_PROJECTION_LIMITS, createWorkflowNodeProjection, projectWorkflowNodeEvent };
|
|
240
|
+
//# sourceMappingURL=workflow.js.map
|
|
241
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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.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"]}
|