veryfront 0.1.700 → 0.1.703
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/esm/cli/templates/manifest.d.ts +0 -2
- package/esm/cli/templates/manifest.js +1 -3
- package/esm/deno.js +1 -1
- package/esm/src/agent/streaming/fork-runtime-part-mapper.d.ts +59 -0
- package/esm/src/agent/streaming/fork-runtime-part-mapper.d.ts.map +1 -0
- package/esm/src/agent/streaming/fork-runtime-part-mapper.js +246 -0
- package/esm/src/agent/streaming/fork-runtime-stream.d.ts +2 -50
- package/esm/src/agent/streaming/fork-runtime-stream.d.ts.map +1 -1
- package/esm/src/agent/streaming/fork-runtime-stream.js +3 -245
- package/esm/src/integrations/_data.d.ts.map +1 -1
- package/esm/src/integrations/_data.js +1 -42
- package/esm/src/oauth/providers/common.d.ts.map +1 -1
- package/esm/src/oauth/providers/common.js +6 -1
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/esm/src/workflow/claude-code/react/event-state-reducer.d.ts +37 -0
- package/esm/src/workflow/claude-code/react/event-state-reducer.d.ts.map +1 -0
- package/esm/src/workflow/claude-code/react/event-state-reducer.js +104 -0
- package/esm/src/workflow/claude-code/react/use-claude-code-stream.d.ts +3 -35
- package/esm/src/workflow/claude-code/react/use-claude-code-stream.d.ts.map +1 -1
- package/esm/src/workflow/claude-code/react/use-claude-code-stream.js +9 -78
- package/esm/src/workflow/claude-code/react/use-claude-code-websocket.d.ts +2 -27
- package/esm/src/workflow/claude-code/react/use-claude-code-websocket.d.ts.map +1 -1
- package/esm/src/workflow/claude-code/react/use-claude-code-websocket.js +5 -57
- package/esm/src/workflow/executor/dag/index.d.ts +1 -2
- package/esm/src/workflow/executor/dag/index.d.ts.map +1 -1
- package/esm/src/workflow/executor/dag/index.js +37 -216
- package/esm/src/workflow/executor/dag/loop-node-strategy.d.ts +13 -0
- package/esm/src/workflow/executor/dag/loop-node-strategy.d.ts.map +1 -0
- package/esm/src/workflow/executor/dag/loop-node-strategy.js +134 -0
- package/esm/src/workflow/executor/dag/map-node-strategy.d.ts +13 -0
- package/esm/src/workflow/executor/dag/map-node-strategy.d.ts.map +1 -0
- package/esm/src/workflow/executor/dag/map-node-strategy.js +78 -0
- package/esm/src/workflow/executor/dag/node-strategy-types.d.ts +11 -0
- package/esm/src/workflow/executor/dag/node-strategy-types.d.ts.map +1 -0
- package/esm/src/workflow/executor/dag/node-strategy-types.js +1 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as dntShim from "../../../../_dnt.shims.js";
|
|
7
7
|
import { useCallback, useEffect, useRef, useState } from "../../../../react/react.js";
|
|
8
|
+
import { createClaudeCodeEventState, reduceClaudeCodeEventState, } from "./event-state-reducer.js";
|
|
8
9
|
import { REQUEST_ERROR } from "../../../errors/index.js";
|
|
9
10
|
/** Default delay before reconnecting after disconnect */
|
|
10
11
|
const DEFAULT_RECONNECT_DELAY_MS = 1_000;
|
|
@@ -48,16 +49,9 @@ const DEFAULT_MAX_EVENT_HISTORY = 100;
|
|
|
48
49
|
export function useClaudeCodeStream(options) {
|
|
49
50
|
const { url, runId, autoConnect = true, autoReconnect = true, maxReconnectAttempts = 5, reconnectDelay = DEFAULT_RECONNECT_DELAY_MS, keepEventHistory = false, maxEventHistory = DEFAULT_MAX_EVENT_HISTORY, onEvent, onConnect, onDisconnect, onError, onComplete, } = options;
|
|
50
51
|
const [state, setState] = useState({
|
|
52
|
+
...createClaudeCodeEventState(),
|
|
51
53
|
isConnected: false,
|
|
52
|
-
isRunning: false,
|
|
53
|
-
currentIteration: 0,
|
|
54
|
-
maxIterations: 20,
|
|
55
|
-
text: "",
|
|
56
|
-
currentTool: null,
|
|
57
|
-
toolCalls: [],
|
|
58
54
|
allToolCalls: [],
|
|
59
|
-
result: null,
|
|
60
|
-
error: null,
|
|
61
55
|
events: [],
|
|
62
56
|
});
|
|
63
57
|
const eventSourceRef = useRef(null);
|
|
@@ -67,76 +61,13 @@ export function useClaudeCodeStream(options) {
|
|
|
67
61
|
const processEvent = useCallback((event) => {
|
|
68
62
|
onEvent?.(event);
|
|
69
63
|
setState((prev) => {
|
|
70
|
-
const newState =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
newState.isRunning = true;
|
|
78
|
-
newState.currentIteration = event.iteration;
|
|
79
|
-
newState.maxIterations = event.maxIterations;
|
|
80
|
-
newState.toolCalls = [];
|
|
81
|
-
newState.currentTool = null;
|
|
82
|
-
break;
|
|
83
|
-
case "text_delta":
|
|
84
|
-
newState.text = prev.text + event.content;
|
|
85
|
-
break;
|
|
86
|
-
case "text_complete":
|
|
87
|
-
newState.text = event.content;
|
|
88
|
-
break;
|
|
89
|
-
case "tool_call_start":
|
|
90
|
-
newState.currentTool = {
|
|
91
|
-
id: event.toolCallId,
|
|
92
|
-
name: event.toolName,
|
|
93
|
-
input: {},
|
|
94
|
-
};
|
|
95
|
-
break;
|
|
96
|
-
case "tool_call_complete":
|
|
97
|
-
newState.currentTool = null;
|
|
98
|
-
newState.toolCalls = [
|
|
99
|
-
...prev.toolCalls,
|
|
100
|
-
{
|
|
101
|
-
id: event.toolCallId,
|
|
102
|
-
name: event.toolName,
|
|
103
|
-
input: event.input,
|
|
104
|
-
},
|
|
105
|
-
];
|
|
106
|
-
break;
|
|
107
|
-
case "tool_result":
|
|
108
|
-
// Update the tool call with its result
|
|
109
|
-
newState.toolCalls = prev.toolCalls.map((tc) => tc.id === event.toolCallId
|
|
110
|
-
? { ...tc, output: event.output, isError: event.isError }
|
|
111
|
-
: tc);
|
|
112
|
-
// Add to all tool calls
|
|
113
|
-
newState.allToolCalls = [
|
|
114
|
-
...prev.allToolCalls,
|
|
115
|
-
{
|
|
116
|
-
iteration: event.iteration || prev.currentIteration,
|
|
117
|
-
id: event.toolCallId,
|
|
118
|
-
name: event.toolName,
|
|
119
|
-
input: prev.toolCalls.find((tc) => tc.id === event.toolCallId)?.input || {},
|
|
120
|
-
output: event.output,
|
|
121
|
-
isError: event.isError,
|
|
122
|
-
},
|
|
123
|
-
];
|
|
124
|
-
break;
|
|
125
|
-
case "iteration_complete":
|
|
126
|
-
newState.currentTool = null;
|
|
127
|
-
break;
|
|
128
|
-
case "complete":
|
|
129
|
-
newState.isRunning = false;
|
|
130
|
-
newState.result = event.result;
|
|
131
|
-
newState.currentTool = null;
|
|
132
|
-
onComplete?.(event.result);
|
|
133
|
-
break;
|
|
134
|
-
case "error":
|
|
135
|
-
newState.error = event.message;
|
|
136
|
-
if (!event.recoverable) {
|
|
137
|
-
newState.isRunning = false;
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
64
|
+
const newState = reduceClaudeCodeEventState(prev, event, {
|
|
65
|
+
keepEventHistory,
|
|
66
|
+
maxEventHistory,
|
|
67
|
+
trackAllToolCalls: true,
|
|
68
|
+
});
|
|
69
|
+
if (event.type === "complete") {
|
|
70
|
+
onComplete?.(event.result);
|
|
140
71
|
}
|
|
141
72
|
return newState;
|
|
142
73
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ClaudeCodeEventExtended, ClaudeCodeResult } from "../types.js";
|
|
2
|
+
import { type ClaudeCodeEventState } from "./event-state-reducer.js";
|
|
2
3
|
/**
|
|
3
4
|
* Pending approval state
|
|
4
5
|
*/
|
|
@@ -22,41 +23,15 @@ export interface PendingInput {
|
|
|
22
23
|
/**
|
|
23
24
|
* State for Claude Code WebSocket
|
|
24
25
|
*/
|
|
25
|
-
export interface UseClaudeCodeWebSocketState {
|
|
26
|
+
export interface UseClaudeCodeWebSocketState extends ClaudeCodeEventState {
|
|
26
27
|
/** Whether currently connected */
|
|
27
28
|
isConnected: boolean;
|
|
28
|
-
/** Whether agent is currently executing */
|
|
29
|
-
isRunning: boolean;
|
|
30
29
|
/** Whether agent was cancelled */
|
|
31
30
|
isCancelled: boolean;
|
|
32
|
-
/** Current iteration number */
|
|
33
|
-
currentIteration: number;
|
|
34
|
-
/** Maximum iterations allowed */
|
|
35
|
-
maxIterations: number;
|
|
36
|
-
/** Accumulated text output */
|
|
37
|
-
text: string;
|
|
38
|
-
/** Current tool being executed (if any) */
|
|
39
|
-
currentTool: {
|
|
40
|
-
id: string;
|
|
41
|
-
name: string;
|
|
42
|
-
input: Record<string, unknown>;
|
|
43
|
-
} | null;
|
|
44
|
-
/** Tool calls in current iteration */
|
|
45
|
-
toolCalls: Array<{
|
|
46
|
-
id: string;
|
|
47
|
-
name: string;
|
|
48
|
-
input: Record<string, unknown>;
|
|
49
|
-
output?: string;
|
|
50
|
-
isError?: boolean;
|
|
51
|
-
}>;
|
|
52
31
|
/** Pending approval requests */
|
|
53
32
|
pendingApprovals: PendingApproval[];
|
|
54
33
|
/** Pending input request (if any) */
|
|
55
34
|
pendingInput: PendingInput | null;
|
|
56
|
-
/** Final result (when complete) */
|
|
57
|
-
result: ClaudeCodeResult | null;
|
|
58
|
-
/** Error message (if any) */
|
|
59
|
-
error: string | null;
|
|
60
35
|
}
|
|
61
36
|
/**
|
|
62
37
|
* Options for useClaudeCodeWebSocket hook
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-claude-code-websocket.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/claude-code/react/use-claude-code-websocket.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAGV,uBAAuB,EACvB,gBAAgB,EAEjB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"use-claude-code-websocket.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/claude-code/react/use-claude-code-websocket.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAGV,uBAAuB,EACvB,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,oBAAoB,EAI1B,MAAM,0BAA0B,CAAC;AASlC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA4B,SAAQ,oBAAoB;IACvE,kCAAkC;IAClC,WAAW,EAAE,OAAO,CAAC;IAErB,kCAAkC;IAClC,WAAW,EAAE,OAAO,CAAC;IAErB,gCAAgC;IAChC,gBAAgB,EAAE,eAAe,EAAE,CAAC;IAEpC,qCAAqC;IACrC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;IAEZ,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IAEd,4BAA4B;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,6BAA6B;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,yBAAyB;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,gBAAgB;IAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAChD,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAC;IACxD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,2BAA2B;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB,gCAAgC;IAChC,UAAU,EAAE,MAAM,IAAI,CAAC;IAEvB,iCAAiC;IACjC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,kCAAkC;IAClC,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAEtC,iCAAiC;IACjC,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAEtD,sBAAsB;IACtB,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,6BAA6B,GACrC,2BAA2B,GAAG,6BAA6B,CAkR7D"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as dntShim from "../../../../_dnt.shims.js";
|
|
7
7
|
import { useCallback, useEffect, useRef, useState } from "../../../../react/react.js";
|
|
8
|
+
import { createClaudeCodeEventState, isClaudeCodeCoreEvent, reduceClaudeCodeEventState, } from "./event-state-reducer.js";
|
|
8
9
|
import { NETWORK_ERROR, REQUEST_ERROR } from "../../../errors/index.js";
|
|
9
10
|
/** Default delay before reconnecting after disconnect */
|
|
10
11
|
const DEFAULT_RECONNECT_DELAY_MS = 1_000;
|
|
@@ -52,18 +53,11 @@ const DEFAULT_PING_INTERVAL_MS = 30_000;
|
|
|
52
53
|
export function useClaudeCodeWebSocket(options) {
|
|
53
54
|
const { url, runId, autoConnect = true, autoReconnect = true, maxReconnectAttempts = 5, reconnectDelay = DEFAULT_RECONNECT_DELAY_MS, pingInterval = DEFAULT_PING_INTERVAL_MS, onEvent, onConnect, onDisconnect, onError, onComplete, onApprovalRequest, onInputRequest, } = options;
|
|
54
55
|
const [state, setState] = useState({
|
|
56
|
+
...createClaudeCodeEventState(),
|
|
55
57
|
isConnected: false,
|
|
56
|
-
isRunning: false,
|
|
57
58
|
isCancelled: false,
|
|
58
|
-
currentIteration: 0,
|
|
59
|
-
maxIterations: 20,
|
|
60
|
-
text: "",
|
|
61
|
-
currentTool: null,
|
|
62
|
-
toolCalls: [],
|
|
63
59
|
pendingApprovals: [],
|
|
64
60
|
pendingInput: null,
|
|
65
|
-
result: null,
|
|
66
|
-
error: null,
|
|
67
61
|
});
|
|
68
62
|
const socketRef = useRef(null);
|
|
69
63
|
const reconnectAttemptsRef = useRef(0);
|
|
@@ -73,61 +67,15 @@ export function useClaudeCodeWebSocket(options) {
|
|
|
73
67
|
const processEvent = useCallback((event) => {
|
|
74
68
|
onEvent?.(event);
|
|
75
69
|
setState((prev) => {
|
|
76
|
-
const newState =
|
|
70
|
+
const newState = isClaudeCodeCoreEvent(event)
|
|
71
|
+
? reduceClaudeCodeEventState(prev, event)
|
|
72
|
+
: { ...prev };
|
|
77
73
|
switch (event.type) {
|
|
78
|
-
case "iteration_start":
|
|
79
|
-
newState.isRunning = true;
|
|
80
|
-
newState.currentIteration = event.iteration;
|
|
81
|
-
newState.maxIterations = event.maxIterations;
|
|
82
|
-
newState.toolCalls = [];
|
|
83
|
-
newState.currentTool = null;
|
|
84
|
-
break;
|
|
85
|
-
case "text_delta":
|
|
86
|
-
newState.text = prev.text + event.content;
|
|
87
|
-
break;
|
|
88
|
-
case "text_complete":
|
|
89
|
-
newState.text = event.content;
|
|
90
|
-
break;
|
|
91
|
-
case "tool_call_start":
|
|
92
|
-
newState.currentTool = {
|
|
93
|
-
id: event.toolCallId,
|
|
94
|
-
name: event.toolName,
|
|
95
|
-
input: {},
|
|
96
|
-
};
|
|
97
|
-
break;
|
|
98
|
-
case "tool_call_complete":
|
|
99
|
-
newState.currentTool = null;
|
|
100
|
-
newState.toolCalls = [
|
|
101
|
-
...prev.toolCalls,
|
|
102
|
-
{
|
|
103
|
-
id: event.toolCallId,
|
|
104
|
-
name: event.toolName,
|
|
105
|
-
input: event.input,
|
|
106
|
-
},
|
|
107
|
-
];
|
|
108
|
-
break;
|
|
109
|
-
case "tool_result":
|
|
110
|
-
newState.toolCalls = prev.toolCalls.map((tc) => tc.id === event.toolCallId
|
|
111
|
-
? { ...tc, output: event.output, isError: event.isError }
|
|
112
|
-
: tc);
|
|
113
|
-
break;
|
|
114
|
-
case "iteration_complete":
|
|
115
|
-
newState.currentTool = null;
|
|
116
|
-
break;
|
|
117
74
|
case "complete":
|
|
118
|
-
newState.isRunning = false;
|
|
119
|
-
newState.result = event.result;
|
|
120
|
-
newState.currentTool = null;
|
|
121
75
|
newState.pendingApprovals = [];
|
|
122
76
|
newState.pendingInput = null;
|
|
123
77
|
onComplete?.(event.result);
|
|
124
78
|
break;
|
|
125
|
-
case "error":
|
|
126
|
-
newState.error = event.message;
|
|
127
|
-
if (!event.recoverable) {
|
|
128
|
-
newState.isRunning = false;
|
|
129
|
-
}
|
|
130
|
-
break;
|
|
131
79
|
case "cancelled":
|
|
132
80
|
newState.isRunning = false;
|
|
133
81
|
newState.isCancelled = true;
|
|
@@ -15,11 +15,10 @@ export declare class DAGExecutor {
|
|
|
15
15
|
private executeNode;
|
|
16
16
|
private executeStepNode;
|
|
17
17
|
private executeParallelNode;
|
|
18
|
-
private executeMapNode;
|
|
19
18
|
private executeBranchNode;
|
|
20
19
|
private executeWaitNode;
|
|
21
20
|
private executeSubWorkflowNode;
|
|
22
|
-
private executeLoopNode;
|
|
23
21
|
private checkpoint;
|
|
22
|
+
private executeChildGraph;
|
|
24
23
|
}
|
|
25
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/executor/dag/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/executor/dag/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAQV,YAAY,EAEZ,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAIxB,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE7F,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAOpB,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAA4B;gBAE9B,MAAM,EAAE,iBAAiB;IAQ/B,OAAO,CACX,KAAK,EAAE,YAAY,EAAE,EACrB,GAAG,EAAE,WAAW,EAChB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,kBAAkB,CAAC;YAsGhB,WAAW;YAgEX,eAAe;YA0Bf,mBAAmB;YA4CnB,iBAAiB;YA+DjB,eAAe;YA8Bf,sBAAsB;YA+DtB,UAAU;YAqBV,iBAAiB;CAiBhC"}
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module ai/workflow/executor/dag
|
|
7
7
|
*/
|
|
8
|
-
import { generateId
|
|
8
|
+
import { generateId } from "../../types.js";
|
|
9
9
|
import { INVALID_ARGUMENT, NOT_SUPPORTED } from "../../../errors/index.js";
|
|
10
|
-
import { deriveNodeStatus, shouldCheckpoint
|
|
10
|
+
import { deriveNodeStatus, shouldCheckpoint } from "./utils.js";
|
|
11
11
|
import { buildGraph, getReadyNodes, hasCycle, updateInDegreesForCompletedNodes } from "./graph.js";
|
|
12
|
+
import { executeLoopNodeStrategy } from "./loop-node-strategy.js";
|
|
13
|
+
import { executeMapNodeStrategy } from "./map-node-strategy.js";
|
|
12
14
|
export class DAGExecutor {
|
|
13
15
|
config;
|
|
14
16
|
constructor(config) {
|
|
@@ -118,7 +120,16 @@ export class DAGExecutor {
|
|
|
118
120
|
case "parallel":
|
|
119
121
|
return this.executeParallelNode(node, config, context, nodeStates);
|
|
120
122
|
case "map":
|
|
121
|
-
return
|
|
123
|
+
return executeMapNodeStrategy({
|
|
124
|
+
node,
|
|
125
|
+
config,
|
|
126
|
+
context,
|
|
127
|
+
nodeStates,
|
|
128
|
+
runtime: {
|
|
129
|
+
executeChildGraph: (nodes, run, options) => this.executeChildGraph(nodes, run, options),
|
|
130
|
+
onNodeComplete: this.config.onNodeComplete,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
122
133
|
case "branch":
|
|
123
134
|
return this.executeBranchNode(node, config, context, nodeStates);
|
|
124
135
|
case "wait":
|
|
@@ -126,7 +137,16 @@ export class DAGExecutor {
|
|
|
126
137
|
case "subWorkflow":
|
|
127
138
|
return this.executeSubWorkflowNode(node, config, context);
|
|
128
139
|
case "loop":
|
|
129
|
-
return
|
|
140
|
+
return executeLoopNodeStrategy({
|
|
141
|
+
node,
|
|
142
|
+
config,
|
|
143
|
+
context,
|
|
144
|
+
nodeStates,
|
|
145
|
+
runtime: {
|
|
146
|
+
executeChildGraph: (nodes, run) => this.executeChildGraph(nodes, run),
|
|
147
|
+
onNodeComplete: this.config.onNodeComplete,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
130
150
|
default:
|
|
131
151
|
throw INVALID_ARGUMENT.create({
|
|
132
152
|
detail: `Unknown node type "${config.type}" for node "${node.id}". ` +
|
|
@@ -186,85 +206,6 @@ export class DAGExecutor {
|
|
|
186
206
|
waiting: result.waiting,
|
|
187
207
|
};
|
|
188
208
|
}
|
|
189
|
-
async executeMapNode(node, config, context, nodeStates) {
|
|
190
|
-
const startTime = Date.now();
|
|
191
|
-
const items = typeof config.items === "function" ? await config.items(context) : config.items;
|
|
192
|
-
if (!Array.isArray(items)) {
|
|
193
|
-
throw INVALID_ARGUMENT.create({ detail: `Map node "${node.id}" items must be an array` });
|
|
194
|
-
}
|
|
195
|
-
if (items.length === 0) {
|
|
196
|
-
const state = {
|
|
197
|
-
nodeId: node.id,
|
|
198
|
-
status: "completed",
|
|
199
|
-
output: [],
|
|
200
|
-
attempt: 1,
|
|
201
|
-
startedAt: new Date(startTime),
|
|
202
|
-
completedAt: new Date(),
|
|
203
|
-
};
|
|
204
|
-
return { state, contextUpdates: { [node.id]: [] }, waiting: false };
|
|
205
|
-
}
|
|
206
|
-
const isWorkflowDef = (p) => typeof p === "object" && p !== null && "steps" in p;
|
|
207
|
-
const childNodes = items.map((item, i) => {
|
|
208
|
-
const childId = `${node.id}_${i}`;
|
|
209
|
-
if (isWorkflowDef(config.processor)) {
|
|
210
|
-
return {
|
|
211
|
-
id: childId,
|
|
212
|
-
config: {
|
|
213
|
-
type: "subWorkflow",
|
|
214
|
-
workflow: config.processor,
|
|
215
|
-
input: item,
|
|
216
|
-
retry: config.retry,
|
|
217
|
-
checkpoint: false,
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
const processorConfig = { ...config.processor.config };
|
|
222
|
-
if (processorConfig.type === "step") {
|
|
223
|
-
processorConfig.input = item;
|
|
224
|
-
}
|
|
225
|
-
return { id: childId, config: processorConfig };
|
|
226
|
-
});
|
|
227
|
-
const originalConcurrency = this.config.maxConcurrency;
|
|
228
|
-
if (config.concurrency) {
|
|
229
|
-
this.config.maxConcurrency = config.concurrency;
|
|
230
|
-
}
|
|
231
|
-
try {
|
|
232
|
-
const result = await this.execute(childNodes, {
|
|
233
|
-
id: `${node.id}_map`,
|
|
234
|
-
workflowId: "",
|
|
235
|
-
status: "running",
|
|
236
|
-
input: context.input,
|
|
237
|
-
// Carry already-accumulated child states so completed children are
|
|
238
|
-
// skipped on resume instead of re-executing (H8).
|
|
239
|
-
nodeStates,
|
|
240
|
-
currentNodes: [],
|
|
241
|
-
context: { ...context },
|
|
242
|
-
checkpoints: [],
|
|
243
|
-
pendingApprovals: [],
|
|
244
|
-
createdAt: new Date(),
|
|
245
|
-
});
|
|
246
|
-
Object.assign(nodeStates, result.nodeStates);
|
|
247
|
-
const outputs = childNodes.map((child) => result.nodeStates[child.id]?.output);
|
|
248
|
-
const state = {
|
|
249
|
-
nodeId: node.id,
|
|
250
|
-
status: deriveNodeStatus(result.completed, result.waiting),
|
|
251
|
-
output: outputs,
|
|
252
|
-
error: result.error,
|
|
253
|
-
attempt: 1,
|
|
254
|
-
startedAt: new Date(startTime),
|
|
255
|
-
completedAt: result.completed ? new Date() : undefined,
|
|
256
|
-
};
|
|
257
|
-
this.config.onNodeComplete?.(node.id, state);
|
|
258
|
-
return {
|
|
259
|
-
state,
|
|
260
|
-
contextUpdates: result.completed ? { [node.id]: outputs } : {},
|
|
261
|
-
waiting: result.waiting,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
finally {
|
|
265
|
-
this.config.maxConcurrency = originalConcurrency;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
209
|
async executeBranchNode(node, config, context, nodeStates) {
|
|
269
210
|
const startTime = Date.now();
|
|
270
211
|
const conditionResult = await config.condition(context);
|
|
@@ -383,139 +324,6 @@ export class DAGExecutor {
|
|
|
383
324
|
waiting: result.waiting,
|
|
384
325
|
};
|
|
385
326
|
}
|
|
386
|
-
async executeLoopNode(node, config, context, nodeStates) {
|
|
387
|
-
const startTime = Date.now();
|
|
388
|
-
const previousResults = [];
|
|
389
|
-
let iteration = 0;
|
|
390
|
-
let exitReason = "condition";
|
|
391
|
-
let lastError;
|
|
392
|
-
const existingLoopState = context[`${node.id}_loop_state`];
|
|
393
|
-
// Child node states for the in-flight (resumed) iteration, so its already
|
|
394
|
-
// completed steps are not re-executed on resume (H9).
|
|
395
|
-
let resumeIterationNodeStates;
|
|
396
|
-
let resumeIteration;
|
|
397
|
-
if (existingLoopState) {
|
|
398
|
-
iteration = existingLoopState.iteration;
|
|
399
|
-
previousResults.push(...existingLoopState.previousResults);
|
|
400
|
-
resumeIterationNodeStates = existingLoopState.iterationNodeStates;
|
|
401
|
-
resumeIteration = existingLoopState.iteration;
|
|
402
|
-
}
|
|
403
|
-
while (iteration < config.maxIterations) {
|
|
404
|
-
const loopContext = {
|
|
405
|
-
iteration,
|
|
406
|
-
totalIterations: iteration,
|
|
407
|
-
previousResults: [...previousResults],
|
|
408
|
-
isFirstIteration: iteration === 0,
|
|
409
|
-
isLastAllowedIteration: iteration === config.maxIterations - 1,
|
|
410
|
-
};
|
|
411
|
-
if (!(await config.while(context, loopContext))) {
|
|
412
|
-
exitReason = "condition";
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
const steps = typeof config.steps === "function"
|
|
416
|
-
? config.steps(context, loopContext)
|
|
417
|
-
: config.steps;
|
|
418
|
-
// On resume, rehydrate the in-flight iteration's child node states so its
|
|
419
|
-
// already-completed steps are skipped instead of re-executed (H9).
|
|
420
|
-
const iterationNodeStates = resumeIteration === iteration && resumeIterationNodeStates
|
|
421
|
-
? { ...resumeIterationNodeStates }
|
|
422
|
-
: {};
|
|
423
|
-
// Only rehydrate once; subsequent iterations start fresh.
|
|
424
|
-
resumeIterationNodeStates = undefined;
|
|
425
|
-
const result = await this.execute(steps, {
|
|
426
|
-
id: `${node.id}_iter_${iteration}`,
|
|
427
|
-
workflowId: "",
|
|
428
|
-
status: "running",
|
|
429
|
-
input: context.input,
|
|
430
|
-
nodeStates: iterationNodeStates,
|
|
431
|
-
currentNodes: [],
|
|
432
|
-
context: { ...context, _loop: loopContext },
|
|
433
|
-
checkpoints: [],
|
|
434
|
-
pendingApprovals: [],
|
|
435
|
-
createdAt: new Date(),
|
|
436
|
-
});
|
|
437
|
-
if (result.waiting) {
|
|
438
|
-
Object.assign(nodeStates, result.nodeStates);
|
|
439
|
-
const state = {
|
|
440
|
-
nodeId: node.id,
|
|
441
|
-
status: "running",
|
|
442
|
-
output: { iteration, waiting: true, previousResults },
|
|
443
|
-
attempt: 1,
|
|
444
|
-
startedAt: new Date(startTime),
|
|
445
|
-
};
|
|
446
|
-
return {
|
|
447
|
-
state,
|
|
448
|
-
contextUpdates: {
|
|
449
|
-
...result.context,
|
|
450
|
-
[`${node.id}_loop_state`]: {
|
|
451
|
-
iteration,
|
|
452
|
-
previousResults,
|
|
453
|
-
// Persist the in-flight iteration's child states so completed
|
|
454
|
-
// steps are not re-executed when this iteration resumes (H9).
|
|
455
|
-
iterationNodeStates: result.nodeStates,
|
|
456
|
-
},
|
|
457
|
-
},
|
|
458
|
-
waiting: true,
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
if (result.error) {
|
|
462
|
-
lastError = result.error;
|
|
463
|
-
exitReason = "error";
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
previousResults.push(result.context);
|
|
467
|
-
Object.assign(context, result.context);
|
|
468
|
-
Object.assign(nodeStates, result.nodeStates);
|
|
469
|
-
if (config.delay && iteration < config.maxIterations - 1) {
|
|
470
|
-
const delayMs = typeof config.delay === "number"
|
|
471
|
-
? config.delay
|
|
472
|
-
: parseDuration(config.delay);
|
|
473
|
-
await sleep(delayMs);
|
|
474
|
-
}
|
|
475
|
-
iteration++;
|
|
476
|
-
}
|
|
477
|
-
if (iteration >= config.maxIterations && exitReason !== "condition") {
|
|
478
|
-
exitReason = "maxIterations";
|
|
479
|
-
}
|
|
480
|
-
const finalLoopContext = {
|
|
481
|
-
iteration,
|
|
482
|
-
totalIterations: iteration,
|
|
483
|
-
previousResults,
|
|
484
|
-
isFirstIteration: false,
|
|
485
|
-
isLastAllowedIteration: true,
|
|
486
|
-
};
|
|
487
|
-
let completionUpdates = {};
|
|
488
|
-
if (exitReason === "maxIterations" && config.onMaxIterations) {
|
|
489
|
-
completionUpdates = await config.onMaxIterations(context, finalLoopContext);
|
|
490
|
-
}
|
|
491
|
-
else if (exitReason === "condition" && config.onComplete) {
|
|
492
|
-
completionUpdates = await config.onComplete(context, finalLoopContext);
|
|
493
|
-
}
|
|
494
|
-
const output = {
|
|
495
|
-
exitReason,
|
|
496
|
-
iterations: iteration,
|
|
497
|
-
previousResults,
|
|
498
|
-
...completionUpdates,
|
|
499
|
-
};
|
|
500
|
-
const state = {
|
|
501
|
-
nodeId: node.id,
|
|
502
|
-
status: exitReason === "error" ? "failed" : "completed",
|
|
503
|
-
output,
|
|
504
|
-
error: lastError,
|
|
505
|
-
attempt: 1,
|
|
506
|
-
startedAt: new Date(startTime),
|
|
507
|
-
completedAt: new Date(),
|
|
508
|
-
};
|
|
509
|
-
this.config.onNodeComplete?.(node.id, state);
|
|
510
|
-
return {
|
|
511
|
-
state,
|
|
512
|
-
contextUpdates: {
|
|
513
|
-
[node.id]: output,
|
|
514
|
-
...completionUpdates,
|
|
515
|
-
},
|
|
516
|
-
waiting: false,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
327
|
async checkpoint(runId, nodeId, context, nodeStates) {
|
|
520
328
|
if (!this.config.checkpointManager) {
|
|
521
329
|
return;
|
|
@@ -529,4 +337,17 @@ export class DAGExecutor {
|
|
|
529
337
|
};
|
|
530
338
|
await this.config.checkpointManager.save(runId, checkpoint);
|
|
531
339
|
}
|
|
340
|
+
async executeChildGraph(nodes, run, options) {
|
|
341
|
+
if (!options?.maxConcurrency) {
|
|
342
|
+
return await this.execute(nodes, run);
|
|
343
|
+
}
|
|
344
|
+
const originalConcurrency = this.config.maxConcurrency;
|
|
345
|
+
this.config.maxConcurrency = options.maxConcurrency;
|
|
346
|
+
try {
|
|
347
|
+
return await this.execute(nodes, run);
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
this.config.maxConcurrency = originalConcurrency;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
532
353
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LoopNodeConfig, NodeState, WorkflowContext, WorkflowNode } from "../../types.js";
|
|
2
|
+
import type { NodeExecutionResult } from "./types.js";
|
|
3
|
+
import type { NodeStrategyRuntime } from "./node-strategy-types.js";
|
|
4
|
+
interface ExecuteLoopNodeStrategyInput {
|
|
5
|
+
node: WorkflowNode;
|
|
6
|
+
config: LoopNodeConfig;
|
|
7
|
+
context: WorkflowContext;
|
|
8
|
+
nodeStates: Record<string, NodeState>;
|
|
9
|
+
runtime: NodeStrategyRuntime;
|
|
10
|
+
}
|
|
11
|
+
export declare function executeLoopNodeStrategy(input: ExecuteLoopNodeStrategyInput): Promise<NodeExecutionResult>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=loop-node-strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop-node-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/src/workflow/executor/dag/loop-node-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,cAAc,EACd,SAAS,EACT,eAAe,EACf,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,UAAU,4BAA4B;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACtC,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAQD,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAwJ9B"}
|