@kiberon-labs/behave-graph-flow 2.0.0 → 3.0.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/.storybook/manager.ts +6 -0
- package/.storybook/preview.ts +49 -1
- package/.storybook/styles.css +9 -3
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +368 -0
- package/dist/AnyControlImpl-Ds-CShIB.js +20 -0
- package/dist/AnyControlImpl-Ds-CShIB.js.map +1 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js +166 -0
- package/dist/DocumentationBrowserPanelImpl-deZNzFX8.js.map +1 -0
- package/dist/index.css +36 -33
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1865 -550
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14357 -11221
- package/dist/index.js.map +1 -1
- package/dist/noteImpl-KkrrWgJd.js +242 -0
- package/dist/noteImpl-KkrrWgJd.js.map +1 -0
- package/dist/styles.module-CvmpDkZj.css +3 -0
- package/dist/styles.module-CvmpDkZj.css.map +1 -0
- package/dist/styles.module-DZxg8aW9.js +271 -0
- package/dist/styles.module-DZxg8aW9.js.map +1 -0
- package/dist/useChangeNodeData-ChQGK7AI.js +23 -0
- package/dist/useChangeNodeData-ChQGK7AI.js.map +1 -0
- package/docs/protocol.md +43 -20
- package/package.json +5 -9
- package/src/components/FloatingToolbar/index.module.css +5 -13
- package/src/components/FloatingToolbar/index.tsx +9 -9
- package/src/components/Flow.tsx +34 -23
- package/src/components/contextMenus/DynamicContextMenu.tsx +85 -0
- package/src/components/contextMenus/NodePicker.module.css +13 -13
- package/src/components/contextMenus/edge.tsx +9 -95
- package/src/components/contextMenus/node.tsx +9 -149
- package/src/components/contextMenus/selection.tsx +5 -71
- package/src/components/controls/any/AnyControlImpl.tsx +14 -0
- package/src/components/controls/any/index.tsx +13 -2
- package/src/components/edges/index.tsx +75 -69
- package/src/components/layoutController/index.module.css +3 -0
- package/src/components/layoutController/index.tsx +24 -1
- package/src/components/layoutController/utils.ts +46 -3
- package/src/components/menubar/defaults.tsx +55 -19
- package/src/components/menubar/menuItem.module.css +18 -3
- package/src/components/menubar/menuItem.tsx +34 -1
- package/src/components/nodes/behave/NodeContainer.module.css +26 -25
- package/src/components/nodes/group/index.tsx +3 -3
- package/src/components/nodes/wrapper/styles.module.css +6 -32
- package/src/components/panels/alignment/index.module.css +0 -10
- package/src/components/panels/alignment/index.tsx +4 -4
- package/src/components/panels/base/styles.module.css +2 -2
- package/src/components/panels/common/PanelHeader.module.css +24 -0
- package/src/components/panels/common/PanelHeader.tsx +22 -0
- package/src/components/panels/common/SectionTitle.module.css +13 -0
- package/src/components/panels/common/SectionTitle.tsx +10 -0
- package/src/components/panels/events/EditEventPanel.tsx +14 -5
- package/src/components/panels/events/ManageEventsPanel.tsx +11 -8
- package/src/components/panels/events/styles.module.css +6 -64
- package/src/components/panels/graphProperties/index.tsx +125 -0
- package/src/components/panels/history/index.tsx +2 -2
- package/src/components/panels/history/styles.module.css +0 -9
- package/src/components/panels/keymaps/index.module.css +3 -13
- package/src/components/panels/keymaps/index.tsx +1 -2
- package/src/components/panels/layers/index.tsx +20 -15
- package/src/components/panels/layers/styles.module.css +9 -12
- package/src/components/panels/legend/index.tsx +1 -1
- package/src/components/panels/logs/index.module.css +25 -19
- package/src/components/panels/logs/index.tsx +7 -7
- package/src/components/panels/nodeInputs/InputsGroup.tsx +1 -0
- package/src/components/panels/nodeInputs/NodeSettings.tsx +2 -2
- package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +1 -1
- package/src/components/panels/nodeInputs/OutputsGroup.tsx +2 -12
- package/src/components/panels/nodeInputs/index.module.css +99 -75
- package/src/components/panels/nodeInputs/index.tsx +21 -11
- package/src/components/panels/nodeInputs/useNodeHandlers.ts +2 -2
- package/src/components/panels/nodeInputs/useNodeInputsData.ts +23 -43
- package/src/components/panels/nodePicker/index.tsx +8 -8
- package/src/components/panels/panel/index.module.css +7 -7
- package/src/components/panels/search/index.module.css +0 -50
- package/src/components/panels/search/index.tsx +2 -2
- package/src/components/panels/systemSettings/ConversionsSettings.tsx +203 -0
- package/src/components/panels/systemSettings/index.tsx +221 -176
- package/src/components/panels/systemSettings/styles.module.css +135 -8
- package/src/components/panels/traces/GridLines.tsx +1 -1
- package/src/components/panels/traces/TimeGrid.tsx +3 -3
- package/src/components/panels/traces/TraceLane.tsx +1 -1
- package/src/components/panels/traces/index.module.css +1 -8
- package/src/components/panels/traces/index.tsx +8 -4
- package/src/components/panels/traces/useDerivedSpans.ts +241 -146
- package/src/components/panels/traces/utils.ts +8 -0
- package/src/components/panels/variables/CreateVariableScreen.tsx +3 -3
- package/src/components/panels/variables/ManageVariablesScreen.tsx +12 -9
- package/src/components/panels/variables/index.tsx +2 -2
- package/src/components/panels/variables/styles.module.css +4 -91
- package/src/components/primitives/icon.module.css +4 -4
- package/src/components/sockets/input/index.tsx +9 -2
- package/src/components/sockets/input/styles.module.css +2 -3
- package/src/components/sockets/output/index.tsx +10 -3
- package/src/components/sockets/output/styles.module.css +1 -6
- package/src/css/notes.css +135 -0
- package/src/css/prosemirror.css +3 -3
- package/src/css/rc-dock.css +143 -43
- package/src/css/rc-menu.css +56 -55
- package/src/css/themes/kiberon.css +127 -0
- package/src/css/vars.css +197 -13
- package/src/css/vscode-elements.css +124 -0
- package/src/generators/CallSubgraphGenerator.tsx +136 -0
- package/src/generators/CustomEventOnTriggeredGenerator.tsx +2 -2
- package/src/generators/GraphBoundaryGenerator.module.css +32 -0
- package/src/generators/GraphBoundaryGenerator.tsx +193 -0
- package/src/generators/SequenceGenerator.tsx +2 -2
- package/src/generators/SwitchOnIntegerGenerator.tsx +2 -2
- package/src/generators/SwitchOnStringGenerator.tsx +2 -2
- package/src/generators/callSubgraphSync.ts +126 -0
- package/src/generators/registerDefaultGenerators.ts +21 -0
- package/src/generators/registerDefaults.ts +26 -0
- package/src/hooks/useBehaveGraphFlow.ts +2 -2
- package/src/hooks/useFlowHandlers.ts +47 -9
- package/src/hooks/useWasdPan.ts +26 -4
- package/src/index.css +4 -16
- package/src/index.ts +17 -0
- package/src/manifest/contributionRegistry.ts +93 -0
- package/src/manifest/index.ts +4 -0
- package/src/manifest/loadManifest.ts +82 -0
- package/src/manifest/manifestPlugin.ts +29 -0
- package/src/manifest/passthroughValueType.ts +40 -0
- package/src/plugin/alignment/index.ts +22 -12
- package/src/plugin/autosave/controller.ts +366 -0
- package/src/plugin/autosave/index.tsx +114 -0
- package/src/plugin/autosave/panel/BackupPanel.tsx +141 -0
- package/src/plugin/autosave/panel/index.tsx +1 -0
- package/src/plugin/autosave/panel/styles.module.css +56 -0
- package/src/plugin/autosave/settings.ts +65 -0
- package/src/plugin/autosave/storage.ts +147 -0
- package/src/plugin/docs/index.tsx +2 -4
- package/src/plugin/docs/panel/DocumentationBrowserPanelImpl.tsx +200 -0
- package/src/plugin/docs/panel/index.tsx +15 -194
- package/src/plugin/docs/panel/styles.module.css +8 -8
- package/src/plugin/graphrunner/actions.ts +258 -185
- package/src/plugin/graphrunner/buttons.tsx +34 -26
- package/src/plugin/graphrunner/client.ts +4 -1
- package/src/plugin/graphrunner/index.tsx +29 -100
- package/src/plugin/graphrunner/panel.tsx +2 -2
- package/src/plugin/graphrunner/runController.ts +283 -0
- package/src/plugin/graphrunner/runner.ts +21 -192
- package/src/plugin/graphrunner/store.ts +14 -24
- package/src/plugin/graphrunner/styles.module.css +17 -57
- package/src/plugin/graphrunner/transport.ts +26 -0
- package/src/plugin/graphrunner/types.ts +21 -0
- package/src/plugin/graphrunner-local/execution-utils.ts +260 -80
- package/src/plugin/graphrunner-local/index.tsx +8 -2
- package/src/plugin/graphrunner-local/panel.tsx +131 -175
- package/src/plugin/graphrunner-local/styles.module.css +57 -76
- package/src/plugin/graphrunner-local/transport.ts +151 -184
- package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +2 -0
- package/src/plugin/graphrunner-webworker/index.tsx +4 -10
- package/src/plugin/graphrunner-webworker/store.ts +9 -0
- package/src/plugin/kitchen-sink/index.ts +38 -0
- package/src/{layout/dagre.tsx → plugin/layout/dagre.ts} +17 -5
- package/src/{layout → plugin/layout}/elk.ts +22 -6
- package/src/plugin/layout/index.ts +80 -0
- package/src/plugin/notes/FormatToolbar.tsx +200 -0
- package/src/plugin/notes/index.tsx +191 -0
- package/src/plugin/notes/nodeActions.ts +100 -0
- package/src/plugin/notes/note.tsx +20 -0
- package/src/plugin/notes/noteImpl.tsx +89 -0
- package/src/plugin/realtime/realtimeRunner.ts +58 -4
- package/src/specifics/CustomEventOnTriggeredSpecific.tsx +2 -2
- package/src/specifics/CustomEventTriggerSpecific.tsx +2 -2
- package/src/specifics/VariableGetSpecific.tsx +2 -2
- package/src/specifics/VariableSetSpecific.tsx +2 -2
- package/src/store/actions.tsx +5 -5
- package/src/store/commands.ts +278 -0
- package/src/store/contextMenu.ts +192 -0
- package/src/store/conversions.ts +47 -0
- package/src/store/flow.tsx +23 -38
- package/src/store/graphMeta.ts +39 -0
- package/src/store/hotKeys.tsx +301 -260
- package/src/store/layers.ts +3 -3
- package/src/store/registry.ts +12 -4
- package/src/store/selection.ts +3 -3
- package/src/store/settings.ts +82 -82
- package/src/store/settingsSchema.ts +210 -0
- package/src/store/tabs.ts +5 -1
- package/src/store/traces.ts +3 -3
- package/src/system/graph.ts +11 -14
- package/src/system/graphSession.ts +172 -0
- package/src/system/index.ts +3 -0
- package/src/system/notifications.ts +13 -0
- package/src/system/persistence.ts +82 -0
- package/src/system/plugin.ts +28 -0
- package/src/system/provider.tsx +64 -0
- package/src/system/system.ts +518 -88
- package/src/system/tabLoader.tsx +70 -32
- package/src/system/undoRedo.ts +1 -1
- package/src/transformers/Uigraph.ts +5 -4
- package/src/transformers/contract.ts +87 -0
- package/src/transformers/flowToBehave.ts +13 -5
- package/src/types/nodes.ts +8 -3
- package/src/types.ts +2 -0
- package/src/util/autoConvert.ts +200 -0
- package/src/util/isValidConnection.ts +23 -2
- package/stories/defaults/defaultStoryProvider.tsx +17 -14
- package/stories/defaults/systemGenerator.ts +6 -1
- package/stories/{components/nodes/comment.stories.tsx → plugins/notes.stories.tsx} +24 -30
- package/tests/autoConvert.test.ts +329 -0
- package/tests/autosavePlugin.test.ts +204 -0
- package/tests/callSubgraphSync.test.ts +148 -0
- package/tests/commandRegistry.test.ts +137 -0
- package/tests/contract.test.ts +51 -0
- package/tests/contractSerialize.test.ts +62 -0
- package/tests/deriveSpans.test.ts +71 -0
- package/tests/flowToBehave.test.ts +2 -1
- package/tests/hotkeys.test.ts +79 -0
- package/tests/keepAliveLifecycle.test.ts +167 -0
- package/tests/loadManifest.test.ts +113 -0
- package/tests/noteMarkdown.test.ts +65 -0
- package/tests/notesPlugin.test.ts +162 -0
- package/tests/persistence.test.ts +51 -0
- package/tests/saveLoad.test.ts +7 -6
- package/tests/settings.test.ts +178 -0
- package/tests/traceStore.test.ts +46 -0
- package/tests/visual/README.md +2 -2
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-events-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-layers-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-legend-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-localGraphRunner-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
- package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png +0 -0
- package/tests/visual/panels.visual.test.tsx +3 -3
- package/tests/wasdPan.test.ts +71 -0
- package/vitest.config.ts +1 -1
- package/vitest.visual.config.ts +7 -0
- package/.storybook/vscode.css +0 -814
- package/src/components/nodes/comment/FormatToolbar.tsx +0 -118
- package/src/components/nodes/comment/comment.tsx +0 -103
- package/src/components/nodes/comment/styles.module.css +0 -150
- package/src/components/panels/conversation/index.module.css +0 -151
- package/src/components/panels/conversation/index.tsx +0 -162
- package/src/components/panels/events/CustomEventsEditor.tsx +0 -384
- package/src/css/vscode.css +0 -13
- package/src/hooks/useDetachNodes.ts +0 -39
- package/src/plugin/graphrunner-webworker/types.ts +0 -17
- package/src/specifics/registerDefaultSpecifics.ts +0 -5
- package/src/store/chat.ts +0 -73
- package/src/store/graphRunnerClient.ts +0 -110
|
@@ -21,7 +21,8 @@ import type {
|
|
|
21
21
|
RunStatus,
|
|
22
22
|
ServerGraphRunnerMessage,
|
|
23
23
|
ServerVariable,
|
|
24
|
-
ServerEvent
|
|
24
|
+
ServerEvent,
|
|
25
|
+
TraceBatchEvent
|
|
25
26
|
} from '../graphrunner/types.js';
|
|
26
27
|
import { sleep } from '@kiberon-labs/behave-graph';
|
|
27
28
|
|
|
@@ -41,6 +42,12 @@ export interface ActiveRun {
|
|
|
41
42
|
isPaused: boolean;
|
|
42
43
|
executionPhase: 'start' | 'tick' | 'end' | 'completed';
|
|
43
44
|
currentTick: number;
|
|
45
|
+
/**
|
|
46
|
+
* Flushes any trace events still buffered by {@link setupTracing}. Set when
|
|
47
|
+
* tracing is enabled; call before emitting `completed`/`stopped` so the
|
|
48
|
+
* client receives the tail of the trace while the run id is still routable.
|
|
49
|
+
*/
|
|
50
|
+
flushTracing?: () => void;
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
export interface MessageContext {
|
|
@@ -188,20 +195,59 @@ export function handleGetCapabilities(ctx: MessageContext): void {
|
|
|
188
195
|
});
|
|
189
196
|
}
|
|
190
197
|
|
|
198
|
+
/** Flush buffered trace events roughly once per frame. */
|
|
199
|
+
const TRACE_FLUSH_INTERVAL_MS = 16;
|
|
200
|
+
/** Safety valve: flush early if a single window buffers this many events. */
|
|
201
|
+
const TRACE_FLUSH_MAX_EVENTS = 2048;
|
|
202
|
+
|
|
191
203
|
/**
|
|
192
|
-
* Setup tracing for a run
|
|
204
|
+
* Setup tracing for a run.
|
|
205
|
+
*
|
|
206
|
+
* Node execution events are buffered and flushed as a single `traceBatch`
|
|
207
|
+
* message per flush window instead of one `trace` message per event. A graph
|
|
208
|
+
* ticking at display rate executes every node twice per frame (start + end);
|
|
209
|
+
* sending each event individually made the message pipeline (store updates,
|
|
210
|
+
* postMessage for the worker runner) the dominant per-frame cost.
|
|
193
211
|
*/
|
|
194
212
|
export function setupTracing(
|
|
195
213
|
run: ActiveRun,
|
|
196
214
|
graphId: string,
|
|
197
215
|
ctx: MessageContext
|
|
198
216
|
): void {
|
|
199
|
-
|
|
200
|
-
|
|
217
|
+
let buffer: TraceBatchEvent[] = [];
|
|
218
|
+
let flushTimer: ReturnType<typeof setTimeout> | undefined;
|
|
219
|
+
|
|
220
|
+
const flush = () => {
|
|
221
|
+
if (flushTimer !== undefined) {
|
|
222
|
+
clearTimeout(flushTimer);
|
|
223
|
+
flushTimer = undefined;
|
|
224
|
+
}
|
|
225
|
+
if (buffer.length === 0) return;
|
|
226
|
+
const events = buffer;
|
|
227
|
+
buffer = [];
|
|
201
228
|
ctx.sendMessage({
|
|
202
|
-
type: '
|
|
229
|
+
type: 'traceBatch',
|
|
203
230
|
runId: run.runId,
|
|
204
231
|
graphId,
|
|
232
|
+
events
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
run.flushTracing = flush;
|
|
236
|
+
|
|
237
|
+
const push = (event: TraceBatchEvent) => {
|
|
238
|
+
buffer.push(event);
|
|
239
|
+
if (buffer.length >= TRACE_FLUSH_MAX_EVENTS) {
|
|
240
|
+
flush();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (flushTimer === undefined) {
|
|
244
|
+
flushTimer = setTimeout(flush, TRACE_FLUSH_INTERVAL_MS);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
run.engine.onNodeExecutionStart.addListener((node) => {
|
|
249
|
+
run.performance.nodesExecuted++;
|
|
250
|
+
push({
|
|
205
251
|
nodeId: node.id,
|
|
206
252
|
event: 'start',
|
|
207
253
|
data: { typeName: node.description.typeName },
|
|
@@ -210,10 +256,7 @@ export function setupTracing(
|
|
|
210
256
|
});
|
|
211
257
|
|
|
212
258
|
run.engine.onNodeExecutionEnd.addListener((node) => {
|
|
213
|
-
|
|
214
|
-
type: 'trace',
|
|
215
|
-
runId: run.runId,
|
|
216
|
-
graphId,
|
|
259
|
+
push({
|
|
217
260
|
nodeId: node.id,
|
|
218
261
|
event: 'end',
|
|
219
262
|
data: { typeName: node.description.typeName },
|
|
@@ -262,91 +305,228 @@ export function setupVariableChangeTracking(
|
|
|
262
305
|
/**
|
|
263
306
|
* Execute a graph through its lifecycle phases
|
|
264
307
|
*/
|
|
265
|
-
|
|
308
|
+
/**
|
|
309
|
+
* The single graph-execution lifecycle shared by both runners (the in-browser
|
|
310
|
+
* local transport and the web-worker runner). It drives the start → tick → end →
|
|
311
|
+
* completed phase machine and emits the `completed` / error messages; runner-
|
|
312
|
+
* specific behaviour (how fibers are stepped, tick timing, and what to do on
|
|
313
|
+
* completion/error) is injected via {@link ExecuteGraphLifecycleOptions} hooks so
|
|
314
|
+
* neither runner keeps its own copy of this logic.
|
|
315
|
+
*/
|
|
316
|
+
export interface ExecuteGraphLifecycleOptions {
|
|
317
|
+
/** Tick timing when no {@link tickStrategy} is given. Defaults to 50ms. */
|
|
318
|
+
tickInterval?: number;
|
|
319
|
+
/**
|
|
320
|
+
* When true, the run finalizes (end phase, `completed` message, engine
|
|
321
|
+
* dispose) once its flows drain. Defaults to false: the run idles in the
|
|
322
|
+
* tick phase, keeping event-node subscriptions live and draining any fibers
|
|
323
|
+
* they commit, until it is explicitly stopped.
|
|
324
|
+
*/
|
|
325
|
+
autoEnd?: boolean;
|
|
326
|
+
/**
|
|
327
|
+
* Run the engine's pending fibers for the current phase. Defaults to
|
|
328
|
+
* `run.engine.executeAllAsync()`; the local runner injects a pause-aware
|
|
329
|
+
* executor that also honours its step-delay / speed settings.
|
|
330
|
+
*/
|
|
331
|
+
executeStep?: () => Promise<void>;
|
|
332
|
+
/** Timing between tick iterations. Defaults to `sleep(tickInterval)`. */
|
|
333
|
+
tickStrategy?: () => Promise<void>;
|
|
334
|
+
/** Invoked after each tick iteration. */
|
|
335
|
+
onStepComplete?: () => Promise<void>;
|
|
336
|
+
/**
|
|
337
|
+
* Invoked after a natural completion — the run is marked completed, the
|
|
338
|
+
* `completed` message has been sent, and the engine disposed. Lets a runner
|
|
339
|
+
* run session hooks and sync its own state (e.g. the local panel's status).
|
|
340
|
+
*/
|
|
341
|
+
onComplete?: () => void | Promise<void>;
|
|
342
|
+
/**
|
|
343
|
+
* Invoked on error (after the run is marked errored, before the engine is
|
|
344
|
+
* disposed and the error rethrown). Replaces the default `sendError`.
|
|
345
|
+
*/
|
|
346
|
+
onError?: (error: Error) => void | Promise<void>;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** True when a lifecycle event has at least one listener attached. */
|
|
350
|
+
function hasListeners(event?: { listenerCount: number }): boolean {
|
|
351
|
+
return !!event && event.listenerCount > 0;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Run the `start` lifecycle phase: emit the start event (if anyone is
|
|
356
|
+
* listening) and drain the fibers it commits, then advance to the tick phase.
|
|
357
|
+
*/
|
|
358
|
+
async function runStartPhase(
|
|
266
359
|
run: ActiveRun,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
options?: {
|
|
270
|
-
tickInterval?: number;
|
|
271
|
-
onStepComplete?: () => Promise<void>;
|
|
272
|
-
autoEnd?: boolean;
|
|
273
|
-
}
|
|
360
|
+
eventEmitter: ILifecycleEventEmitter | undefined,
|
|
361
|
+
executeStep: () => Promise<unknown>
|
|
274
362
|
): Promise<void> {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
363
|
+
if (run.executionPhase !== 'start') return;
|
|
364
|
+
if (hasListeners(eventEmitter?.startEvent)) {
|
|
365
|
+
eventEmitter!.startEvent.emit();
|
|
366
|
+
await executeStep();
|
|
367
|
+
}
|
|
368
|
+
run.executionPhase = 'tick';
|
|
369
|
+
}
|
|
279
370
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Run the `tick` phase until the run is paused or stopped. Returns `true` when
|
|
373
|
+
* the loop yielded control mid-tick (paused/stopped) so the caller should bail
|
|
374
|
+
* out without finalizing.
|
|
375
|
+
*
|
|
376
|
+
* A run stays alive here even with no tick listeners: completing it would
|
|
377
|
+
* dispose the engine and tear down event-node subscriptions (ai/onToolCall,
|
|
378
|
+
* ai/onMessage, custom triggers) that fire out-of-band, after the start flow has
|
|
379
|
+
* drained. The loop keeps draining fibers those events commit. Pass
|
|
380
|
+
* `autoEnd: true` to restore the finalize-when-drained behaviour for
|
|
381
|
+
* fire-and-forget runs.
|
|
382
|
+
*/
|
|
383
|
+
async function runTickPhase(
|
|
384
|
+
run: ActiveRun,
|
|
385
|
+
eventEmitter: ILifecycleEventEmitter | undefined,
|
|
386
|
+
executeStep: () => Promise<unknown>,
|
|
387
|
+
tickStrategy: () => Promise<unknown>,
|
|
388
|
+
options?: ExecuteGraphLifecycleOptions
|
|
389
|
+
): Promise<boolean> {
|
|
390
|
+
if (run.executionPhase !== 'tick') return false;
|
|
391
|
+
|
|
392
|
+
const hasTickListeners = hasListeners(eventEmitter?.tickEvent);
|
|
393
|
+
const autoEnd = options?.autoEnd ?? false;
|
|
394
|
+
if (!hasTickListeners && autoEnd) {
|
|
395
|
+
run.executionPhase = 'end';
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
while (!run.isPaused && run.status === 'running') {
|
|
400
|
+
if (hasTickListeners) {
|
|
401
|
+
eventEmitter!.tickEvent.emit();
|
|
402
|
+
run.currentTick++;
|
|
290
403
|
}
|
|
404
|
+
await executeStep();
|
|
291
405
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (eventEmitter?.tickEvent && eventEmitter.tickEvent.listenerCount > 0) {
|
|
295
|
-
while (!run.isPaused && run.status === 'running') {
|
|
296
|
-
eventEmitter.tickEvent.emit();
|
|
297
|
-
await run.engine.executeAllAsync();
|
|
298
|
-
run.currentTick++;
|
|
299
|
-
|
|
300
|
-
if (options?.onStepComplete) {
|
|
301
|
-
await options.onStepComplete();
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (run.isPaused || run.status !== 'running') {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
await sleep((options?.tickInterval ?? 50) / 1000);
|
|
309
|
-
}
|
|
310
|
-
} else {
|
|
311
|
-
run.executionPhase = 'end';
|
|
312
|
-
}
|
|
406
|
+
if (options?.onStepComplete) {
|
|
407
|
+
await options.onStepComplete();
|
|
313
408
|
}
|
|
314
409
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (eventEmitter?.endEvent && eventEmitter.endEvent.listenerCount > 0) {
|
|
318
|
-
eventEmitter.endEvent.emit();
|
|
319
|
-
await run.engine.executeAllAsync();
|
|
320
|
-
}
|
|
321
|
-
run.executionPhase = 'completed';
|
|
410
|
+
if (run.isPaused || run.status !== 'running') {
|
|
411
|
+
return true;
|
|
322
412
|
}
|
|
323
413
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
414
|
+
await tickStrategy();
|
|
415
|
+
}
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
328
418
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
419
|
+
/**
|
|
420
|
+
* Run the `end` lifecycle phase: emit the end event (if listened) and drain,
|
|
421
|
+
* then mark the run as reaching the `completed` phase.
|
|
422
|
+
*/
|
|
423
|
+
async function runEndPhase(
|
|
424
|
+
run: ActiveRun,
|
|
425
|
+
eventEmitter: ILifecycleEventEmitter | undefined,
|
|
426
|
+
executeStep: () => Promise<unknown>
|
|
427
|
+
): Promise<void> {
|
|
428
|
+
if (run.executionPhase !== 'end' || run.isPaused) return;
|
|
429
|
+
if (hasListeners(eventEmitter?.endEvent)) {
|
|
430
|
+
eventEmitter!.endEvent.emit();
|
|
431
|
+
await executeStep();
|
|
432
|
+
}
|
|
433
|
+
run.executionPhase = 'completed';
|
|
434
|
+
}
|
|
338
435
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
436
|
+
/**
|
|
437
|
+
* Finalize a run that ran out of fibers and isn't paused. Only autoEnd runs
|
|
438
|
+
* advance this far; by default a run idles in the tick phase until stopped.
|
|
439
|
+
*/
|
|
440
|
+
async function finalizeCompletedRun(
|
|
441
|
+
run: ActiveRun,
|
|
442
|
+
graphId: string,
|
|
443
|
+
ctx: MessageContext,
|
|
444
|
+
options?: ExecuteGraphLifecycleOptions
|
|
445
|
+
): Promise<void> {
|
|
446
|
+
if (
|
|
447
|
+
run.executionPhase !== 'completed' ||
|
|
448
|
+
run.isPaused ||
|
|
449
|
+
!(options?.autoEnd ?? false)
|
|
450
|
+
) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
run.status = 'completed';
|
|
455
|
+
const elapsedMs = Date.now() - run.startedAt;
|
|
456
|
+
|
|
457
|
+
// Deliver any buffered trace events before `completed` , the client
|
|
458
|
+
// unregisters the run id on completion and would drop a late batch.
|
|
459
|
+
run.flushTracing?.();
|
|
460
|
+
|
|
461
|
+
ctx.sendMessage({
|
|
462
|
+
type: 'completed',
|
|
463
|
+
runId: run.runId,
|
|
464
|
+
graphId,
|
|
465
|
+
completedAt: Date.now(),
|
|
466
|
+
elapsedMs,
|
|
467
|
+
result: null,
|
|
468
|
+
performance: run.performance
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
run.engine.dispose();
|
|
472
|
+
await options?.onComplete?.();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** Mark the run errored, notify, dispose the engine, and rethrow. */
|
|
476
|
+
async function handleLifecycleError(
|
|
477
|
+
run: ActiveRun,
|
|
478
|
+
graphId: string,
|
|
479
|
+
ctx: MessageContext,
|
|
480
|
+
error: unknown,
|
|
481
|
+
options?: ExecuteGraphLifecycleOptions
|
|
482
|
+
): Promise<never> {
|
|
483
|
+
run.status = 'error';
|
|
484
|
+
run.flushTracing?.();
|
|
485
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
486
|
+
if (options?.onError) {
|
|
487
|
+
await options.onError(err);
|
|
488
|
+
} else {
|
|
489
|
+
ctx.sendError('NODE_EXECUTION_ERROR', err.message, {
|
|
345
490
|
runId: run.runId,
|
|
346
491
|
graphId
|
|
347
492
|
});
|
|
348
|
-
|
|
349
|
-
|
|
493
|
+
}
|
|
494
|
+
run.engine.dispose();
|
|
495
|
+
throw error;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export async function executeGraphLifecycle(
|
|
499
|
+
run: ActiveRun,
|
|
500
|
+
graphId: string,
|
|
501
|
+
ctx: MessageContext,
|
|
502
|
+
options?: ExecuteGraphLifecycleOptions
|
|
503
|
+
): Promise<void> {
|
|
504
|
+
const executeStep =
|
|
505
|
+
options?.executeStep ?? (() => run.engine.executeAllAsync());
|
|
506
|
+
const tickStrategy =
|
|
507
|
+
options?.tickStrategy ??
|
|
508
|
+
(() => sleep((options?.tickInterval ?? 50) / 1000));
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
const eventEmitter = run.registry.dependencies?.ILifecycleEventEmitter as
|
|
512
|
+
| ILifecycleEventEmitter
|
|
513
|
+
| undefined;
|
|
514
|
+
|
|
515
|
+
await runStartPhase(run, eventEmitter, executeStep);
|
|
516
|
+
|
|
517
|
+
const yielded = await runTickPhase(
|
|
518
|
+
run,
|
|
519
|
+
eventEmitter,
|
|
520
|
+
executeStep,
|
|
521
|
+
tickStrategy,
|
|
522
|
+
options
|
|
523
|
+
);
|
|
524
|
+
if (yielded) return;
|
|
525
|
+
|
|
526
|
+
await runEndPhase(run, eventEmitter, executeStep);
|
|
527
|
+
await finalizeCompletedRun(run, graphId, ctx, options);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
await handleLifecycleError(run, graphId, ctx, error, options);
|
|
350
530
|
}
|
|
351
531
|
}
|
|
352
532
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { System } from '../../system/system.js';
|
|
7
7
|
import { plugin } from '../../system/plugin.js';
|
|
8
8
|
import type { IRegistry } from '@kiberon-labs/behave-graph';
|
|
9
|
+
import { buildUIGraphJSON } from '../../transformers/Uigraph.js';
|
|
9
10
|
import { GraphRunnerClient } from '../graphrunner/client.js';
|
|
10
11
|
import { LocalTransport } from './transport.js';
|
|
11
12
|
import {
|
|
@@ -86,11 +87,16 @@ export async function localGraphRunnerPluginLoader(
|
|
|
86
87
|
tickStrategy
|
|
87
88
|
});
|
|
88
89
|
|
|
89
|
-
// Create local transport with access to the node registry and store
|
|
90
|
+
// Create local transport with access to the node registry and store.
|
|
91
|
+
// `resolveGraph` lets Call Subgraph nodes run other open graphs by id.
|
|
90
92
|
const transport = new LocalTransport(options.registry, {
|
|
91
93
|
...options,
|
|
92
94
|
store: localStore,
|
|
93
|
-
sessionFactory
|
|
95
|
+
sessionFactory,
|
|
96
|
+
resolveGraph: (id) => {
|
|
97
|
+
const target = system.activeGraph.getState().sessions[id];
|
|
98
|
+
return target ? buildUIGraphJSON(target).flow : undefined;
|
|
99
|
+
}
|
|
94
100
|
});
|
|
95
101
|
|
|
96
102
|
// Create client with the local transport and message activity tracking
|