@q1k-oss/behaviour-tree-workflows 0.0.1

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/dist/index.cjs ADDED
@@ -0,0 +1,5011 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ActionNode: () => ActionNode,
34
+ AlwaysCondition: () => AlwaysCondition,
35
+ BaseNode: () => BaseNode,
36
+ BehaviorTree: () => BehaviorTree,
37
+ CheckCondition: () => CheckCondition,
38
+ CodeExecution: () => CodeExecution,
39
+ CompositeNode: () => CompositeNode,
40
+ ConditionNode: () => ConditionNode,
41
+ Conditional: () => Conditional,
42
+ ConfigValidationError: () => ConfigValidationError,
43
+ ConfigurationError: () => ConfigurationError,
44
+ CounterAction: () => CounterAction,
45
+ DecoratorNode: () => DecoratorNode,
46
+ Delay: () => Delay,
47
+ ExecutionTracker: () => ExecutionTracker,
48
+ FailureNode: () => FailureNode,
49
+ Fallback: () => Fallback,
50
+ ForEach: () => ForEach,
51
+ ForceFailure: () => ForceFailure,
52
+ ForceSuccess: () => ForceSuccess,
53
+ GenerateFile: () => GenerateFile,
54
+ HttpRequest: () => HttpRequest,
55
+ IntegrationAction: () => IntegrationAction,
56
+ Invert: () => Invert,
57
+ KeepRunningUntilFailure: () => KeepRunningUntilFailure,
58
+ LogMessage: () => LogMessage,
59
+ MemoryDataStore: () => MemoryDataStore,
60
+ MemorySequence: () => MemorySequence,
61
+ MockAction: () => MockAction,
62
+ NodeEventEmitter: () => NodeEventEmitter,
63
+ NodeEventType: () => NodeEventType,
64
+ NodeStatus: () => NodeStatus,
65
+ Parallel: () => Parallel,
66
+ ParseFile: () => ParseFile,
67
+ Precondition: () => Precondition,
68
+ PrintAction: () => PrintAction,
69
+ PythonScript: () => PythonScript,
70
+ ReactiveSequence: () => ReactiveSequence,
71
+ Recovery: () => Recovery,
72
+ RegexExtract: () => RegexExtract,
73
+ Registry: () => Registry,
74
+ Repeat: () => Repeat,
75
+ ResumePoint: () => ResumePoint,
76
+ RunOnce: () => RunOnce,
77
+ RunningNode: () => RunningNode,
78
+ SchemaRegistry: () => SchemaRegistry,
79
+ ScopedBlackboard: () => ScopedBlackboard,
80
+ Selector: () => Selector,
81
+ SemanticValidationError: () => SemanticValidationError,
82
+ Sequence: () => Sequence,
83
+ SequenceWithMemory: () => SequenceWithMemory,
84
+ SoftAssert: () => SoftAssert,
85
+ StructureValidationError: () => StructureValidationError,
86
+ SubTree: () => SubTree,
87
+ SuccessNode: () => SuccessNode,
88
+ Timeout: () => Timeout,
89
+ ValidationError: () => ValidationError,
90
+ ValidationErrors: () => ValidationErrors,
91
+ WaitAction: () => WaitAction,
92
+ While: () => While,
93
+ YamlSyntaxError: () => YamlSyntaxError,
94
+ clearPieceCache: () => clearPieceCache,
95
+ createNodeSchema: () => createNodeSchema,
96
+ createObservabilitySinkHandler: () => createObservabilitySinkHandler,
97
+ envTokenProvider: () => envTokenProvider,
98
+ executePieceAction: () => executePieceAction,
99
+ extractVariables: () => extractVariables,
100
+ getTemplateIds: () => getTemplateIds,
101
+ hasTemplate: () => hasTemplate,
102
+ hasVariables: () => hasVariables,
103
+ isDataRef: () => isDataRef,
104
+ isPieceInstalled: () => isPieceInstalled,
105
+ listPieceActions: () => listPieceActions,
106
+ loadTemplate: () => loadTemplate,
107
+ loadTemplatesFromDirectory: () => loadTemplatesFromDirectory,
108
+ loadTreeFromFile: () => loadTreeFromFile,
109
+ loadTreeFromYaml: () => loadTreeFromYaml,
110
+ nodeConfigurationSchema: () => nodeConfigurationSchema,
111
+ parseYaml: () => parseYaml,
112
+ registerStandardNodes: () => registerStandardNodes,
113
+ resolveString: () => resolveString,
114
+ resolveValue: () => resolveValue,
115
+ safeValidateConfiguration: () => safeValidateConfiguration,
116
+ schemaRegistry: () => schemaRegistry,
117
+ semanticValidator: () => semanticValidator,
118
+ toYaml: () => toYaml,
119
+ treeDefinitionSchema: () => treeDefinitionSchema,
120
+ validateChildCount: () => validateChildCount,
121
+ validateChildCountRange: () => validateChildCountRange,
122
+ validateCompositeChildren: () => validateCompositeChildren,
123
+ validateConfiguration: () => validateConfiguration,
124
+ validateDecoratorChildren: () => validateDecoratorChildren,
125
+ validateTreeDefinition: () => validateTreeDefinition,
126
+ validateYaml: () => validateYaml,
127
+ validations: () => validations,
128
+ zodErrorToConfigurationError: () => zodErrorToConfigurationError
129
+ });
130
+ module.exports = __toCommonJS(index_exports);
131
+
132
+ // src/events.ts
133
+ var NodeEventType = /* @__PURE__ */ ((NodeEventType2) => {
134
+ NodeEventType2["TICK_START"] = "tick_start";
135
+ NodeEventType2["TICK_END"] = "tick_end";
136
+ NodeEventType2["STATUS_CHANGE"] = "status_change";
137
+ NodeEventType2["ERROR"] = "error";
138
+ NodeEventType2["HALT"] = "halt";
139
+ NodeEventType2["RESET"] = "reset";
140
+ NodeEventType2["LOG"] = "log";
141
+ return NodeEventType2;
142
+ })(NodeEventType || {});
143
+ var NodeEventEmitter = class {
144
+ listeners = /* @__PURE__ */ new Map();
145
+ allListeners = /* @__PURE__ */ new Set();
146
+ /**
147
+ * Subscribe to a specific event type
148
+ * @param type - The event type to listen for
149
+ * @param callback - Function to call when event occurs
150
+ */
151
+ on(type, callback) {
152
+ if (!this.listeners.has(type)) {
153
+ this.listeners.set(type, /* @__PURE__ */ new Set());
154
+ }
155
+ this.listeners.get(type)?.add(callback);
156
+ }
157
+ /**
158
+ * Subscribe to all event types
159
+ * @param callback - Function to call for any event
160
+ */
161
+ onAll(callback) {
162
+ this.allListeners.add(callback);
163
+ }
164
+ /**
165
+ * Unsubscribe from a specific event type
166
+ * @param type - The event type to stop listening for
167
+ * @param callback - The callback to remove
168
+ */
169
+ off(type, callback) {
170
+ this.listeners.get(type)?.delete(callback);
171
+ }
172
+ /**
173
+ * Unsubscribe from all events
174
+ * @param callback - The callback to remove
175
+ */
176
+ offAll(callback) {
177
+ this.allListeners.delete(callback);
178
+ }
179
+ /**
180
+ * Emit an event to all registered listeners
181
+ * Errors in callbacks are caught and logged to prevent breaking execution
182
+ * @param event - The event to emit
183
+ */
184
+ emit(event) {
185
+ const typeListeners = this.listeners.get(event.type);
186
+ if (typeListeners) {
187
+ for (const callback of typeListeners) {
188
+ try {
189
+ callback(event);
190
+ } catch (error) {
191
+ console.error(`Error in event callback for ${event.type}:`, error);
192
+ }
193
+ }
194
+ }
195
+ for (const callback of this.allListeners) {
196
+ try {
197
+ callback(event);
198
+ } catch (error) {
199
+ console.error("Error in event callback (onAll):", error);
200
+ }
201
+ }
202
+ }
203
+ /**
204
+ * Remove all listeners
205
+ */
206
+ clear() {
207
+ this.listeners.clear();
208
+ this.allListeners.clear();
209
+ }
210
+ /**
211
+ * Get count of listeners for a specific type
212
+ * @param type - The event type to check
213
+ * @returns Number of listeners
214
+ */
215
+ listenerCount(type) {
216
+ return this.listeners.get(type)?.size || 0;
217
+ }
218
+ /**
219
+ * Get count of "all events" listeners
220
+ * @returns Number of listeners
221
+ */
222
+ allListenerCount() {
223
+ return this.allListeners.size;
224
+ }
225
+ /**
226
+ * Check if there are any listeners
227
+ * @returns True if any listeners are registered
228
+ */
229
+ hasListeners() {
230
+ return this.listeners.size > 0 || this.allListeners.size > 0;
231
+ }
232
+ /**
233
+ * Returns an Effect Stream of all events
234
+ * REMOVED: Effect-TS integration removed in Phase 1
235
+ * For streaming events in Temporal workflows, use native event emitter pattern
236
+ */
237
+ // toStream(): Stream.Stream<NodeEvent<unknown>> {
238
+ // return Stream.asyncPush<NodeEvent<unknown>>((emit) =>
239
+ // Effect.acquireRelease(
240
+ // Effect.sync(() => {
241
+ // const callback = (event: NodeEvent<unknown>) => {
242
+ // emit.single(event);
243
+ // };
244
+ // this.onAll(callback);
245
+ // return callback;
246
+ // }),
247
+ // (callback) => Effect.sync(() => this.offAll(callback)),
248
+ // ),
249
+ // );
250
+ // }
251
+ /**
252
+ * Returns an AsyncIterable of all events
253
+ * REMOVED: Effect-TS dependency removed in Phase 1
254
+ * Implement using native async generators if needed
255
+ */
256
+ // toAsyncIterable(): AsyncIterable<NodeEvent<unknown>> {
257
+ // return Stream.toAsyncIterable(this.toStream());
258
+ // }
259
+ };
260
+
261
+ // src/types.ts
262
+ var NodeStatus = /* @__PURE__ */ ((NodeStatus2) => {
263
+ NodeStatus2["SUCCESS"] = "SUCCESS";
264
+ NodeStatus2["FAILURE"] = "FAILURE";
265
+ NodeStatus2["RUNNING"] = "RUNNING";
266
+ NodeStatus2["IDLE"] = "IDLE";
267
+ return NodeStatus2;
268
+ })(NodeStatus || {});
269
+
270
+ // src/errors.ts
271
+ var ConfigurationError = class _ConfigurationError extends Error {
272
+ constructor(message, hint) {
273
+ super(message);
274
+ this.hint = hint;
275
+ this.name = "ConfigurationError";
276
+ if (Error.captureStackTrace) {
277
+ Error.captureStackTrace(this, _ConfigurationError);
278
+ }
279
+ }
280
+ isConfigurationError = true;
281
+ };
282
+
283
+ // src/utils/signal-check.ts
284
+ var OperationCancelledError = class _OperationCancelledError extends Error {
285
+ constructor(message = "Operation was cancelled") {
286
+ super(message);
287
+ this.name = "OperationCancelledError";
288
+ if (Error.captureStackTrace) {
289
+ Error.captureStackTrace(this, _OperationCancelledError);
290
+ }
291
+ }
292
+ };
293
+ function checkSignal(signal, message) {
294
+ if (signal?.aborted) {
295
+ throw new OperationCancelledError(message);
296
+ }
297
+ }
298
+
299
+ // src/utils/error-handler.ts
300
+ function handleNodeError(error) {
301
+ if (error instanceof ConfigurationError) {
302
+ throw error;
303
+ }
304
+ if (error instanceof OperationCancelledError) {
305
+ throw error;
306
+ }
307
+ return "FAILURE" /* FAILURE */;
308
+ }
309
+
310
+ // src/base-node.ts
311
+ var BaseNode = class {
312
+ id;
313
+ name;
314
+ type;
315
+ _status = "IDLE" /* IDLE */;
316
+ _lastError;
317
+ config;
318
+ _eventEmitter;
319
+ parent;
320
+ children;
321
+ constructor(config) {
322
+ this.id = config.id;
323
+ this.name = config.name || config.id;
324
+ this.type = this.constructor.name;
325
+ this.config = config;
326
+ }
327
+ halt() {
328
+ console.log(`[${this.type}:${this.name}] Halting...`);
329
+ this._eventEmitter?.emit({
330
+ type: "halt" /* HALT */,
331
+ nodeId: this.id,
332
+ nodeName: this.name,
333
+ nodeType: this.type,
334
+ timestamp: Date.now()
335
+ });
336
+ if (this._status === "RUNNING" /* RUNNING */) {
337
+ this.onHalt();
338
+ this._status = "IDLE" /* IDLE */;
339
+ }
340
+ }
341
+ reset() {
342
+ console.log(`[${this.type}:${this.name}] Resetting...`);
343
+ this._eventEmitter?.emit({
344
+ type: "reset" /* RESET */,
345
+ nodeId: this.id,
346
+ nodeName: this.name,
347
+ nodeType: this.type,
348
+ timestamp: Date.now()
349
+ });
350
+ this._status = "IDLE" /* IDLE */;
351
+ this._lastError = void 0;
352
+ this.onReset();
353
+ }
354
+ status() {
355
+ return this._status;
356
+ }
357
+ get lastError() {
358
+ return this._lastError;
359
+ }
360
+ providedPorts() {
361
+ return [];
362
+ }
363
+ /**
364
+ * Hook for derived classes to implement custom halt logic
365
+ */
366
+ onHalt() {
367
+ }
368
+ /**
369
+ * Hook for derived classes to implement custom reset logic
370
+ */
371
+ onReset() {
372
+ }
373
+ /**
374
+ * Helper to get input value from blackboard
375
+ */
376
+ getInput(context, key, defaultValue) {
377
+ const fullKey = this.config[key] || key;
378
+ return context.blackboard.getPort(fullKey, defaultValue);
379
+ }
380
+ /**
381
+ * Helper to set output value to blackboard
382
+ */
383
+ setOutput(context, key, value) {
384
+ const fullKey = this.config[key] || key;
385
+ context.blackboard.setPort(fullKey, value);
386
+ }
387
+ /**
388
+ * Log helper for debugging
389
+ */
390
+ log(message, ...args) {
391
+ console.log(`[${this.type}:${this.name}] ${message}`, ...args);
392
+ }
393
+ };
394
+ var ActionNode = class extends BaseNode {
395
+ /**
396
+ * Clone this action node
397
+ * Leaf nodes have no children to clone
398
+ */
399
+ clone() {
400
+ const ClonedClass = this.constructor;
401
+ return new ClonedClass({ ...this.config });
402
+ }
403
+ /**
404
+ * Tick with resumable execution support for leaf nodes
405
+ * Uses async/await for Promise-based async/RUNNING semantics
406
+ * All errors are caught and converted to NodeStatus.FAILURE
407
+ */
408
+ async tick(context) {
409
+ try {
410
+ this._eventEmitter = context.eventEmitter;
411
+ context.eventEmitter?.emit({
412
+ type: "tick_start" /* TICK_START */,
413
+ nodeId: this.id,
414
+ nodeName: this.name,
415
+ nodeType: this.type,
416
+ timestamp: Date.now()
417
+ });
418
+ const status = await this.executeTick(context);
419
+ this._status = status;
420
+ context.eventEmitter?.emit({
421
+ type: "tick_end" /* TICK_END */,
422
+ nodeId: this.id,
423
+ nodeName: this.name,
424
+ nodeType: this.type,
425
+ timestamp: Date.now(),
426
+ data: { status }
427
+ });
428
+ return status;
429
+ } catch (error) {
430
+ const errorMessage = error instanceof Error ? error.message : String(error);
431
+ const errorStack = error instanceof Error ? error.stack : void 0;
432
+ this._lastError = errorMessage;
433
+ this._status = "FAILURE" /* FAILURE */;
434
+ context.eventEmitter?.emit({
435
+ type: "error" /* ERROR */,
436
+ nodeId: this.id,
437
+ nodeName: this.name,
438
+ nodeType: this.type,
439
+ timestamp: Date.now(),
440
+ data: {
441
+ error: {
442
+ message: errorMessage,
443
+ stack: errorStack
444
+ },
445
+ blackboard: context.blackboard?.toJSON?.() ?? {}
446
+ }
447
+ });
448
+ context.eventEmitter?.emit({
449
+ type: "tick_end" /* TICK_END */,
450
+ nodeId: this.id,
451
+ nodeName: this.name,
452
+ nodeType: this.type,
453
+ timestamp: Date.now(),
454
+ data: { status: "FAILURE" /* FAILURE */ }
455
+ });
456
+ return handleNodeError(error);
457
+ }
458
+ }
459
+ };
460
+ var ConditionNode = class extends BaseNode {
461
+ /**
462
+ * Clone this condition node
463
+ * Leaf nodes have no children to clone
464
+ */
465
+ clone() {
466
+ const ClonedClass = this.constructor;
467
+ return new ClonedClass({ ...this.config });
468
+ }
469
+ /**
470
+ * Tick with resumable execution support for leaf nodes
471
+ * Uses async/await for Promise-based async/RUNNING semantics
472
+ * All errors are caught and converted to NodeStatus.FAILURE
473
+ */
474
+ async tick(context) {
475
+ try {
476
+ this._eventEmitter = context.eventEmitter;
477
+ context.eventEmitter?.emit({
478
+ type: "tick_start" /* TICK_START */,
479
+ nodeId: this.id,
480
+ nodeName: this.name,
481
+ nodeType: this.type,
482
+ timestamp: Date.now()
483
+ });
484
+ const status = await this.executeTick(context);
485
+ this._status = status;
486
+ context.eventEmitter?.emit({
487
+ type: "tick_end" /* TICK_END */,
488
+ nodeId: this.id,
489
+ nodeName: this.name,
490
+ nodeType: this.type,
491
+ timestamp: Date.now(),
492
+ data: { status }
493
+ });
494
+ return status;
495
+ } catch (error) {
496
+ const errorMessage = error instanceof Error ? error.message : String(error);
497
+ const errorStack = error instanceof Error ? error.stack : void 0;
498
+ this._lastError = errorMessage;
499
+ this._status = "FAILURE" /* FAILURE */;
500
+ context.eventEmitter?.emit({
501
+ type: "error" /* ERROR */,
502
+ nodeId: this.id,
503
+ nodeName: this.name,
504
+ nodeType: this.type,
505
+ timestamp: Date.now(),
506
+ data: {
507
+ error: {
508
+ message: errorMessage,
509
+ stack: errorStack
510
+ },
511
+ blackboard: context.blackboard?.toJSON?.() ?? {}
512
+ }
513
+ });
514
+ context.eventEmitter?.emit({
515
+ type: "tick_end" /* TICK_END */,
516
+ nodeId: this.id,
517
+ nodeName: this.name,
518
+ nodeType: this.type,
519
+ timestamp: Date.now(),
520
+ data: { status: "FAILURE" /* FAILURE */ }
521
+ });
522
+ return handleNodeError(error);
523
+ }
524
+ }
525
+ };
526
+ var DecoratorNode = class extends BaseNode {
527
+ child;
528
+ /**
529
+ * Clone this decorator node including its child
530
+ */
531
+ clone() {
532
+ const ClonedClass = this.constructor;
533
+ const cloned = new ClonedClass({ ...this.config });
534
+ if (this.child) {
535
+ cloned.setChild(this.child.clone());
536
+ }
537
+ return cloned;
538
+ }
539
+ /**
540
+ * Tick with resumable execution support - decorators can be resume points
541
+ * Uses async/await for Promise-based async/RUNNING semantics
542
+ * All errors are caught and converted to NodeStatus.FAILURE
543
+ */
544
+ async tick(context) {
545
+ try {
546
+ context.eventEmitter?.emit({
547
+ type: "tick_start" /* TICK_START */,
548
+ nodeId: this.id,
549
+ nodeName: this.name,
550
+ nodeType: this.type,
551
+ timestamp: Date.now()
552
+ });
553
+ const status = await this.executeTick(context);
554
+ context.eventEmitter?.emit({
555
+ type: "tick_end" /* TICK_END */,
556
+ nodeId: this.id,
557
+ nodeName: this.name,
558
+ nodeType: this.type,
559
+ timestamp: Date.now(),
560
+ data: { status }
561
+ });
562
+ return status;
563
+ } catch (error) {
564
+ const errorMessage = error instanceof Error ? error.message : String(error);
565
+ const errorStack = error instanceof Error ? error.stack : void 0;
566
+ this._lastError = errorMessage;
567
+ this._status = "FAILURE" /* FAILURE */;
568
+ context.eventEmitter?.emit({
569
+ type: "error" /* ERROR */,
570
+ nodeId: this.id,
571
+ nodeName: this.name,
572
+ nodeType: this.type,
573
+ timestamp: Date.now(),
574
+ data: {
575
+ error: {
576
+ message: errorMessage,
577
+ stack: errorStack
578
+ },
579
+ blackboard: context.blackboard?.toJSON?.() ?? {}
580
+ }
581
+ });
582
+ context.eventEmitter?.emit({
583
+ type: "tick_end" /* TICK_END */,
584
+ nodeId: this.id,
585
+ nodeName: this.name,
586
+ nodeType: this.type,
587
+ timestamp: Date.now(),
588
+ data: { status: "FAILURE" /* FAILURE */ }
589
+ });
590
+ return handleNodeError(error);
591
+ }
592
+ }
593
+ setChild(child) {
594
+ if (!child) {
595
+ throw new Error("Cannot set undefined child on decorator node");
596
+ }
597
+ this.child = child;
598
+ this.children = [child];
599
+ child.parent = this;
600
+ }
601
+ halt() {
602
+ super.halt();
603
+ if (this.child && this.child.status() === "RUNNING" /* RUNNING */) {
604
+ this.child.halt();
605
+ }
606
+ }
607
+ reset() {
608
+ super.reset();
609
+ if (this.child) {
610
+ this.child.reset();
611
+ }
612
+ }
613
+ };
614
+ var CompositeNode = class extends BaseNode {
615
+ _children = [];
616
+ /**
617
+ * Clone this composite node including all children
618
+ */
619
+ clone() {
620
+ const ClonedClass = this.constructor;
621
+ const cloned = new ClonedClass({ ...this.config });
622
+ const clonedChildren = this._children.map((child) => child.clone());
623
+ cloned.addChildren(clonedChildren);
624
+ return cloned;
625
+ }
626
+ /**
627
+ * Tick with resumable execution support - composites can be resume points
628
+ * Uses async/await for Promise-based async/RUNNING semantics
629
+ * All errors are caught and converted to NodeStatus.FAILURE
630
+ */
631
+ async tick(context) {
632
+ try {
633
+ context.eventEmitter?.emit({
634
+ type: "tick_start" /* TICK_START */,
635
+ nodeId: this.id,
636
+ nodeName: this.name,
637
+ nodeType: this.type,
638
+ timestamp: Date.now()
639
+ });
640
+ const status = await this.executeTick(context);
641
+ context.eventEmitter?.emit({
642
+ type: "tick_end" /* TICK_END */,
643
+ nodeId: this.id,
644
+ nodeName: this.name,
645
+ nodeType: this.type,
646
+ timestamp: Date.now(),
647
+ data: { status }
648
+ });
649
+ return status;
650
+ } catch (error) {
651
+ const errorMessage = error instanceof Error ? error.message : String(error);
652
+ const errorStack = error instanceof Error ? error.stack : void 0;
653
+ this._lastError = errorMessage;
654
+ this._status = "FAILURE" /* FAILURE */;
655
+ context.eventEmitter?.emit({
656
+ type: "error" /* ERROR */,
657
+ nodeId: this.id,
658
+ nodeName: this.name,
659
+ nodeType: this.type,
660
+ timestamp: Date.now(),
661
+ data: {
662
+ error: {
663
+ message: errorMessage,
664
+ stack: errorStack
665
+ },
666
+ blackboard: context.blackboard?.toJSON?.() ?? {}
667
+ }
668
+ });
669
+ context.eventEmitter?.emit({
670
+ type: "tick_end" /* TICK_END */,
671
+ nodeId: this.id,
672
+ nodeName: this.name,
673
+ nodeType: this.type,
674
+ timestamp: Date.now(),
675
+ data: { status: "FAILURE" /* FAILURE */ }
676
+ });
677
+ return handleNodeError(error);
678
+ }
679
+ }
680
+ addChild(child) {
681
+ if (!child) {
682
+ throw new Error("Cannot add undefined child to composite node");
683
+ }
684
+ this._children.push(child);
685
+ this.children = this._children;
686
+ child.parent = this;
687
+ }
688
+ addChildren(children) {
689
+ children.forEach((child) => {
690
+ this.addChild(child);
691
+ });
692
+ }
693
+ halt() {
694
+ super.halt();
695
+ for (const child of this._children) {
696
+ if (child.status() === "RUNNING" /* RUNNING */) {
697
+ child.halt();
698
+ }
699
+ }
700
+ }
701
+ reset() {
702
+ super.reset();
703
+ for (const child of this._children) {
704
+ child.reset();
705
+ }
706
+ }
707
+ haltChildren(startIndex = 0) {
708
+ for (let i = startIndex; i < this._children.length; i++) {
709
+ const child = this._children[i];
710
+ if (child && child.status() === "RUNNING" /* RUNNING */) {
711
+ child.halt();
712
+ }
713
+ }
714
+ }
715
+ };
716
+
717
+ // src/blackboard.ts
718
+ var ScopedBlackboard = class _ScopedBlackboard {
719
+ data = {};
720
+ parent = null;
721
+ scopeName;
722
+ childScopes = /* @__PURE__ */ new Map();
723
+ constructor(scopeName = "root", parent = null) {
724
+ this.scopeName = scopeName;
725
+ this.parent = parent;
726
+ }
727
+ get(key) {
728
+ if (key in this.data) {
729
+ return this.data[key];
730
+ }
731
+ if (this.parent) {
732
+ return this.parent.get(key);
733
+ }
734
+ return void 0;
735
+ }
736
+ set(key, value) {
737
+ this.data[key] = value;
738
+ }
739
+ has(key) {
740
+ if (key in this.data) {
741
+ return true;
742
+ }
743
+ if (this.parent) {
744
+ return this.parent.has(key);
745
+ }
746
+ return false;
747
+ }
748
+ delete(key) {
749
+ delete this.data[key];
750
+ }
751
+ clear() {
752
+ this.data = {};
753
+ this.childScopes.clear();
754
+ }
755
+ createScope(name) {
756
+ if (this.childScopes.has(name)) {
757
+ const scope = this.childScopes.get(name);
758
+ if (!scope) {
759
+ throw new Error(`Scope ${name} not found`);
760
+ }
761
+ return scope;
762
+ }
763
+ const childScope = new _ScopedBlackboard(name, this);
764
+ this.childScopes.set(name, childScope);
765
+ return childScope;
766
+ }
767
+ getParentScope() {
768
+ return this.parent;
769
+ }
770
+ getScopeName() {
771
+ return this.scopeName;
772
+ }
773
+ getPort(key, defaultValue) {
774
+ const value = this.get(key);
775
+ if (value === void 0 && defaultValue !== void 0) {
776
+ return defaultValue;
777
+ }
778
+ return value;
779
+ }
780
+ setPort(key, value) {
781
+ this.set(key, value);
782
+ }
783
+ keys() {
784
+ const localKeys = Object.keys(this.data);
785
+ const parentKeys = this.parent ? this.parent.keys() : [];
786
+ const allKeys = /* @__PURE__ */ new Set([...localKeys, ...parentKeys]);
787
+ return Array.from(allKeys);
788
+ }
789
+ entries() {
790
+ const result = [];
791
+ const processedKeys = /* @__PURE__ */ new Set();
792
+ for (const [key, value] of Object.entries(this.data)) {
793
+ result.push([key, value]);
794
+ processedKeys.add(key);
795
+ }
796
+ if (this.parent) {
797
+ for (const [key, value] of this.parent.entries()) {
798
+ if (!processedKeys.has(key)) {
799
+ result.push([key, value]);
800
+ }
801
+ }
802
+ }
803
+ return result;
804
+ }
805
+ toJSON() {
806
+ return { ...this.data };
807
+ }
808
+ /**
809
+ * Create a deep clone of this blackboard for snapshots
810
+ * Uses structured cloning for deep copy
811
+ */
812
+ clone() {
813
+ const cloned = new _ScopedBlackboard(this.scopeName, this.parent);
814
+ cloned.data = structuredClone(this.data);
815
+ this.childScopes.forEach((childScope, name) => {
816
+ cloned.childScopes.set(name, childScope.clone());
817
+ });
818
+ return cloned;
819
+ }
820
+ /**
821
+ * Get the full scope path (e.g., "root.child.grandchild")
822
+ */
823
+ getFullScopePath() {
824
+ const path2 = [this.scopeName];
825
+ let current = this.parent;
826
+ while (current) {
827
+ path2.unshift(current.scopeName);
828
+ current = current.parent;
829
+ }
830
+ return path2.join(".");
831
+ }
832
+ /**
833
+ * Debug utility to print the blackboard hierarchy
834
+ */
835
+ debug(indent = 0) {
836
+ const prefix = " ".repeat(indent);
837
+ console.log(`${prefix}Scope: ${this.scopeName}`);
838
+ for (const [key, value] of Object.entries(this.data)) {
839
+ console.log(`${prefix} ${key}: ${JSON.stringify(value)}`);
840
+ }
841
+ for (const [_name, childScope] of this.childScopes) {
842
+ childScope.debug(indent + 1);
843
+ }
844
+ }
845
+ };
846
+
847
+ // src/behavior-tree.ts
848
+ var BehaviorTree = class _BehaviorTree {
849
+ root;
850
+ pathIndex = /* @__PURE__ */ new Map();
851
+ idIndex = /* @__PURE__ */ new Map();
852
+ constructor(root) {
853
+ this.root = root;
854
+ this.buildNodeIndex();
855
+ }
856
+ /**
857
+ * Parse a path with tree ID prefix.
858
+ * Format: #TreeID/node/path
859
+ *
860
+ * @param fullPath Path string starting with # followed by tree ID
861
+ * @returns Object with treeId and nodePath
862
+ * @throws Error if path format is invalid
863
+ *
864
+ * Valid examples:
865
+ * - "#SimpleTest/0/1" -> { treeId: "SimpleTest", nodePath: "/0/1" }
866
+ * - "#MyTree/" -> { treeId: "MyTree", nodePath: "/" }
867
+ * - "#OnlyTree" -> { treeId: "OnlyTree", nodePath: "/" }
868
+ *
869
+ * Invalid examples:
870
+ * - "/0/1" - missing #TreeID prefix
871
+ * - "#/0/1" - empty tree ID
872
+ * - "#" - empty tree ID
873
+ */
874
+ static parsePathWithTreeId(fullPath) {
875
+ if (!fullPath.startsWith("#")) {
876
+ throw new Error(
877
+ `Invalid path format: '${fullPath}'. Path must start with #TreeID (e.g., #SimpleTest/0/1)`
878
+ );
879
+ }
880
+ const slashIndex = fullPath.indexOf("/");
881
+ let treeId;
882
+ let nodePath;
883
+ if (slashIndex === -1) {
884
+ treeId = fullPath.slice(1);
885
+ nodePath = "/";
886
+ } else {
887
+ treeId = fullPath.slice(1, slashIndex);
888
+ nodePath = fullPath.slice(slashIndex);
889
+ }
890
+ if (!treeId || treeId.trim() === "") {
891
+ throw new Error(
892
+ `Invalid path: tree ID cannot be empty in '${fullPath}'. Expected format: #TreeID/path`
893
+ );
894
+ }
895
+ if (!nodePath.startsWith("/")) {
896
+ throw new Error(
897
+ `Invalid path: node path must start with '/' in '${fullPath}'`
898
+ );
899
+ }
900
+ return { treeId, nodePath };
901
+ }
902
+ /**
903
+ * Get the root node of the tree
904
+ */
905
+ getRoot() {
906
+ return this.root;
907
+ }
908
+ /**
909
+ * Find a node by its path
910
+ * Path format: / for root, /0 for first child, /0/1 for second child of first child
911
+ */
912
+ findNodeByPath(path2) {
913
+ return this.pathIndex.get(path2) || null;
914
+ }
915
+ /**
916
+ * Find a node by its ID (if it has one)
917
+ * Convenience method for nodes with explicit IDs
918
+ */
919
+ findNodeById(nodeId) {
920
+ return this.idIndex.get(nodeId) || null;
921
+ }
922
+ /**
923
+ * Get the path for a given node
924
+ * Returns null if the node is not in the tree
925
+ */
926
+ getNodePath(targetNode) {
927
+ for (const [path2, node] of this.pathIndex.entries()) {
928
+ if (node === targetNode) {
929
+ return path2;
930
+ }
931
+ }
932
+ return null;
933
+ }
934
+ /**
935
+ * Get the path for a node by its ID
936
+ * More reliable than instance-based lookup
937
+ * Returns null if the node is not in the tree
938
+ */
939
+ getNodePathById(nodeId) {
940
+ const node = this.findNodeById(nodeId);
941
+ if (!node) return null;
942
+ for (const [path2, indexedNode] of this.pathIndex.entries()) {
943
+ if (indexedNode.id === nodeId) {
944
+ return path2;
945
+ }
946
+ }
947
+ return null;
948
+ }
949
+ /**
950
+ * Clone this BehaviorTree (deep clones the underlying TreeNode)
951
+ */
952
+ clone() {
953
+ const clonedRoot = this.root.clone();
954
+ return new _BehaviorTree(clonedRoot);
955
+ }
956
+ /**
957
+ * Convert this BehaviorTree to a Temporal workflow function
958
+ * Returns a workflow function that can be registered with Temporal
959
+ *
960
+ * @returns A Temporal workflow function that executes this behavior tree
961
+ *
962
+ * @example
963
+ * ```typescript
964
+ * import { BehaviorTree } from '@wayfarer-ai/btree';
965
+ * import { Sequence } from '@wayfarer-ai/btree';
966
+ * import { PrintAction } from '@wayfarer-ai/btree';
967
+ *
968
+ * const root = new Sequence({ id: 'root' });
969
+ * root.addChild(new PrintAction({ id: 'step1', message: 'Hello' }));
970
+ * root.addChild(new PrintAction({ id: 'step2', message: 'World' }));
971
+ *
972
+ * const tree = new BehaviorTree(root);
973
+ * const workflow = tree.toWorkflow();
974
+ *
975
+ * // Register with Temporal worker
976
+ * // Worker.create({ workflows: { myWorkflow: workflow }, ... })
977
+ * ```
978
+ */
979
+ toWorkflow() {
980
+ const root = this.root;
981
+ return async function behaviorTreeWorkflow(args) {
982
+ const context = {
983
+ blackboard: new ScopedBlackboard(),
984
+ treeRegistry: args.treeRegistry,
985
+ timestamp: Date.now(),
986
+ sessionId: args.sessionId || `session-${Date.now()}`,
987
+ // Store input immutably for ${input.key} resolution
988
+ input: args.input ? Object.freeze({ ...args.input }) : void 0,
989
+ // Pass activities for I/O operations (deterministic Temporal execution)
990
+ activities: args.activities,
991
+ // Pass tokenProvider for IntegrationAction authentication
992
+ tokenProvider: args.tokenProvider
993
+ };
994
+ if (args.input) {
995
+ for (const [key, value] of Object.entries(args.input)) {
996
+ context.blackboard.set(key, value);
997
+ }
998
+ }
999
+ const status = await root.tick(context);
1000
+ return {
1001
+ status,
1002
+ output: context.blackboard.toJSON()
1003
+ };
1004
+ };
1005
+ }
1006
+ /**
1007
+ * Replace a node at the given path with a new node
1008
+ * Updates parent-child relationships and rebuilds the index
1009
+ */
1010
+ replaceNodeAtPath(path2, newNode) {
1011
+ const oldNode = this.findNodeByPath(path2);
1012
+ if (!oldNode) {
1013
+ throw new Error(`Node not found at path: ${path2}`);
1014
+ }
1015
+ if (path2 === "/") {
1016
+ this.root = newNode;
1017
+ newNode.parent = void 0;
1018
+ } else {
1019
+ const pathParts = path2.split("/").filter((p) => p);
1020
+ const lastPart = pathParts[pathParts.length - 1];
1021
+ if (!lastPart) {
1022
+ throw new Error(`Invalid path format: ${path2}`);
1023
+ }
1024
+ const childIndex = parseInt(lastPart, 10);
1025
+ const parent = oldNode.parent;
1026
+ if (!parent || !parent.children) {
1027
+ throw new Error(`Cannot replace node: invalid parent at path ${path2}`);
1028
+ }
1029
+ parent.children[childIndex] = newNode;
1030
+ newNode.parent = parent;
1031
+ }
1032
+ this.buildNodeIndex();
1033
+ }
1034
+ /**
1035
+ * Build the node index with path-based and ID-based lookups
1036
+ */
1037
+ buildNodeIndex() {
1038
+ this.pathIndex.clear();
1039
+ this.idIndex.clear();
1040
+ const indexNode = (node, path2) => {
1041
+ this.pathIndex.set(path2, node);
1042
+ if (node.id) {
1043
+ this.idIndex.set(node.id, node);
1044
+ }
1045
+ if (node.children) {
1046
+ node.children.forEach((child, index) => {
1047
+ const childPath = path2 === "/" ? `/${index}` : `${path2}/${index}`;
1048
+ indexNode(child, childPath);
1049
+ });
1050
+ }
1051
+ };
1052
+ indexNode(this.root, "/");
1053
+ }
1054
+ };
1055
+
1056
+ // src/composites/conditional.ts
1057
+ var Conditional = class extends CompositeNode {
1058
+ condition;
1059
+ thenBranch;
1060
+ elseBranch;
1061
+ conditionEvaluated = false;
1062
+ selectedBranch;
1063
+ /**
1064
+ * Override addChild to enforce conditional structure
1065
+ */
1066
+ addChild(child) {
1067
+ if (!this.condition) {
1068
+ this.condition = child;
1069
+ this._children.push(child);
1070
+ child.parent = this;
1071
+ } else if (!this.thenBranch) {
1072
+ this.thenBranch = child;
1073
+ this._children.push(child);
1074
+ child.parent = this;
1075
+ } else if (!this.elseBranch) {
1076
+ this.elseBranch = child;
1077
+ this._children.push(child);
1078
+ child.parent = this;
1079
+ } else {
1080
+ throw new ConfigurationError(
1081
+ "Conditional can have maximum 3 children (condition, then, else)"
1082
+ );
1083
+ }
1084
+ }
1085
+ async executeTick(context) {
1086
+ checkSignal(context.signal);
1087
+ if (!this.condition) {
1088
+ throw new Error("Conditional requires at least a condition child");
1089
+ }
1090
+ if (!this.thenBranch) {
1091
+ throw new Error(
1092
+ "Conditional requires at least condition and then branch"
1093
+ );
1094
+ }
1095
+ if (!this.conditionEvaluated) {
1096
+ this.log("Evaluating condition");
1097
+ const conditionStatus = await this.condition.tick(context);
1098
+ switch (conditionStatus) {
1099
+ case "SUCCESS" /* SUCCESS */:
1100
+ this.log("Condition succeeded - will execute then branch");
1101
+ this.selectedBranch = this.thenBranch;
1102
+ this.conditionEvaluated = true;
1103
+ break;
1104
+ case "FAILURE" /* FAILURE */:
1105
+ if (this.elseBranch) {
1106
+ this.log("Condition failed - will execute else branch");
1107
+ this.selectedBranch = this.elseBranch;
1108
+ this.conditionEvaluated = true;
1109
+ } else {
1110
+ this.log("Condition failed - no else branch, returning FAILURE");
1111
+ this._status = "FAILURE" /* FAILURE */;
1112
+ return "FAILURE" /* FAILURE */;
1113
+ }
1114
+ break;
1115
+ case "RUNNING" /* RUNNING */:
1116
+ this.log("Condition is running");
1117
+ this._status = "RUNNING" /* RUNNING */;
1118
+ return "RUNNING" /* RUNNING */;
1119
+ default:
1120
+ throw new Error(
1121
+ `Unexpected status from condition: ${conditionStatus}`
1122
+ );
1123
+ }
1124
+ } else {
1125
+ this.log("Condition already evaluated - continuing branch execution");
1126
+ }
1127
+ if (!this.selectedBranch) {
1128
+ throw new Error("No branch selected for execution");
1129
+ }
1130
+ const branchStatus = await this.selectedBranch.tick(context);
1131
+ this._status = branchStatus;
1132
+ if (branchStatus !== "RUNNING" /* RUNNING */) {
1133
+ this.log("Branch completed - resetting condition check flag");
1134
+ this.conditionEvaluated = false;
1135
+ this.selectedBranch = void 0;
1136
+ }
1137
+ return branchStatus;
1138
+ }
1139
+ onHalt() {
1140
+ this.log("Halting - resetting condition check flag");
1141
+ this.conditionEvaluated = false;
1142
+ this.selectedBranch = void 0;
1143
+ super.onHalt();
1144
+ }
1145
+ onReset() {
1146
+ this.log("Resetting - clearing condition check flag");
1147
+ this.conditionEvaluated = false;
1148
+ this.selectedBranch = void 0;
1149
+ }
1150
+ };
1151
+
1152
+ // src/composites/for-each.ts
1153
+ var ForEach = class extends CompositeNode {
1154
+ collectionKey;
1155
+ itemKey;
1156
+ indexKey;
1157
+ currentIndex = 0;
1158
+ constructor(config) {
1159
+ super(config);
1160
+ this.collectionKey = config.collectionKey;
1161
+ this.itemKey = config.itemKey;
1162
+ this.indexKey = config.indexKey;
1163
+ }
1164
+ async executeTick(context) {
1165
+ if (this._children.length === 0) {
1166
+ throw new ConfigurationError(
1167
+ "ForEach requires at least one child (body)"
1168
+ );
1169
+ }
1170
+ const body = this._children[0];
1171
+ if (!body) {
1172
+ throw new ConfigurationError(
1173
+ "ForEach requires at least one child (body)"
1174
+ );
1175
+ }
1176
+ const collection = context.blackboard.get(this.collectionKey);
1177
+ if (!collection) {
1178
+ this.log(`Collection '${this.collectionKey}' not found in blackboard`);
1179
+ this._status = "FAILURE" /* FAILURE */;
1180
+ return "FAILURE" /* FAILURE */;
1181
+ }
1182
+ if (!Array.isArray(collection)) {
1183
+ throw new Error(`Collection '${this.collectionKey}' is not an array`);
1184
+ }
1185
+ if (collection.length === 0) {
1186
+ this.log("Collection is empty - returning SUCCESS");
1187
+ this._status = "SUCCESS" /* SUCCESS */;
1188
+ return "SUCCESS" /* SUCCESS */;
1189
+ }
1190
+ this.log(
1191
+ `Iterating over collection (${collection.length} items), starting at index ${this.currentIndex}`
1192
+ );
1193
+ while (this.currentIndex < collection.length) {
1194
+ checkSignal(context.signal);
1195
+ const item = collection[this.currentIndex];
1196
+ context.blackboard.set(this.itemKey, item);
1197
+ if (this.indexKey) {
1198
+ context.blackboard.set(this.indexKey, this.currentIndex);
1199
+ }
1200
+ this.log(
1201
+ `Processing item ${this.currentIndex}: ${JSON.stringify(item)}`
1202
+ );
1203
+ const bodyStatus = await body.tick(context);
1204
+ switch (bodyStatus) {
1205
+ case "SUCCESS" /* SUCCESS */:
1206
+ this.log(`Item ${this.currentIndex} succeeded`);
1207
+ this.currentIndex++;
1208
+ body.reset();
1209
+ break;
1210
+ case "FAILURE" /* FAILURE */:
1211
+ this.log(`Item ${this.currentIndex} failed - ForEach fails`);
1212
+ this._status = "FAILURE" /* FAILURE */;
1213
+ this.currentIndex = 0;
1214
+ return "FAILURE" /* FAILURE */;
1215
+ case "RUNNING" /* RUNNING */:
1216
+ this.log(`Item ${this.currentIndex} is running`);
1217
+ this._status = "RUNNING" /* RUNNING */;
1218
+ return "RUNNING" /* RUNNING */;
1219
+ // Will resume from this index next tick
1220
+ default:
1221
+ throw new Error(`Unexpected status from body: ${bodyStatus}`);
1222
+ }
1223
+ }
1224
+ this.log("All items processed successfully");
1225
+ this._status = "SUCCESS" /* SUCCESS */;
1226
+ this.currentIndex = 0;
1227
+ return "SUCCESS" /* SUCCESS */;
1228
+ }
1229
+ onReset() {
1230
+ super.onReset();
1231
+ this.currentIndex = 0;
1232
+ }
1233
+ onHalt() {
1234
+ super.onHalt();
1235
+ this.currentIndex = 0;
1236
+ }
1237
+ };
1238
+
1239
+ // src/composites/sequence.ts
1240
+ var Sequence = class extends CompositeNode {
1241
+ currentChildIndex = 0;
1242
+ async executeTick(context) {
1243
+ this.log("Ticking with", this._children.length, "children");
1244
+ if (this._children.length === 0) {
1245
+ return "SUCCESS" /* SUCCESS */;
1246
+ }
1247
+ while (this.currentChildIndex < this._children.length) {
1248
+ checkSignal(context.signal);
1249
+ const child = this._children[this.currentChildIndex];
1250
+ if (!child) {
1251
+ throw new Error(
1252
+ `Child at index ${this.currentChildIndex} is undefined`
1253
+ );
1254
+ }
1255
+ this.log(`Ticking child ${this.currentChildIndex}: ${child.name}`);
1256
+ const childStatus = await child.tick(context);
1257
+ switch (childStatus) {
1258
+ case "SUCCESS" /* SUCCESS */:
1259
+ this.log(`Child ${child.name} succeeded`);
1260
+ this.currentChildIndex++;
1261
+ break;
1262
+ case "FAILURE" /* FAILURE */:
1263
+ this.log(`Child ${child.name} failed - sequence fails`);
1264
+ this._status = "FAILURE" /* FAILURE */;
1265
+ this.currentChildIndex = 0;
1266
+ return "FAILURE" /* FAILURE */;
1267
+ case "RUNNING" /* RUNNING */:
1268
+ this.log(`Child ${child.name} is running`);
1269
+ this._status = "RUNNING" /* RUNNING */;
1270
+ return "RUNNING" /* RUNNING */;
1271
+ default:
1272
+ throw new Error(`Unexpected status from child: ${childStatus}`);
1273
+ }
1274
+ }
1275
+ this.log("All children succeeded");
1276
+ this._status = "SUCCESS" /* SUCCESS */;
1277
+ this.currentChildIndex = 0;
1278
+ return "SUCCESS" /* SUCCESS */;
1279
+ }
1280
+ onHalt() {
1281
+ this.haltChildren(this.currentChildIndex);
1282
+ this.currentChildIndex = 0;
1283
+ }
1284
+ onReset() {
1285
+ this.currentChildIndex = 0;
1286
+ }
1287
+ };
1288
+
1289
+ // src/composites/memory-sequence.ts
1290
+ var MemorySequence = class extends Sequence {
1291
+ completedChildren = /* @__PURE__ */ new Set();
1292
+ async executeTick(context) {
1293
+ this.log(
1294
+ `Ticking with ${this._children.length} children (${this.completedChildren.size} completed)`
1295
+ );
1296
+ if (this._children.length === 0) {
1297
+ return "SUCCESS" /* SUCCESS */;
1298
+ }
1299
+ for (let i = 0; i < this._children.length; i++) {
1300
+ checkSignal(context.signal);
1301
+ const child = this._children[i];
1302
+ if (!child) {
1303
+ throw new ConfigurationError(`Child at index ${i} is undefined`);
1304
+ }
1305
+ if (this.completedChildren.has(child.id)) {
1306
+ this.log(`Skipping completed child: ${child.name}`);
1307
+ continue;
1308
+ }
1309
+ this.log(`Ticking child ${i}: ${child.name}`);
1310
+ const childStatus = await child.tick(context);
1311
+ switch (childStatus) {
1312
+ case "SUCCESS" /* SUCCESS */:
1313
+ this.log(`Child ${child.name} succeeded - remembering`);
1314
+ this.completedChildren.add(child.id);
1315
+ break;
1316
+ case "FAILURE" /* FAILURE */:
1317
+ this.log(`Child ${child.name} failed - sequence fails`);
1318
+ this._status = "FAILURE" /* FAILURE */;
1319
+ return "FAILURE" /* FAILURE */;
1320
+ case "RUNNING" /* RUNNING */:
1321
+ this.log(`Child ${child.name} is running`);
1322
+ this._status = "RUNNING" /* RUNNING */;
1323
+ return "RUNNING" /* RUNNING */;
1324
+ default:
1325
+ throw new Error(`Unexpected status from child: ${childStatus}`);
1326
+ }
1327
+ }
1328
+ this.log("All children succeeded");
1329
+ this._status = "SUCCESS" /* SUCCESS */;
1330
+ return "SUCCESS" /* SUCCESS */;
1331
+ }
1332
+ onReset() {
1333
+ super.onReset();
1334
+ this.log("Clearing completed children memory");
1335
+ this.completedChildren.clear();
1336
+ }
1337
+ onHalt() {
1338
+ super.onHalt();
1339
+ }
1340
+ };
1341
+ var SequenceWithMemory = class extends MemorySequence {
1342
+ constructor(config) {
1343
+ super({ ...config, type: "SequenceWithMemory" });
1344
+ }
1345
+ };
1346
+
1347
+ // src/composites/parallel.ts
1348
+ var Parallel = class extends CompositeNode {
1349
+ strategy;
1350
+ successThreshold;
1351
+ failureThreshold;
1352
+ constructor(config) {
1353
+ super(config);
1354
+ this.strategy = config.strategy ?? "strict";
1355
+ this.successThreshold = config.successThreshold;
1356
+ this.failureThreshold = config.failureThreshold;
1357
+ }
1358
+ async executeTick(context) {
1359
+ this.log(
1360
+ `Ticking with ${this._children.length} children (strategy: ${this.strategy})`
1361
+ );
1362
+ if (this._children.length === 0) {
1363
+ return "SUCCESS" /* SUCCESS */;
1364
+ }
1365
+ const childrenToTick = this._children.filter((child) => {
1366
+ const status = child.status();
1367
+ return status === "IDLE" /* IDLE */ || status === "RUNNING" /* RUNNING */;
1368
+ });
1369
+ this.log(
1370
+ `Ticking ${childrenToTick.length}/${this._children.length} children (others completed)`
1371
+ );
1372
+ checkSignal(context.signal);
1373
+ if (childrenToTick.length > 0) {
1374
+ await Promise.all(childrenToTick.map((child) => child.tick(context)));
1375
+ }
1376
+ const allStatuses = this._children.map((child) => child.status());
1377
+ const hasRunning = allStatuses.some(
1378
+ (status) => status === "RUNNING" /* RUNNING */
1379
+ );
1380
+ if (hasRunning) {
1381
+ this.log("At least one child returned RUNNING");
1382
+ return "RUNNING" /* RUNNING */;
1383
+ }
1384
+ const successes = allStatuses.filter(
1385
+ (status) => status === "SUCCESS" /* SUCCESS */
1386
+ ).length;
1387
+ const failures = allStatuses.filter(
1388
+ (status) => status === "FAILURE" /* FAILURE */
1389
+ ).length;
1390
+ this.log(`Results - Successes: ${successes}, Failures: ${failures}`);
1391
+ if (this.successThreshold !== void 0 && successes >= this.successThreshold) {
1392
+ this.log(
1393
+ `Success threshold met: ${successes}/${this.successThreshold} -> SUCCESS`
1394
+ );
1395
+ return "SUCCESS" /* SUCCESS */;
1396
+ }
1397
+ if (this.failureThreshold !== void 0 && failures >= this.failureThreshold) {
1398
+ this.log(
1399
+ `Failure threshold met: ${failures}/${this.failureThreshold} -> FAILURE`
1400
+ );
1401
+ return "FAILURE" /* FAILURE */;
1402
+ }
1403
+ if (this.strategy === "strict") {
1404
+ const finalStatus = successes === this._children.length ? "SUCCESS" /* SUCCESS */ : "FAILURE" /* FAILURE */;
1405
+ this.log(
1406
+ `Strategy 'strict': ${successes}/${this._children.length} succeeded -> ${finalStatus}`
1407
+ );
1408
+ return finalStatus;
1409
+ } else {
1410
+ const finalStatus = successes > 0 ? "SUCCESS" /* SUCCESS */ : "FAILURE" /* FAILURE */;
1411
+ this.log(`Strategy 'any': ${successes} succeeded -> ${finalStatus}`);
1412
+ return finalStatus;
1413
+ }
1414
+ }
1415
+ onHalt() {
1416
+ this.log("Halting parallel execution");
1417
+ for (const child of this._children) {
1418
+ if (child.status() === "RUNNING" /* RUNNING */) {
1419
+ child.halt();
1420
+ }
1421
+ }
1422
+ }
1423
+ onReset() {
1424
+ this.log("Resetting parallel state");
1425
+ }
1426
+ };
1427
+
1428
+ // src/composites/reactive-sequence.ts
1429
+ var ReactiveSequence = class extends Sequence {
1430
+ async executeTick(context) {
1431
+ this.log("Ticking (reactive - always starts from beginning)");
1432
+ if (this._children.length === 0) {
1433
+ return "SUCCESS" /* SUCCESS */;
1434
+ }
1435
+ for (let i = 0; i < this._children.length; i++) {
1436
+ checkSignal(context.signal);
1437
+ const child = this._children[i];
1438
+ if (!child) {
1439
+ throw new ConfigurationError(`Child at index ${i} is undefined`);
1440
+ }
1441
+ this.log(`Ticking child ${i}: ${child.name}`);
1442
+ const childStatus = await child.tick(context);
1443
+ switch (childStatus) {
1444
+ case "SUCCESS" /* SUCCESS */:
1445
+ this.log(`Child ${child.name} succeeded`);
1446
+ break;
1447
+ case "FAILURE" /* FAILURE */:
1448
+ this.log(`Child ${child.name} failed - sequence fails`);
1449
+ this._status = "FAILURE" /* FAILURE */;
1450
+ return "FAILURE" /* FAILURE */;
1451
+ case "RUNNING" /* RUNNING */:
1452
+ this.log(`Child ${child.name} is running`);
1453
+ this._status = "RUNNING" /* RUNNING */;
1454
+ return "RUNNING" /* RUNNING */;
1455
+ default:
1456
+ throw new Error(`Unexpected status from child: ${childStatus}`);
1457
+ }
1458
+ }
1459
+ this.log("All children succeeded");
1460
+ this._status = "SUCCESS" /* SUCCESS */;
1461
+ return "SUCCESS" /* SUCCESS */;
1462
+ }
1463
+ /**
1464
+ * Override to prevent parent Sequence from resetting currentChildIndex
1465
+ * (ReactiveSequence doesn't use currentChildIndex)
1466
+ */
1467
+ onReset() {
1468
+ this._status = "IDLE" /* IDLE */;
1469
+ for (const child of this._children) {
1470
+ child.reset();
1471
+ }
1472
+ }
1473
+ };
1474
+
1475
+ // src/composites/recovery.ts
1476
+ var Recovery = class extends CompositeNode {
1477
+ tryBranch;
1478
+ catchBranch;
1479
+ finallyBranch;
1480
+ addChild(child) {
1481
+ if (!this.tryBranch) {
1482
+ this.tryBranch = child;
1483
+ this._children.push(child);
1484
+ child.parent = this;
1485
+ } else if (!this.catchBranch) {
1486
+ this.catchBranch = child;
1487
+ this._children.push(child);
1488
+ child.parent = this;
1489
+ } else if (!this.finallyBranch) {
1490
+ this.finallyBranch = child;
1491
+ this._children.push(child);
1492
+ child.parent = this;
1493
+ } else {
1494
+ throw new ConfigurationError(
1495
+ "Recovery can have maximum 3 children (try, catch, finally)"
1496
+ );
1497
+ }
1498
+ }
1499
+ async executeTick(context) {
1500
+ checkSignal(context.signal);
1501
+ if (!this.tryBranch) {
1502
+ throw new ConfigurationError("Recovery requires at least a try branch");
1503
+ }
1504
+ this.log("Executing try branch");
1505
+ const tryResult = await this.tryBranch.tick(context);
1506
+ let mainResult;
1507
+ if (tryResult === "FAILURE" /* FAILURE */ && this.catchBranch) {
1508
+ this.log("Try branch failed - executing catch branch");
1509
+ mainResult = await this.catchBranch.tick(context);
1510
+ } else {
1511
+ mainResult = tryResult;
1512
+ }
1513
+ if (this.finallyBranch) {
1514
+ this.log("Executing finally branch");
1515
+ await this.finallyBranch.tick(context);
1516
+ this.log("Finally branch completed");
1517
+ }
1518
+ this._status = mainResult;
1519
+ return mainResult;
1520
+ }
1521
+ };
1522
+
1523
+ // src/composites/selector.ts
1524
+ var Selector = class extends CompositeNode {
1525
+ currentChildIndex = 0;
1526
+ async executeTick(context) {
1527
+ this.log("Ticking with", this._children.length, "children");
1528
+ if (this._children.length === 0) {
1529
+ return "FAILURE" /* FAILURE */;
1530
+ }
1531
+ while (this.currentChildIndex < this._children.length) {
1532
+ checkSignal(context.signal);
1533
+ const child = this._children[this.currentChildIndex];
1534
+ if (!child) {
1535
+ throw new Error(
1536
+ `Child at index ${this.currentChildIndex} is undefined`
1537
+ );
1538
+ }
1539
+ this.log(`Ticking child ${this.currentChildIndex}: ${child.name}`);
1540
+ const childStatus = await child.tick(context);
1541
+ switch (childStatus) {
1542
+ case "SUCCESS" /* SUCCESS */:
1543
+ this.log(`Child ${child.name} succeeded - selector succeeds`);
1544
+ this._status = "SUCCESS" /* SUCCESS */;
1545
+ this.currentChildIndex = 0;
1546
+ return "SUCCESS" /* SUCCESS */;
1547
+ case "FAILURE" /* FAILURE */:
1548
+ this.log(`Child ${child.name} failed`);
1549
+ this.currentChildIndex++;
1550
+ break;
1551
+ case "RUNNING" /* RUNNING */:
1552
+ this.log(`Child ${child.name} is running`);
1553
+ this._status = "RUNNING" /* RUNNING */;
1554
+ return "RUNNING" /* RUNNING */;
1555
+ default:
1556
+ throw new Error(`Unexpected status from child: ${childStatus}`);
1557
+ }
1558
+ }
1559
+ this.log("All children failed");
1560
+ this._status = "FAILURE" /* FAILURE */;
1561
+ this.currentChildIndex = 0;
1562
+ return "FAILURE" /* FAILURE */;
1563
+ }
1564
+ onHalt() {
1565
+ this.haltChildren(this.currentChildIndex);
1566
+ this.currentChildIndex = 0;
1567
+ }
1568
+ onReset() {
1569
+ this.currentChildIndex = 0;
1570
+ }
1571
+ };
1572
+ var Fallback = class extends Selector {
1573
+ constructor(config) {
1574
+ super({ ...config, type: "Fallback" });
1575
+ }
1576
+ };
1577
+
1578
+ // src/utilities/variable-resolver.ts
1579
+ var VARIABLE_PATTERN = /\$\{(input|bb|env|param)\.([a-zA-Z0-9_.]+)\}|\$\{([a-zA-Z0-9_.]+)\}/g;
1580
+ var HAS_VARIABLE_PATTERN = /\$\{(input|bb|env|param)\.([a-zA-Z0-9_.]+)\}|\$\{([a-zA-Z0-9_.]+)\}/;
1581
+ var FULL_MATCH_PATTERN = /^\$\{(input|bb|env|param)\.([a-zA-Z0-9_.]+)\}$|^\$\{([a-zA-Z0-9_.]+)\}$/;
1582
+ var safeProcessEnv = () => {
1583
+ try {
1584
+ return typeof process !== "undefined" && process?.env ? process.env : {};
1585
+ } catch {
1586
+ return {};
1587
+ }
1588
+ };
1589
+ function resolveString(str, ctx, opts = {}) {
1590
+ const { preserveUndefined = true, envSource = safeProcessEnv() } = opts;
1591
+ const fullMatch = str.match(FULL_MATCH_PATTERN);
1592
+ if (fullMatch) {
1593
+ const namespace = fullMatch[1];
1594
+ const namespacedKey = fullMatch[2];
1595
+ const simpleKey = fullMatch[3];
1596
+ const key = namespacedKey || simpleKey;
1597
+ const ns = namespace || "bb";
1598
+ if (key) {
1599
+ const value = resolveVariable(ns, key, ctx, envSource);
1600
+ if (value !== void 0) {
1601
+ return value;
1602
+ }
1603
+ return preserveUndefined ? str : void 0;
1604
+ }
1605
+ }
1606
+ return str.replace(VARIABLE_PATTERN, (match, namespace, namespacedKey, simpleKey) => {
1607
+ const key = namespacedKey || simpleKey;
1608
+ const ns = namespace || "bb";
1609
+ const value = resolveVariable(ns, key, ctx, envSource);
1610
+ if (value === void 0) {
1611
+ return preserveUndefined ? match : "";
1612
+ }
1613
+ if (value === null) {
1614
+ return "null";
1615
+ }
1616
+ if (typeof value === "object") {
1617
+ try {
1618
+ return JSON.stringify(value);
1619
+ } catch {
1620
+ return String(value);
1621
+ }
1622
+ }
1623
+ return String(value);
1624
+ });
1625
+ }
1626
+ function resolveValue(value, ctx, opts = {}) {
1627
+ if (typeof value === "string") {
1628
+ return resolveString(value, ctx, opts);
1629
+ }
1630
+ if (Array.isArray(value)) {
1631
+ return value.map((item) => resolveValue(item, ctx, opts));
1632
+ }
1633
+ if (value !== null && typeof value === "object") {
1634
+ const resolved = {};
1635
+ for (const [key, val] of Object.entries(value)) {
1636
+ resolved[key] = resolveValue(val, ctx, opts);
1637
+ }
1638
+ return resolved;
1639
+ }
1640
+ return value;
1641
+ }
1642
+ function resolveVariable(namespace, key, ctx, envSource) {
1643
+ switch (namespace) {
1644
+ case "input":
1645
+ return getNestedValue(ctx.input, key);
1646
+ case "bb":
1647
+ return getNestedBlackboardValue(ctx.blackboard, key);
1648
+ case "env":
1649
+ return envSource[key];
1650
+ case "param":
1651
+ if (ctx.testData) {
1652
+ const parts = key.split(".");
1653
+ const firstPart = parts[0];
1654
+ if (firstPart) {
1655
+ let value = ctx.testData.get(firstPart);
1656
+ for (let i = 1; i < parts.length && value !== void 0; i++) {
1657
+ const part = parts[i];
1658
+ if (part && typeof value === "object" && value !== null) {
1659
+ value = value[part];
1660
+ } else {
1661
+ return void 0;
1662
+ }
1663
+ }
1664
+ return value;
1665
+ }
1666
+ }
1667
+ return void 0;
1668
+ default:
1669
+ return getNestedBlackboardValue(ctx.blackboard, key);
1670
+ }
1671
+ }
1672
+ function getNestedValue(obj, path2) {
1673
+ if (obj === void 0 || obj === null) {
1674
+ return void 0;
1675
+ }
1676
+ if (typeof obj !== "object") {
1677
+ return void 0;
1678
+ }
1679
+ const parts = path2.split(".");
1680
+ let value = obj;
1681
+ for (const part of parts) {
1682
+ if (value === void 0 || value === null) {
1683
+ return void 0;
1684
+ }
1685
+ if (typeof value !== "object") {
1686
+ return void 0;
1687
+ }
1688
+ value = value[part];
1689
+ }
1690
+ return value;
1691
+ }
1692
+ function getNestedBlackboardValue(blackboard, path2) {
1693
+ const parts = path2.split(".");
1694
+ const firstPart = parts[0];
1695
+ if (!firstPart) {
1696
+ return void 0;
1697
+ }
1698
+ let value = blackboard.get(firstPart);
1699
+ for (let i = 1; i < parts.length && value !== void 0; i++) {
1700
+ const part = parts[i];
1701
+ if (part && typeof value === "object" && value !== null) {
1702
+ value = value[part];
1703
+ } else {
1704
+ return void 0;
1705
+ }
1706
+ }
1707
+ return value;
1708
+ }
1709
+ function hasVariables(str) {
1710
+ return HAS_VARIABLE_PATTERN.test(str);
1711
+ }
1712
+ function extractVariables(str) {
1713
+ const variables = [];
1714
+ const pattern = /\$\{(input|bb|env|param)\.([a-zA-Z0-9_.]+)\}|\$\{([a-zA-Z0-9_.]+)\}/g;
1715
+ let match;
1716
+ while ((match = pattern.exec(str)) !== null) {
1717
+ const namespace = match[1] || "bb";
1718
+ const key = match[2] || match[3];
1719
+ if (key) {
1720
+ variables.push({ namespace, key });
1721
+ }
1722
+ }
1723
+ return variables;
1724
+ }
1725
+
1726
+ // src/composites/sub-tree.ts
1727
+ var SubTree = class extends ActionNode {
1728
+ treeId;
1729
+ params;
1730
+ outputs;
1731
+ clonedTree;
1732
+ // Cached tree instance
1733
+ constructor(config) {
1734
+ super(config);
1735
+ this.treeId = config.treeId;
1736
+ this.params = config.params ?? {};
1737
+ this.outputs = config.outputs ?? [];
1738
+ }
1739
+ async executeTick(context) {
1740
+ checkSignal(context.signal);
1741
+ if (!this.clonedTree) {
1742
+ if (!context.treeRegistry.hasTree(this.treeId)) {
1743
+ throw new Error(
1744
+ `SubTree tree '${this.treeId}' not found in registry. Available trees: ${context.treeRegistry.getAllTreeIds().join(", ") || "none"}`
1745
+ );
1746
+ }
1747
+ const clonedBehaviorTree = context.treeRegistry.cloneTree(this.treeId);
1748
+ this.clonedTree = clonedBehaviorTree.getRoot();
1749
+ this.log(`Cloned SubTree tree '${this.treeId}' from registry`);
1750
+ }
1751
+ const subtreeScope = context.blackboard.createScope(`subtree_${this.id}`);
1752
+ this.log(`Created scoped blackboard: ${subtreeScope.getFullScopePath()}`);
1753
+ if (Object.keys(this.params).length > 0) {
1754
+ const varCtx = {
1755
+ blackboard: context.blackboard,
1756
+ input: context.input,
1757
+ testData: context.testData
1758
+ };
1759
+ const resolvedParams = resolveValue(this.params, varCtx);
1760
+ for (const [key, value] of Object.entries(resolvedParams)) {
1761
+ subtreeScope.set(key, value);
1762
+ this.log(`Set param '${key}' in subtree scope`);
1763
+ }
1764
+ }
1765
+ const scopedContext = {
1766
+ ...context,
1767
+ blackboard: subtreeScope
1768
+ };
1769
+ try {
1770
+ this.log(`Executing SubTree tree '${this.treeId}'`);
1771
+ const status = await this.clonedTree.tick(scopedContext);
1772
+ if (this.outputs.length > 0 && (status === "SUCCESS" /* SUCCESS */ || status === "RUNNING" /* RUNNING */)) {
1773
+ for (const outputKey of this.outputs) {
1774
+ if (subtreeScope.has(outputKey)) {
1775
+ const value = subtreeScope.get(outputKey);
1776
+ context.blackboard.set(outputKey, value);
1777
+ this.log(`Exported output '${outputKey}' to parent scope`);
1778
+ } else {
1779
+ this.log(`Output '${outputKey}' not found in subtree scope, skipping`);
1780
+ }
1781
+ }
1782
+ }
1783
+ this.log(
1784
+ `SubTree tree '${this.treeId}' completed with status: ${status}`
1785
+ );
1786
+ return status;
1787
+ } catch (error) {
1788
+ this.log(`SubTree tree '${this.treeId}' failed with error: ${error}`);
1789
+ throw error;
1790
+ }
1791
+ }
1792
+ /**
1793
+ * Override clone to include cloned tree
1794
+ */
1795
+ clone() {
1796
+ const ClonedClass = this.constructor;
1797
+ const cloned = new ClonedClass({ ...this.config });
1798
+ return cloned;
1799
+ }
1800
+ /**
1801
+ * Override halt to halt the referenced tree
1802
+ */
1803
+ halt() {
1804
+ super.halt();
1805
+ if (this.clonedTree && this.clonedTree.status() === "RUNNING" /* RUNNING */) {
1806
+ this.clonedTree.halt();
1807
+ }
1808
+ }
1809
+ /**
1810
+ * Override reset to reset the referenced tree
1811
+ */
1812
+ reset() {
1813
+ super.reset();
1814
+ if (this.clonedTree) {
1815
+ this.clonedTree.reset();
1816
+ }
1817
+ }
1818
+ };
1819
+
1820
+ // src/composites/while.ts
1821
+ var While = class extends CompositeNode {
1822
+ maxIterations;
1823
+ currentIteration = 0;
1824
+ condition;
1825
+ body;
1826
+ bodyStarted = false;
1827
+ constructor(config) {
1828
+ super(config);
1829
+ this.maxIterations = config.maxIterations ?? 1e3;
1830
+ }
1831
+ addChild(child) {
1832
+ if (!this.condition) {
1833
+ this.condition = child;
1834
+ this._children.push(child);
1835
+ child.parent = this;
1836
+ } else if (!this.body) {
1837
+ this.body = child;
1838
+ this._children.push(child);
1839
+ child.parent = this;
1840
+ } else {
1841
+ throw new ConfigurationError(
1842
+ "While can have maximum 2 children (condition, body)"
1843
+ );
1844
+ }
1845
+ }
1846
+ async executeTick(context) {
1847
+ if (!this.condition) {
1848
+ throw new ConfigurationError("While requires a condition child");
1849
+ }
1850
+ if (!this.body) {
1851
+ throw new ConfigurationError("While requires a body child");
1852
+ }
1853
+ this.log(
1854
+ `Starting while loop (iteration ${this.currentIteration}/${this.maxIterations})`
1855
+ );
1856
+ while (this.currentIteration < this.maxIterations) {
1857
+ checkSignal(context.signal);
1858
+ if (!this.bodyStarted) {
1859
+ this.log(`Evaluating condition (iteration ${this.currentIteration})`);
1860
+ const conditionStatus = await this.condition.tick(context);
1861
+ if (conditionStatus === "RUNNING" /* RUNNING */) {
1862
+ this.log("Condition is running");
1863
+ this._status = "RUNNING" /* RUNNING */;
1864
+ return "RUNNING" /* RUNNING */;
1865
+ }
1866
+ if (conditionStatus === "FAILURE" /* FAILURE */) {
1867
+ this.log("Condition failed - exiting loop");
1868
+ this._status = "SUCCESS" /* SUCCESS */;
1869
+ this.currentIteration = 0;
1870
+ this.bodyStarted = false;
1871
+ return "SUCCESS" /* SUCCESS */;
1872
+ }
1873
+ this.bodyStarted = true;
1874
+ } else {
1875
+ this.log(
1876
+ `Body already started for iteration ${this.currentIteration} - continuing execution`
1877
+ );
1878
+ }
1879
+ this.log(`Executing body (iteration ${this.currentIteration})`);
1880
+ const bodyStatus = await this.body.tick(context);
1881
+ switch (bodyStatus) {
1882
+ case "SUCCESS" /* SUCCESS */:
1883
+ this.log("Body succeeded - continuing loop");
1884
+ this.currentIteration++;
1885
+ this.bodyStarted = false;
1886
+ this.condition.reset();
1887
+ this.body.reset();
1888
+ break;
1889
+ case "FAILURE" /* FAILURE */:
1890
+ this.log("Body failed - While fails");
1891
+ this._status = "FAILURE" /* FAILURE */;
1892
+ this.currentIteration = 0;
1893
+ this.bodyStarted = false;
1894
+ return "FAILURE" /* FAILURE */;
1895
+ case "RUNNING" /* RUNNING */:
1896
+ this.log("Body is running");
1897
+ this._status = "RUNNING" /* RUNNING */;
1898
+ return "RUNNING" /* RUNNING */;
1899
+ default:
1900
+ throw new Error(`Unexpected status from body: ${bodyStatus}`);
1901
+ }
1902
+ }
1903
+ this.log(`Max iterations (${this.maxIterations}) reached`);
1904
+ this._status = "FAILURE" /* FAILURE */;
1905
+ this.currentIteration = 0;
1906
+ this.bodyStarted = false;
1907
+ return "FAILURE" /* FAILURE */;
1908
+ }
1909
+ onReset() {
1910
+ super.onReset();
1911
+ this.log("Resetting - clearing body started flag");
1912
+ this.currentIteration = 0;
1913
+ this.bodyStarted = false;
1914
+ }
1915
+ onHalt() {
1916
+ super.onHalt();
1917
+ this.log("Halting - clearing body started flag");
1918
+ this.currentIteration = 0;
1919
+ this.bodyStarted = false;
1920
+ }
1921
+ };
1922
+
1923
+ // src/decorators/delay.ts
1924
+ var import_workflow = require("@temporalio/workflow");
1925
+ var Delay = class extends DecoratorNode {
1926
+ delayMs;
1927
+ delayStartTime = null;
1928
+ useTemporalAPI = null;
1929
+ // Cached detection result
1930
+ constructor(config) {
1931
+ super(config);
1932
+ this.delayMs = config.delayMs;
1933
+ if (this.delayMs < 0) {
1934
+ throw new ConfigurationError(
1935
+ `${this.name}: Delay must be non-negative (got ${this.delayMs})`
1936
+ );
1937
+ }
1938
+ }
1939
+ async executeTick(context) {
1940
+ checkSignal(context.signal);
1941
+ if (!this.child) {
1942
+ throw new ConfigurationError(`${this.name}: Decorator must have a child`);
1943
+ }
1944
+ if (this.delayMs === 0) {
1945
+ return await this.child.tick(context);
1946
+ }
1947
+ if (this.useTemporalAPI === null) {
1948
+ try {
1949
+ this.log(`Starting delay of ${this.delayMs}ms`);
1950
+ await (0, import_workflow.sleep)(this.delayMs);
1951
+ this.useTemporalAPI = true;
1952
+ this.log("Delay completed, executing child");
1953
+ checkSignal(context.signal);
1954
+ const childStatus2 = await this.child.tick(context);
1955
+ this._status = childStatus2;
1956
+ return childStatus2;
1957
+ } catch (err) {
1958
+ this.useTemporalAPI = false;
1959
+ }
1960
+ }
1961
+ if (this.useTemporalAPI === true) {
1962
+ this.log(`Starting delay of ${this.delayMs}ms`);
1963
+ await (0, import_workflow.sleep)(this.delayMs);
1964
+ this.log("Delay completed, executing child");
1965
+ checkSignal(context.signal);
1966
+ const childStatus2 = await this.child.tick(context);
1967
+ this._status = childStatus2;
1968
+ return childStatus2;
1969
+ }
1970
+ if (this.child.status() === "RUNNING" /* RUNNING */) {
1971
+ checkSignal(context.signal);
1972
+ const childStatus2 = await this.child.tick(context);
1973
+ if (childStatus2 !== "RUNNING" /* RUNNING */) {
1974
+ this.delayStartTime = null;
1975
+ }
1976
+ this._status = childStatus2;
1977
+ return childStatus2;
1978
+ }
1979
+ if (this.delayStartTime === null) {
1980
+ this.delayStartTime = Date.now();
1981
+ this.log(`Starting delay of ${this.delayMs}ms`);
1982
+ }
1983
+ const elapsed = Date.now() - this.delayStartTime;
1984
+ if (elapsed < this.delayMs) {
1985
+ this._status = "RUNNING" /* RUNNING */;
1986
+ return "RUNNING" /* RUNNING */;
1987
+ }
1988
+ this.log("Delay completed, executing child");
1989
+ checkSignal(context.signal);
1990
+ const childStatus = await this.child.tick(context);
1991
+ if (childStatus !== "RUNNING" /* RUNNING */) {
1992
+ this.delayStartTime = null;
1993
+ }
1994
+ this._status = childStatus;
1995
+ return childStatus;
1996
+ }
1997
+ onHalt() {
1998
+ this.delayStartTime = null;
1999
+ }
2000
+ onReset() {
2001
+ this.delayStartTime = null;
2002
+ this.useTemporalAPI = null;
2003
+ }
2004
+ };
2005
+
2006
+ // src/decorators/force-result.ts
2007
+ var ForceSuccess = class extends DecoratorNode {
2008
+ async executeTick(context) {
2009
+ checkSignal(context.signal);
2010
+ if (!this.child) {
2011
+ throw new ConfigurationError("ForceSuccess requires a child");
2012
+ }
2013
+ const childStatus = await this.child.tick(context);
2014
+ if (childStatus === "RUNNING" /* RUNNING */) {
2015
+ this._status = "RUNNING" /* RUNNING */;
2016
+ return "RUNNING" /* RUNNING */;
2017
+ }
2018
+ this._status = "SUCCESS" /* SUCCESS */;
2019
+ return "SUCCESS" /* SUCCESS */;
2020
+ }
2021
+ };
2022
+ var ForceFailure = class extends DecoratorNode {
2023
+ async executeTick(context) {
2024
+ checkSignal(context.signal);
2025
+ if (!this.child) {
2026
+ throw new ConfigurationError("ForceFailure requires a child");
2027
+ }
2028
+ const childStatus = await this.child.tick(context);
2029
+ if (childStatus === "RUNNING" /* RUNNING */) {
2030
+ this._status = "RUNNING" /* RUNNING */;
2031
+ return "RUNNING" /* RUNNING */;
2032
+ }
2033
+ this._status = "FAILURE" /* FAILURE */;
2034
+ return "FAILURE" /* FAILURE */;
2035
+ }
2036
+ };
2037
+
2038
+ // src/decorators/invert.ts
2039
+ var Invert = class extends DecoratorNode {
2040
+ async executeTick(context) {
2041
+ checkSignal(context.signal);
2042
+ if (!this.child) {
2043
+ throw new ConfigurationError(`${this.name}: Decorator must have a child`);
2044
+ }
2045
+ this.log("Ticking child");
2046
+ const childStatus = await this.child.tick(context);
2047
+ switch (childStatus) {
2048
+ case "SUCCESS" /* SUCCESS */:
2049
+ this.log("Child succeeded - returning FAILURE");
2050
+ this._status = "FAILURE" /* FAILURE */;
2051
+ return "FAILURE" /* FAILURE */;
2052
+ case "FAILURE" /* FAILURE */:
2053
+ this.log("Child failed - returning SUCCESS");
2054
+ this._status = "SUCCESS" /* SUCCESS */;
2055
+ return "SUCCESS" /* SUCCESS */;
2056
+ case "RUNNING" /* RUNNING */:
2057
+ this.log("Child is running");
2058
+ this._status = "RUNNING" /* RUNNING */;
2059
+ return "RUNNING" /* RUNNING */;
2060
+ default:
2061
+ return childStatus;
2062
+ }
2063
+ }
2064
+ };
2065
+
2066
+ // src/decorators/keep-running.ts
2067
+ var KeepRunningUntilFailure = class extends DecoratorNode {
2068
+ async executeTick(context) {
2069
+ checkSignal(context.signal);
2070
+ if (!this.child) {
2071
+ throw new ConfigurationError(
2072
+ "KeepRunningUntilFailure requires a child"
2073
+ );
2074
+ }
2075
+ const result = await this.child.tick(context);
2076
+ switch (result) {
2077
+ case "SUCCESS" /* SUCCESS */:
2078
+ this.log("Child succeeded - resetting and continuing");
2079
+ this.child.reset();
2080
+ this._status = "RUNNING" /* RUNNING */;
2081
+ return "RUNNING" /* RUNNING */;
2082
+ case "FAILURE" /* FAILURE */:
2083
+ this.log("Child failed - goal achieved");
2084
+ this._status = "SUCCESS" /* SUCCESS */;
2085
+ return "SUCCESS" /* SUCCESS */;
2086
+ case "RUNNING" /* RUNNING */:
2087
+ this.log("Child is running");
2088
+ this._status = "RUNNING" /* RUNNING */;
2089
+ return "RUNNING" /* RUNNING */;
2090
+ default:
2091
+ throw new Error(`Unexpected status from child: ${result}`);
2092
+ }
2093
+ }
2094
+ };
2095
+
2096
+ // src/decorators/precondition.ts
2097
+ var Precondition = class extends DecoratorNode {
2098
+ preconditions = [];
2099
+ preconditionsChecked = false;
2100
+ /**
2101
+ * Add a precondition to check before main execution
2102
+ */
2103
+ addPrecondition(condition, resolver, required = true) {
2104
+ this.preconditions.push({ condition, resolver, required });
2105
+ }
2106
+ async executeTick(context) {
2107
+ checkSignal(context.signal);
2108
+ if (!this.child) {
2109
+ throw new ConfigurationError("Precondition requires a child");
2110
+ }
2111
+ if (!this.preconditionsChecked) {
2112
+ for (let i = 0; i < this.preconditions.length; i++) {
2113
+ checkSignal(context.signal);
2114
+ const precond = this.preconditions[i];
2115
+ if (!precond) {
2116
+ continue;
2117
+ }
2118
+ this.log(
2119
+ `Checking precondition ${i + 1}/${this.preconditions.length}`
2120
+ );
2121
+ const conditionResult = await precond.condition.tick(context);
2122
+ if (conditionResult === "RUNNING" /* RUNNING */) {
2123
+ this.log(`Precondition ${i + 1} is running`);
2124
+ this._status = "RUNNING" /* RUNNING */;
2125
+ return "RUNNING" /* RUNNING */;
2126
+ }
2127
+ if (conditionResult === "FAILURE" /* FAILURE */) {
2128
+ this.log(`Precondition ${i + 1} failed`);
2129
+ if (precond.resolver) {
2130
+ this.log(`Attempting to resolve precondition ${i + 1}`);
2131
+ const resolverResult = await precond.resolver.tick(context);
2132
+ if (resolverResult === "RUNNING" /* RUNNING */) {
2133
+ this.log(`Resolver ${i + 1} is running`);
2134
+ this._status = "RUNNING" /* RUNNING */;
2135
+ return "RUNNING" /* RUNNING */;
2136
+ }
2137
+ if (resolverResult === "SUCCESS" /* SUCCESS */) {
2138
+ this.log(`Precondition ${i + 1} resolved successfully`);
2139
+ const recheckResult = await precond.condition.tick(context);
2140
+ if (recheckResult !== "SUCCESS" /* SUCCESS */) {
2141
+ if (precond.required) {
2142
+ this.log(
2143
+ `Precondition ${i + 1} still not met after resolution`
2144
+ );
2145
+ this._status = "FAILURE" /* FAILURE */;
2146
+ return "FAILURE" /* FAILURE */;
2147
+ } else {
2148
+ this.log(`Optional precondition ${i + 1} skipped`);
2149
+ }
2150
+ }
2151
+ } else if (precond.required) {
2152
+ this.log(`Failed to resolve required precondition ${i + 1}`);
2153
+ this._status = "FAILURE" /* FAILURE */;
2154
+ return "FAILURE" /* FAILURE */;
2155
+ }
2156
+ } else if (precond.required) {
2157
+ this.log(`Required precondition ${i + 1} not met (no resolver)`);
2158
+ this._status = "FAILURE" /* FAILURE */;
2159
+ return "FAILURE" /* FAILURE */;
2160
+ } else {
2161
+ this.log(`Optional precondition ${i + 1} skipped`);
2162
+ }
2163
+ }
2164
+ }
2165
+ this.preconditionsChecked = true;
2166
+ this.log("All preconditions met - executing main child");
2167
+ } else {
2168
+ this.log("Preconditions already verified - continuing child execution");
2169
+ }
2170
+ checkSignal(context.signal);
2171
+ const result = await this.child.tick(context);
2172
+ this._status = result;
2173
+ if (result !== "RUNNING" /* RUNNING */) {
2174
+ this.log("Child completed - resetting precondition check flag");
2175
+ this.preconditionsChecked = false;
2176
+ }
2177
+ return result;
2178
+ }
2179
+ onHalt() {
2180
+ this.log("Halting - resetting precondition check flag");
2181
+ this.preconditionsChecked = false;
2182
+ super.onHalt();
2183
+ }
2184
+ onReset() {
2185
+ this.log("Resetting - clearing precondition check flag");
2186
+ this.preconditionsChecked = false;
2187
+ }
2188
+ };
2189
+
2190
+ // src/decorators/repeat.ts
2191
+ var Repeat = class extends DecoratorNode {
2192
+ numCycles;
2193
+ currentCycle = 0;
2194
+ constructor(config) {
2195
+ super(config);
2196
+ this.numCycles = config.numCycles;
2197
+ }
2198
+ async executeTick(context) {
2199
+ checkSignal(context.signal);
2200
+ if (!this.child) {
2201
+ throw new ConfigurationError("Repeat requires a child");
2202
+ }
2203
+ this.log(`Repeat cycle ${this.currentCycle}/${this.numCycles}`);
2204
+ if (this.currentCycle >= this.numCycles) {
2205
+ this.log("All cycles completed");
2206
+ this._status = "SUCCESS" /* SUCCESS */;
2207
+ this.currentCycle = 0;
2208
+ return "SUCCESS" /* SUCCESS */;
2209
+ }
2210
+ const result = await this.child.tick(context);
2211
+ switch (result) {
2212
+ case "SUCCESS" /* SUCCESS */:
2213
+ this.log(`Cycle ${this.currentCycle} succeeded`);
2214
+ this.currentCycle++;
2215
+ if (this.currentCycle < this.numCycles) {
2216
+ this.child.reset();
2217
+ this._status = "RUNNING" /* RUNNING */;
2218
+ return "RUNNING" /* RUNNING */;
2219
+ } else {
2220
+ this._status = "SUCCESS" /* SUCCESS */;
2221
+ this.currentCycle = 0;
2222
+ return "SUCCESS" /* SUCCESS */;
2223
+ }
2224
+ case "FAILURE" /* FAILURE */:
2225
+ this.log(`Cycle ${this.currentCycle} failed`);
2226
+ this._status = "FAILURE" /* FAILURE */;
2227
+ this.currentCycle = 0;
2228
+ return "FAILURE" /* FAILURE */;
2229
+ case "RUNNING" /* RUNNING */:
2230
+ this.log(`Cycle ${this.currentCycle} is running`);
2231
+ this._status = "RUNNING" /* RUNNING */;
2232
+ return "RUNNING" /* RUNNING */;
2233
+ default:
2234
+ throw new Error(`Unexpected status from child: ${result}`);
2235
+ }
2236
+ }
2237
+ onReset() {
2238
+ super.onReset();
2239
+ this.currentCycle = 0;
2240
+ }
2241
+ onHalt() {
2242
+ super.onHalt();
2243
+ this.currentCycle = 0;
2244
+ }
2245
+ };
2246
+
2247
+ // src/decorators/run-once.ts
2248
+ var RunOnce = class extends DecoratorNode {
2249
+ hasRun = false;
2250
+ cachedResult;
2251
+ async executeTick(context) {
2252
+ checkSignal(context.signal);
2253
+ if (!this.child) {
2254
+ throw new ConfigurationError("RunOnce requires a child");
2255
+ }
2256
+ if (this.hasRun) {
2257
+ this.log(
2258
+ `Already executed, returning cached result: ${this.cachedResult}`
2259
+ );
2260
+ if (this.cachedResult === void 0) {
2261
+ this._status = "RUNNING" /* RUNNING */;
2262
+ return "RUNNING" /* RUNNING */;
2263
+ }
2264
+ this._status = this.cachedResult;
2265
+ return this.cachedResult;
2266
+ }
2267
+ this.log("First execution - ticking child");
2268
+ const result = await this.child.tick(context);
2269
+ if (result !== "RUNNING" /* RUNNING */) {
2270
+ this.hasRun = true;
2271
+ this.cachedResult = result;
2272
+ this.log(`Caching result: ${result}`);
2273
+ } else {
2274
+ this.log("Child is running - will retry on next tick");
2275
+ }
2276
+ this._status = result;
2277
+ return result;
2278
+ }
2279
+ onReset() {
2280
+ super.onReset();
2281
+ this.hasRun = false;
2282
+ this.cachedResult = void 0;
2283
+ }
2284
+ };
2285
+
2286
+ // src/decorators/soft-assert.ts
2287
+ var SoftAssert = class extends DecoratorNode {
2288
+ failures = [];
2289
+ async executeTick(context) {
2290
+ checkSignal(context.signal);
2291
+ if (!this.child) {
2292
+ throw new ConfigurationError("SoftAssert requires a child");
2293
+ }
2294
+ const result = await this.child.tick(context);
2295
+ if (result === "FAILURE" /* FAILURE */) {
2296
+ const failure = {
2297
+ timestamp: Date.now(),
2298
+ message: `Soft assertion failed: ${this.child.name}`
2299
+ };
2300
+ this.failures.push(failure);
2301
+ this.log(`Soft assertion failed (continuing): ${this.child.name}`);
2302
+ this._status = "SUCCESS" /* SUCCESS */;
2303
+ return "SUCCESS" /* SUCCESS */;
2304
+ }
2305
+ this._status = result;
2306
+ return result;
2307
+ }
2308
+ /**
2309
+ * Get all recorded failures
2310
+ */
2311
+ getFailures() {
2312
+ return [...this.failures];
2313
+ }
2314
+ /**
2315
+ * Check if any assertions have failed
2316
+ */
2317
+ hasFailures() {
2318
+ return this.failures.length > 0;
2319
+ }
2320
+ onReset() {
2321
+ super.onReset();
2322
+ this.failures = [];
2323
+ }
2324
+ };
2325
+
2326
+ // src/decorators/timeout.ts
2327
+ var import_workflow2 = require("@temporalio/workflow");
2328
+ var Timeout = class extends DecoratorNode {
2329
+ timeoutMs;
2330
+ startTime = null;
2331
+ useTemporalAPI = null;
2332
+ // Cached detection result
2333
+ constructor(config) {
2334
+ super(config);
2335
+ this.timeoutMs = config.timeoutMs;
2336
+ if (this.timeoutMs <= 0) {
2337
+ throw new ConfigurationError(
2338
+ `${this.name}: Timeout must be positive (got ${this.timeoutMs})`
2339
+ );
2340
+ }
2341
+ }
2342
+ async executeTick(context) {
2343
+ checkSignal(context.signal);
2344
+ if (!this.child) {
2345
+ throw new ConfigurationError(`${this.name}: Decorator must have a child`);
2346
+ }
2347
+ if (this.useTemporalAPI === null) {
2348
+ try {
2349
+ this.log(`Starting timeout for ${this.timeoutMs}ms`);
2350
+ const childStatus2 = await import_workflow2.CancellationScope.withTimeout(
2351
+ this.timeoutMs,
2352
+ async () => {
2353
+ return await this.child.tick(context);
2354
+ }
2355
+ );
2356
+ this.useTemporalAPI = true;
2357
+ this._status = childStatus2;
2358
+ this.log(`Child completed with ${childStatus2}`);
2359
+ return childStatus2;
2360
+ } catch (err) {
2361
+ if ((0, import_workflow2.isCancellation)(err)) {
2362
+ this.useTemporalAPI = true;
2363
+ this.log(`Timeout after ${this.timeoutMs}ms`);
2364
+ if (this.child.status() === "RUNNING" /* RUNNING */) {
2365
+ this.child.halt();
2366
+ }
2367
+ this._status = "FAILURE" /* FAILURE */;
2368
+ return "FAILURE" /* FAILURE */;
2369
+ }
2370
+ this.useTemporalAPI = false;
2371
+ }
2372
+ }
2373
+ if (this.useTemporalAPI === true) {
2374
+ try {
2375
+ this.log(`Starting timeout for ${this.timeoutMs}ms`);
2376
+ const childStatus2 = await import_workflow2.CancellationScope.withTimeout(
2377
+ this.timeoutMs,
2378
+ async () => {
2379
+ return await this.child.tick(context);
2380
+ }
2381
+ );
2382
+ this._status = childStatus2;
2383
+ this.log(`Child completed with ${childStatus2}`);
2384
+ return childStatus2;
2385
+ } catch (err) {
2386
+ if ((0, import_workflow2.isCancellation)(err)) {
2387
+ this.log(`Timeout after ${this.timeoutMs}ms`);
2388
+ if (this.child.status() === "RUNNING" /* RUNNING */) {
2389
+ this.child.halt();
2390
+ }
2391
+ this._status = "FAILURE" /* FAILURE */;
2392
+ return "FAILURE" /* FAILURE */;
2393
+ }
2394
+ throw err;
2395
+ }
2396
+ }
2397
+ if (this.startTime === null) {
2398
+ this.startTime = Date.now();
2399
+ this.log(`Starting timeout for ${this.timeoutMs}ms`);
2400
+ }
2401
+ const elapsed = Date.now() - this.startTime;
2402
+ if (elapsed >= this.timeoutMs) {
2403
+ this.log(`Timeout after ${this.timeoutMs}ms`);
2404
+ this.startTime = null;
2405
+ if (this.child.status() === "RUNNING" /* RUNNING */) {
2406
+ this.child.halt();
2407
+ }
2408
+ this._status = "FAILURE" /* FAILURE */;
2409
+ return "FAILURE" /* FAILURE */;
2410
+ }
2411
+ const childStatus = await this.child.tick(context);
2412
+ if (childStatus !== "RUNNING" /* RUNNING */) {
2413
+ this.startTime = null;
2414
+ }
2415
+ this._status = childStatus;
2416
+ return childStatus;
2417
+ }
2418
+ onHalt() {
2419
+ this.startTime = null;
2420
+ if (this.child && this.child.status() === "RUNNING" /* RUNNING */) {
2421
+ this.child.halt();
2422
+ }
2423
+ }
2424
+ onReset() {
2425
+ this.startTime = null;
2426
+ this.useTemporalAPI = null;
2427
+ }
2428
+ };
2429
+
2430
+ // src/schemas/base.schema.ts
2431
+ var import_zod = require("zod");
2432
+ var nodeConfigurationSchema = import_zod.z.object({
2433
+ id: import_zod.z.string().min(1, "Node ID cannot be empty"),
2434
+ name: import_zod.z.string().optional()
2435
+ }).passthrough();
2436
+ function createNodeSchema(nodeType, fields) {
2437
+ return nodeConfigurationSchema.extend(fields).describe(`${nodeType} configuration`);
2438
+ }
2439
+ var validations = {
2440
+ /**
2441
+ * Positive number validator (> 0)
2442
+ */
2443
+ positiveNumber: (fieldName) => import_zod.z.number().positive(`${fieldName} must be positive`),
2444
+ /**
2445
+ * Non-negative number validator (>= 0)
2446
+ */
2447
+ nonNegativeNumber: (fieldName) => import_zod.z.number().nonnegative(`${fieldName} must be non-negative`),
2448
+ /**
2449
+ * Positive integer validator (> 0, whole number)
2450
+ */
2451
+ positiveInteger: (fieldName) => import_zod.z.number().int().positive(`${fieldName} must be a positive integer`),
2452
+ /**
2453
+ * Non-negative integer validator (>= 0, whole number)
2454
+ */
2455
+ nonNegativeInteger: (fieldName) => import_zod.z.number().int().nonnegative(`${fieldName} must be a non-negative integer`),
2456
+ /**
2457
+ * Blackboard key validator (non-empty string)
2458
+ */
2459
+ blackboardKey: import_zod.z.string().min(1, "Blackboard key cannot be empty"),
2460
+ /**
2461
+ * Tree ID validator (non-empty string for SubTree references)
2462
+ */
2463
+ treeId: import_zod.z.string().min(1, "Tree ID cannot be empty"),
2464
+ /**
2465
+ * Duration in milliseconds validator (non-negative)
2466
+ */
2467
+ durationMs: (fieldName = "duration") => import_zod.z.number().nonnegative(`${fieldName} must be non-negative milliseconds`)
2468
+ };
2469
+
2470
+ // src/decorators/timeout.schema.ts
2471
+ var timeoutConfigurationSchema = createNodeSchema("Timeout", {
2472
+ timeoutMs: validations.positiveNumber("timeoutMs")
2473
+ });
2474
+
2475
+ // src/decorators/delay.schema.ts
2476
+ var delayConfigurationSchema = createNodeSchema("Delay", {
2477
+ delayMs: validations.nonNegativeNumber("delayMs")
2478
+ });
2479
+
2480
+ // src/decorators/repeat.schema.ts
2481
+ var repeatConfigurationSchema = createNodeSchema("Repeat", {
2482
+ numCycles: validations.positiveInteger("numCycles")
2483
+ });
2484
+
2485
+ // src/decorators/invert.schema.ts
2486
+ var invertConfigurationSchema = nodeConfigurationSchema;
2487
+
2488
+ // src/decorators/force-result.schema.ts
2489
+ var forceSuccessConfigurationSchema = nodeConfigurationSchema;
2490
+ var forceFailureConfigurationSchema = nodeConfigurationSchema;
2491
+
2492
+ // src/decorators/precondition.schema.ts
2493
+ var preconditionConfigurationSchema = nodeConfigurationSchema;
2494
+
2495
+ // src/decorators/run-once.schema.ts
2496
+ var runOnceConfigurationSchema = nodeConfigurationSchema;
2497
+
2498
+ // src/decorators/keep-running.schema.ts
2499
+ var keepRunningUntilFailureConfigurationSchema = nodeConfigurationSchema;
2500
+
2501
+ // src/decorators/soft-assert.schema.ts
2502
+ var softAssertConfigurationSchema = nodeConfigurationSchema;
2503
+
2504
+ // src/composites/parallel.schema.ts
2505
+ var import_zod2 = require("zod");
2506
+ var parallelStrategySchema = import_zod2.z.enum(["strict", "any"]);
2507
+ var parallelConfigurationSchema = createNodeSchema("Parallel", {
2508
+ strategy: parallelStrategySchema.optional().default("strict"),
2509
+ successThreshold: validations.positiveInteger("successThreshold").optional(),
2510
+ failureThreshold: validations.positiveInteger("failureThreshold").optional()
2511
+ });
2512
+
2513
+ // src/composites/for-each.schema.ts
2514
+ var forEachConfigurationSchema = createNodeSchema("ForEach", {
2515
+ collectionKey: validations.blackboardKey,
2516
+ itemKey: validations.blackboardKey,
2517
+ indexKey: validations.blackboardKey.optional()
2518
+ });
2519
+
2520
+ // src/composites/while.schema.ts
2521
+ var whileConfigurationSchema = createNodeSchema("While", {
2522
+ maxIterations: validations.positiveInteger("maxIterations").optional().default(1e3)
2523
+ });
2524
+
2525
+ // src/composites/sub-tree.schema.ts
2526
+ var subTreeConfigurationSchema = createNodeSchema("SubTree", {
2527
+ treeId: validations.treeId
2528
+ });
2529
+
2530
+ // src/composites/sequence.schema.ts
2531
+ var sequenceConfigurationSchema = nodeConfigurationSchema;
2532
+
2533
+ // src/composites/selector.schema.ts
2534
+ var selectorConfigurationSchema = nodeConfigurationSchema;
2535
+
2536
+ // src/composites/conditional.schema.ts
2537
+ var conditionalConfigurationSchema = nodeConfigurationSchema;
2538
+
2539
+ // src/composites/reactive-sequence.schema.ts
2540
+ var reactiveSequenceConfigurationSchema = nodeConfigurationSchema;
2541
+
2542
+ // src/composites/memory-sequence.schema.ts
2543
+ var memorySequenceConfigurationSchema = nodeConfigurationSchema;
2544
+
2545
+ // src/composites/recovery.schema.ts
2546
+ var recoveryConfigurationSchema = nodeConfigurationSchema;
2547
+
2548
+ // src/schemas/validation.ts
2549
+ var import_zod3 = require("zod");
2550
+ function zodErrorToConfigurationError(error, nodeType, nodeId) {
2551
+ const nodeIdentifier = nodeId ? `${nodeType}:${nodeId}` : nodeType;
2552
+ const issues = error.issues.map((issue) => {
2553
+ const path2 = issue.path.join(".");
2554
+ return ` - ${path2 ? path2 + ": " : ""}${issue.message}`;
2555
+ }).join("\n");
2556
+ const message = `Invalid configuration for ${nodeIdentifier}:
2557
+ ${issues}`;
2558
+ const hint = "Check the node configuration and ensure all required fields are provided with valid values.";
2559
+ return new ConfigurationError(message, hint);
2560
+ }
2561
+ function validateConfiguration(schema, config, nodeType, nodeId) {
2562
+ try {
2563
+ return schema.parse(config);
2564
+ } catch (error) {
2565
+ if (error instanceof import_zod3.z.ZodError) {
2566
+ throw zodErrorToConfigurationError(error, nodeType, nodeId);
2567
+ }
2568
+ throw error;
2569
+ }
2570
+ }
2571
+ function safeValidateConfiguration(schema, config, nodeType, nodeId) {
2572
+ const result = schema.safeParse(config);
2573
+ if (result.success) {
2574
+ return { success: true, data: result.data };
2575
+ } else {
2576
+ return {
2577
+ success: false,
2578
+ error: zodErrorToConfigurationError(result.error, nodeType, nodeId)
2579
+ };
2580
+ }
2581
+ }
2582
+
2583
+ // src/schemas/tree-definition.schema.ts
2584
+ var import_zod4 = require("zod");
2585
+ var treeDefSchemaObject = import_zod4.z.object({
2586
+ type: import_zod4.z.string().min(1, "Node type is required"),
2587
+ id: import_zod4.z.string().optional(),
2588
+ name: import_zod4.z.string().optional(),
2589
+ props: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional(),
2590
+ children: import_zod4.z.array(
2591
+ import_zod4.z.lazy(() => treeDefinitionSchema)
2592
+ ).optional()
2593
+ });
2594
+ var treeDefinitionSchema = treeDefSchemaObject;
2595
+ function validateTreeDefinition(definition) {
2596
+ return treeDefinitionSchema.parse(definition);
2597
+ }
2598
+ function validateDecoratorChildren(nodeType, children) {
2599
+ const childCount = children?.length || 0;
2600
+ if (childCount !== 1) {
2601
+ throw new Error(
2602
+ `Decorator ${nodeType} must have exactly one child (got ${childCount})`
2603
+ );
2604
+ }
2605
+ }
2606
+ function validateCompositeChildren(nodeType, children, minChildren = 0) {
2607
+ const count = children?.length || 0;
2608
+ if (count < minChildren) {
2609
+ throw new Error(
2610
+ `Composite ${nodeType} requires at least ${minChildren} children (got ${count})`
2611
+ );
2612
+ }
2613
+ }
2614
+ function validateChildCount(nodeType, children, expectedCount = 0) {
2615
+ const count = children?.length || 0;
2616
+ if (count !== expectedCount) {
2617
+ throw new Error(
2618
+ `${nodeType} requires exactly ${expectedCount} children (got ${count})`
2619
+ );
2620
+ }
2621
+ }
2622
+ function validateChildCountRange(nodeType, children, minChildren = 0, maxChildren) {
2623
+ const count = children?.length || 0;
2624
+ if (count < minChildren) {
2625
+ throw new Error(
2626
+ `${nodeType} requires at least ${minChildren} children (got ${count})`
2627
+ );
2628
+ }
2629
+ if (maxChildren !== void 0 && count > maxChildren) {
2630
+ throw new Error(
2631
+ `${nodeType} allows at most ${maxChildren} children (got ${count})`
2632
+ );
2633
+ }
2634
+ }
2635
+
2636
+ // src/schemas/index.ts
2637
+ var SchemaRegistry = class {
2638
+ schemas = /* @__PURE__ */ new Map();
2639
+ constructor() {
2640
+ this.registerAllSchemas();
2641
+ }
2642
+ /**
2643
+ * Register all node schemas
2644
+ * Called automatically on construction
2645
+ */
2646
+ registerAllSchemas() {
2647
+ this.register("Timeout", timeoutConfigurationSchema);
2648
+ this.register("Delay", delayConfigurationSchema);
2649
+ this.register("Repeat", repeatConfigurationSchema);
2650
+ this.register("Invert", invertConfigurationSchema);
2651
+ this.register("ForceSuccess", forceSuccessConfigurationSchema);
2652
+ this.register("ForceFailure", forceFailureConfigurationSchema);
2653
+ this.register("Precondition", preconditionConfigurationSchema);
2654
+ this.register("RunOnce", runOnceConfigurationSchema);
2655
+ this.register(
2656
+ "KeepRunningUntilFailure",
2657
+ keepRunningUntilFailureConfigurationSchema
2658
+ );
2659
+ this.register("SoftAssert", softAssertConfigurationSchema);
2660
+ this.register("Parallel", parallelConfigurationSchema);
2661
+ this.register("ForEach", forEachConfigurationSchema);
2662
+ this.register("While", whileConfigurationSchema);
2663
+ this.register("SubTree", subTreeConfigurationSchema);
2664
+ this.register("Sequence", sequenceConfigurationSchema);
2665
+ this.register("Selector", selectorConfigurationSchema);
2666
+ this.register("Conditional", conditionalConfigurationSchema);
2667
+ this.register("ReactiveSequence", reactiveSequenceConfigurationSchema);
2668
+ this.register("MemorySequence", memorySequenceConfigurationSchema);
2669
+ this.register("Recovery", recoveryConfigurationSchema);
2670
+ }
2671
+ /**
2672
+ * Register a validation schema for a node type
2673
+ *
2674
+ * @param nodeType - Name of the node type (e.g., 'Timeout', 'Sequence')
2675
+ * @param schema - Zod schema for validating this node type's configuration
2676
+ * @throws Error if node type is already registered
2677
+ *
2678
+ * @example
2679
+ * ```typescript
2680
+ * schemaRegistry.register('Timeout', timeoutConfigurationSchema);
2681
+ * ```
2682
+ */
2683
+ register(nodeType, schema) {
2684
+ if (this.schemas.has(nodeType)) {
2685
+ throw new Error(`Schema for node type '${nodeType}' already registered`);
2686
+ }
2687
+ this.schemas.set(nodeType, schema);
2688
+ }
2689
+ /**
2690
+ * Get schema for a node type
2691
+ * Returns base schema if no specific schema registered
2692
+ *
2693
+ * @param nodeType - Name of the node type
2694
+ * @returns Zod schema for the node type (or base schema if not found)
2695
+ *
2696
+ * @example
2697
+ * ```typescript
2698
+ * const schema = schemaRegistry.getSchema('Timeout');
2699
+ * const validated = schema.parse({ id: 'test', timeoutMs: 1000 });
2700
+ * ```
2701
+ */
2702
+ getSchema(nodeType) {
2703
+ return this.schemas.get(nodeType) || nodeConfigurationSchema;
2704
+ }
2705
+ /**
2706
+ * Check if a schema is registered for a node type
2707
+ *
2708
+ * @param nodeType - Name of the node type
2709
+ * @returns True if schema is registered, false otherwise
2710
+ */
2711
+ hasSchema(nodeType) {
2712
+ return this.schemas.has(nodeType);
2713
+ }
2714
+ /**
2715
+ * Get all registered node types
2716
+ *
2717
+ * @returns Array of registered node type names
2718
+ */
2719
+ getRegisteredTypes() {
2720
+ return Array.from(this.schemas.keys());
2721
+ }
2722
+ /**
2723
+ * Validate configuration for a specific node type
2724
+ * Throws ZodError if validation fails
2725
+ *
2726
+ * @param nodeType - Name of the node type
2727
+ * @param config - Configuration object to validate
2728
+ * @returns Validated and parsed configuration
2729
+ * @throws ZodError if validation fails
2730
+ *
2731
+ * @example
2732
+ * ```typescript
2733
+ * const config = schemaRegistry.validate('Timeout', {
2734
+ * id: 'test',
2735
+ * timeoutMs: 1000
2736
+ * });
2737
+ * ```
2738
+ */
2739
+ validate(nodeType, config) {
2740
+ const schema = this.getSchema(nodeType);
2741
+ return schema.parse(config);
2742
+ }
2743
+ /**
2744
+ * Safe validation that returns success/error result
2745
+ * Does not throw errors
2746
+ *
2747
+ * @param nodeType - Name of the node type
2748
+ * @param config - Configuration object to validate
2749
+ * @returns Result with success/error
2750
+ *
2751
+ * @example
2752
+ * ```typescript
2753
+ * const result = schemaRegistry.safeParse('Timeout', { id: 'test', timeoutMs: -100 });
2754
+ * if (result.success) {
2755
+ * console.log(result.data);
2756
+ * } else {
2757
+ * console.error(result.error);
2758
+ * }
2759
+ * ```
2760
+ */
2761
+ safeParse(nodeType, config) {
2762
+ const schema = this.getSchema(nodeType);
2763
+ return schema.safeParse(config);
2764
+ }
2765
+ /**
2766
+ * Clear all registered schemas
2767
+ * Useful for testing
2768
+ */
2769
+ clear() {
2770
+ this.schemas.clear();
2771
+ }
2772
+ };
2773
+ var schemaRegistry = new SchemaRegistry();
2774
+
2775
+ // src/registry.ts
2776
+ var Registry = class {
2777
+ nodeTypes = /* @__PURE__ */ new Map();
2778
+ nodeMetadata = /* @__PURE__ */ new Map();
2779
+ behaviorTrees = /* @__PURE__ */ new Map();
2780
+ constructor() {
2781
+ this.log("Registry created");
2782
+ }
2783
+ /**
2784
+ * Register a node type with the registry
2785
+ */
2786
+ register(type, ctor, metadata) {
2787
+ if (this.nodeTypes.has(type)) {
2788
+ throw new Error(`Node type '${type}' is already registered`);
2789
+ }
2790
+ this.nodeTypes.set(type, ctor);
2791
+ const fullMetadata = {
2792
+ type,
2793
+ category: metadata?.category || "action",
2794
+ description: metadata?.description,
2795
+ ports: metadata?.ports || []
2796
+ };
2797
+ this.nodeMetadata.set(type, fullMetadata);
2798
+ this.log(`Registered node type: ${type} (${fullMetadata.category})`);
2799
+ }
2800
+ /**
2801
+ * Create a node instance by type
2802
+ * Validates configuration against schema before creating node
2803
+ */
2804
+ create(type, config) {
2805
+ const Constructor = this.nodeTypes.get(type);
2806
+ if (!Constructor) {
2807
+ throw new Error(
2808
+ `Unknown node type: '${type}'. Available types: ${this.getRegisteredTypes().join(", ")}`
2809
+ );
2810
+ }
2811
+ const validatedConfig = validateConfiguration(
2812
+ schemaRegistry.getSchema(type),
2813
+ config,
2814
+ type,
2815
+ config.id
2816
+ );
2817
+ this.log(`Creating node of type: ${type} with id: ${validatedConfig.id}`);
2818
+ return new Constructor(validatedConfig);
2819
+ }
2820
+ /**
2821
+ * Check if a node type is registered
2822
+ */
2823
+ has(type) {
2824
+ return this.nodeTypes.has(type);
2825
+ }
2826
+ /**
2827
+ * Get metadata for a node type
2828
+ */
2829
+ getMetadata(type) {
2830
+ return this.nodeMetadata.get(type);
2831
+ }
2832
+ /**
2833
+ * Get all registered node types
2834
+ */
2835
+ getRegisteredTypes() {
2836
+ return Array.from(this.nodeTypes.keys());
2837
+ }
2838
+ /**
2839
+ * Get registered types by category
2840
+ */
2841
+ getTypesByCategory(category) {
2842
+ const types = [];
2843
+ for (const [type, metadata] of this.nodeMetadata) {
2844
+ if (metadata.category === category) {
2845
+ types.push(type);
2846
+ }
2847
+ }
2848
+ return types;
2849
+ }
2850
+ /**
2851
+ * Clear all registrations (node types, metadata, and behavior trees)
2852
+ */
2853
+ clear() {
2854
+ this.nodeTypes.clear();
2855
+ this.nodeMetadata.clear();
2856
+ this.behaviorTrees.clear();
2857
+ this.log("Registry cleared (nodes and trees)");
2858
+ }
2859
+ /**
2860
+ * Register a behavior tree instance with source file metadata.
2861
+ * Used for reusable trees like elements, step groups, and test cases.
2862
+ *
2863
+ * @param id Unique identifier for the tree
2864
+ * @param tree BehaviorTree instance to register
2865
+ * @param sourceFile Path to the source .sigma file
2866
+ * @throws Error if a tree with the same ID is already registered
2867
+ */
2868
+ registerTree(id, tree, sourceFile) {
2869
+ if (this.behaviorTrees.has(id)) {
2870
+ throw new Error(`Behavior tree '${id}' is already registered`);
2871
+ }
2872
+ this.behaviorTrees.set(id, { tree, sourceFile });
2873
+ this.log(`Registered behavior tree: ${id}`);
2874
+ }
2875
+ /**
2876
+ * Unregister a behavior tree by ID.
2877
+ * Useful for cleanup in long-running processes or tests.
2878
+ *
2879
+ * @param id Tree ID to unregister
2880
+ * @returns true if tree was found and removed, false otherwise
2881
+ */
2882
+ unregisterTree(id) {
2883
+ const deleted = this.behaviorTrees.delete(id);
2884
+ if (deleted) {
2885
+ this.log(`Unregistered behavior tree: ${id}`);
2886
+ }
2887
+ return deleted;
2888
+ }
2889
+ /**
2890
+ * Replace an existing behavior tree registration.
2891
+ * If the tree doesn't exist, it will be registered as new.
2892
+ *
2893
+ * @param id Tree ID to replace
2894
+ * @param tree New BehaviorTree instance
2895
+ * @param sourceFile Path to the source .sigma file
2896
+ */
2897
+ replaceTree(id, tree, sourceFile) {
2898
+ const existed = this.behaviorTrees.has(id);
2899
+ this.behaviorTrees.set(id, { tree, sourceFile });
2900
+ this.log(`${existed ? "Replaced" : "Registered"} behavior tree: ${id}`);
2901
+ }
2902
+ /**
2903
+ * Get a behavior tree instance by ID
2904
+ */
2905
+ getTree(id) {
2906
+ return this.behaviorTrees.get(id)?.tree;
2907
+ }
2908
+ /**
2909
+ * Get the source file path for a behavior tree
2910
+ */
2911
+ getTreeSourceFile(id) {
2912
+ return this.behaviorTrees.get(id)?.sourceFile;
2913
+ }
2914
+ /**
2915
+ * Get all trees that belong to a specific source file
2916
+ */
2917
+ getTreesForFile(filePath) {
2918
+ const result = /* @__PURE__ */ new Map();
2919
+ for (const [id, entry] of this.behaviorTrees) {
2920
+ if (entry.sourceFile === filePath) {
2921
+ result.set(id, entry.tree);
2922
+ }
2923
+ }
2924
+ return result;
2925
+ }
2926
+ /**
2927
+ * Check if a behavior tree is registered
2928
+ */
2929
+ hasTree(id) {
2930
+ return this.behaviorTrees.has(id);
2931
+ }
2932
+ /**
2933
+ * Clone a registered behavior tree for instantiation
2934
+ * Returns the cloned BehaviorTree (caller should use getRoot() for TreeNode)
2935
+ */
2936
+ cloneTree(id) {
2937
+ const entry = this.behaviorTrees.get(id);
2938
+ if (!entry) {
2939
+ throw new Error(
2940
+ `Behavior tree '${id}' not found. Available trees: ${this.getAllTreeIds().join(", ") || "none"}`
2941
+ );
2942
+ }
2943
+ this.log(`Cloning behavior tree: ${id}`);
2944
+ return entry.tree.clone();
2945
+ }
2946
+ /**
2947
+ * Get all registered behavior tree IDs
2948
+ */
2949
+ getAllTreeIds() {
2950
+ return Array.from(this.behaviorTrees.keys());
2951
+ }
2952
+ /**
2953
+ * Clear all registered behavior trees
2954
+ */
2955
+ clearTrees() {
2956
+ this.behaviorTrees.clear();
2957
+ this.log("Cleared all behavior trees");
2958
+ }
2959
+ /**
2960
+ * Create a tree from a JSON definition
2961
+ * Validates tree structure before creating nodes
2962
+ */
2963
+ createTree(definition) {
2964
+ const validatedDef = treeDefinitionSchema.parse(definition);
2965
+ if (!validatedDef.type) {
2966
+ throw new Error("Node definition must have a type");
2967
+ }
2968
+ const config = {
2969
+ id: validatedDef.id || `${validatedDef.type}_${Date.now()}`,
2970
+ name: validatedDef.name || validatedDef.id,
2971
+ ...validatedDef.props
2972
+ };
2973
+ const node = this.create(validatedDef.type, config);
2974
+ if (validatedDef.children && Array.isArray(validatedDef.children)) {
2975
+ if ("setChild" in node && typeof node.setChild === "function") {
2976
+ if (validatedDef.children.length !== 1) {
2977
+ throw new Error(
2978
+ `Decorator ${validatedDef.type} must have exactly one child`
2979
+ );
2980
+ }
2981
+ const child = this.createTree(validatedDef.children[0]);
2982
+ node.setChild(child);
2983
+ } else if ("addChildren" in node && typeof node.addChildren === "function") {
2984
+ const children = validatedDef.children.map(
2985
+ (childDef) => this.createTree(childDef)
2986
+ );
2987
+ node.addChildren(
2988
+ children
2989
+ );
2990
+ }
2991
+ }
2992
+ return node;
2993
+ }
2994
+ /**
2995
+ * Safe tree creation that returns success/error result
2996
+ * Useful for user-facing tools that need graceful error handling
2997
+ *
2998
+ * @param definition - Tree definition to create
2999
+ * @returns Success result with tree or failure result with error
3000
+ *
3001
+ * @example
3002
+ * ```typescript
3003
+ * const result = registry.safeCreateTree({
3004
+ * type: 'Sequence',
3005
+ * id: 'root',
3006
+ * children: [...]
3007
+ * });
3008
+ *
3009
+ * if (result.success) {
3010
+ * console.log('Tree created:', result.tree);
3011
+ * } else {
3012
+ * console.error('Failed:', result.error.message);
3013
+ * }
3014
+ * ```
3015
+ */
3016
+ safeCreateTree(definition) {
3017
+ try {
3018
+ const tree = this.createTree(definition);
3019
+ return { success: true, tree };
3020
+ } catch (error) {
3021
+ return {
3022
+ success: false,
3023
+ error: error instanceof Error ? error : new Error(String(error))
3024
+ };
3025
+ }
3026
+ }
3027
+ log(message) {
3028
+ console.log(`[Registry] ${message}`);
3029
+ }
3030
+ };
3031
+
3032
+ // src/test-nodes.ts
3033
+ var PrintAction = class extends ActionNode {
3034
+ message;
3035
+ constructor(config) {
3036
+ super(config);
3037
+ this.message = config.message || "Hello from PrintAction!";
3038
+ }
3039
+ async executeTick(context) {
3040
+ this.log(`Executing: "${this.message}"`);
3041
+ if (this.config.outputKey && typeof this.config.outputKey === "string") {
3042
+ context.blackboard.set(this.config.outputKey, this.message);
3043
+ }
3044
+ this._status = "SUCCESS" /* SUCCESS */;
3045
+ return "SUCCESS" /* SUCCESS */;
3046
+ }
3047
+ };
3048
+ var WaitAction = class extends ActionNode {
3049
+ waitMs;
3050
+ startTime = null;
3051
+ constructor(config) {
3052
+ super(config);
3053
+ this.waitMs = config.waitMs || 1e3;
3054
+ }
3055
+ async executeTick(_context) {
3056
+ if (this.startTime === null) {
3057
+ this.startTime = Date.now();
3058
+ this.log(`Starting wait for ${this.waitMs}ms`);
3059
+ this._status = "RUNNING" /* RUNNING */;
3060
+ return "RUNNING" /* RUNNING */;
3061
+ }
3062
+ const elapsed = Date.now() - this.startTime;
3063
+ if (elapsed < this.waitMs) {
3064
+ this.log(`Waiting... ${this.waitMs - elapsed}ms remaining`);
3065
+ this._status = "RUNNING" /* RUNNING */;
3066
+ return "RUNNING" /* RUNNING */;
3067
+ }
3068
+ this.log(`Wait completed after ${elapsed}ms`);
3069
+ this.startTime = null;
3070
+ this._status = "SUCCESS" /* SUCCESS */;
3071
+ return "SUCCESS" /* SUCCESS */;
3072
+ }
3073
+ onReset() {
3074
+ this.startTime = null;
3075
+ }
3076
+ onHalt() {
3077
+ this.startTime = null;
3078
+ }
3079
+ };
3080
+ var CounterAction = class extends ActionNode {
3081
+ counterKey;
3082
+ increment;
3083
+ constructor(config) {
3084
+ super(config);
3085
+ this.counterKey = config.counterKey || "counter";
3086
+ this.increment = config.increment || 1;
3087
+ }
3088
+ async executeTick(context) {
3089
+ const currentValue = context.blackboard.get(this.counterKey) || 0;
3090
+ const newValue = currentValue + this.increment;
3091
+ context.blackboard.set(this.counterKey, newValue);
3092
+ this.log(
3093
+ `Counter '${this.counterKey}' incremented from ${currentValue} to ${newValue}`
3094
+ );
3095
+ this._status = "SUCCESS" /* SUCCESS */;
3096
+ return "SUCCESS" /* SUCCESS */;
3097
+ }
3098
+ };
3099
+ var MockAction = class extends ActionNode {
3100
+ returnStatus;
3101
+ ticksBeforeComplete;
3102
+ currentTicks = 0;
3103
+ constructor(config) {
3104
+ super(config);
3105
+ this.returnStatus = config.returnStatus || "SUCCESS" /* SUCCESS */;
3106
+ this.ticksBeforeComplete = config.ticksBeforeComplete || 1;
3107
+ }
3108
+ async executeTick(_context) {
3109
+ this.currentTicks++;
3110
+ if (this.currentTicks < this.ticksBeforeComplete) {
3111
+ this.log(
3112
+ `Running... (tick ${this.currentTicks}/${this.ticksBeforeComplete})`
3113
+ );
3114
+ this._status = "RUNNING" /* RUNNING */;
3115
+ return "RUNNING" /* RUNNING */;
3116
+ }
3117
+ this.log(`Completing with status: ${this.returnStatus}`);
3118
+ this.currentTicks = 0;
3119
+ this._status = this.returnStatus;
3120
+ return this.returnStatus;
3121
+ }
3122
+ onReset() {
3123
+ this.currentTicks = 0;
3124
+ }
3125
+ onHalt() {
3126
+ this.currentTicks = 0;
3127
+ }
3128
+ };
3129
+ var CheckCondition = class extends ConditionNode {
3130
+ key;
3131
+ operator;
3132
+ value;
3133
+ constructor(config) {
3134
+ super(config);
3135
+ this.key = config.key;
3136
+ this.operator = config.operator || "==";
3137
+ this.value = config.value;
3138
+ }
3139
+ async executeTick(context) {
3140
+ const actualValue = context.blackboard.get(this.key);
3141
+ let result = false;
3142
+ switch (this.operator) {
3143
+ case "==":
3144
+ result = actualValue === this.value;
3145
+ break;
3146
+ case "!=":
3147
+ result = actualValue !== this.value;
3148
+ break;
3149
+ case ">":
3150
+ result = actualValue > this.value;
3151
+ break;
3152
+ case "<":
3153
+ result = actualValue < this.value;
3154
+ break;
3155
+ case ">=":
3156
+ result = actualValue >= this.value;
3157
+ break;
3158
+ case "<=":
3159
+ result = actualValue <= this.value;
3160
+ break;
3161
+ }
3162
+ this.log(
3163
+ `Checking: ${this.key} ${this.operator} ${this.value} => ${actualValue} ${this.operator} ${this.value} = ${result}`
3164
+ );
3165
+ this._status = result ? "SUCCESS" /* SUCCESS */ : "FAILURE" /* FAILURE */;
3166
+ return this._status;
3167
+ }
3168
+ };
3169
+ var AlwaysCondition = class extends ConditionNode {
3170
+ returnStatus;
3171
+ constructor(config) {
3172
+ super(config);
3173
+ this.returnStatus = config.returnStatus || "SUCCESS" /* SUCCESS */;
3174
+ }
3175
+ async executeTick(_context) {
3176
+ this.log(`Returning ${this.returnStatus}`);
3177
+ this._status = this.returnStatus;
3178
+ return this.returnStatus;
3179
+ }
3180
+ };
3181
+ var SuccessNode = class extends ActionNode {
3182
+ async executeTick(_context) {
3183
+ this.log("Executing (SUCCESS)");
3184
+ this._status = "SUCCESS" /* SUCCESS */;
3185
+ return "SUCCESS" /* SUCCESS */;
3186
+ }
3187
+ };
3188
+ var FailureNode = class extends ActionNode {
3189
+ async executeTick(_context) {
3190
+ this.log("Executing (FAILURE)");
3191
+ this._status = "FAILURE" /* FAILURE */;
3192
+ return "FAILURE" /* FAILURE */;
3193
+ }
3194
+ };
3195
+ var RunningNode = class extends ActionNode {
3196
+ async executeTick(_context) {
3197
+ this.log("Executing (RUNNING)");
3198
+ this._status = "RUNNING" /* RUNNING */;
3199
+ return "RUNNING" /* RUNNING */;
3200
+ }
3201
+ };
3202
+
3203
+ // src/utilities/log-message.ts
3204
+ var LogMessage = class extends ActionNode {
3205
+ message;
3206
+ level;
3207
+ constructor(config) {
3208
+ super(config);
3209
+ this.message = config.message;
3210
+ this.level = config.level || "info";
3211
+ }
3212
+ async executeTick(context) {
3213
+ try {
3214
+ const resolvedMessage = this.resolveMessage(this.message, context);
3215
+ switch (this.level) {
3216
+ case "warn":
3217
+ console.warn(`[LogMessage:${this.name}] ${resolvedMessage}`);
3218
+ break;
3219
+ case "error":
3220
+ console.error(`[LogMessage:${this.name}] ${resolvedMessage}`);
3221
+ break;
3222
+ case "debug":
3223
+ console.debug(`[LogMessage:${this.name}] ${resolvedMessage}`);
3224
+ break;
3225
+ default:
3226
+ console.log(`[LogMessage:${this.name}] ${resolvedMessage}`);
3227
+ break;
3228
+ }
3229
+ context.eventEmitter?.emit({
3230
+ type: "log" /* LOG */,
3231
+ nodeId: this.id,
3232
+ nodeName: this.name,
3233
+ nodeType: this.type,
3234
+ timestamp: Date.now(),
3235
+ data: { level: this.level, message: resolvedMessage }
3236
+ });
3237
+ this._status = "SUCCESS" /* SUCCESS */;
3238
+ return "SUCCESS" /* SUCCESS */;
3239
+ } catch (error) {
3240
+ const errorMessage = error instanceof Error ? error.message : String(error);
3241
+ console.error(
3242
+ `[LogMessage:${this.name}] Failed to log message: ${errorMessage}`
3243
+ );
3244
+ this._status = "FAILURE" /* FAILURE */;
3245
+ return "FAILURE" /* FAILURE */;
3246
+ }
3247
+ }
3248
+ /**
3249
+ * Resolve variable references in message string
3250
+ * Supports ${key}, ${input.key}, ${bb.key}, ${env.KEY}, ${param.key}
3251
+ */
3252
+ resolveMessage(message, context) {
3253
+ const varCtx = {
3254
+ blackboard: context.blackboard,
3255
+ input: context.input,
3256
+ testData: context.testData
3257
+ };
3258
+ const resolved = resolveString(message, varCtx);
3259
+ if (typeof resolved === "string") {
3260
+ return resolved;
3261
+ }
3262
+ if (resolved === null) {
3263
+ return "null";
3264
+ }
3265
+ if (typeof resolved === "object") {
3266
+ try {
3267
+ return JSON.stringify(resolved);
3268
+ } catch {
3269
+ return String(resolved);
3270
+ }
3271
+ }
3272
+ return String(resolved);
3273
+ }
3274
+ };
3275
+
3276
+ // src/utilities/regex-extract.ts
3277
+ var RegexExtract = class extends ActionNode {
3278
+ input;
3279
+ pattern;
3280
+ outputKey;
3281
+ flags;
3282
+ matchIndex;
3283
+ constructor(config) {
3284
+ super(config);
3285
+ this.input = config.input;
3286
+ this.pattern = config.pattern;
3287
+ this.outputKey = config.outputKey;
3288
+ this.flags = config.flags || "g";
3289
+ this.matchIndex = config.matchIndex;
3290
+ }
3291
+ async executeTick(context) {
3292
+ try {
3293
+ const text = context.blackboard.get(this.input);
3294
+ if (text === void 0 || text === null) {
3295
+ throw new ConfigurationError(
3296
+ `Input '${this.input}' not found in blackboard`
3297
+ );
3298
+ }
3299
+ if (typeof text !== "string") {
3300
+ throw new ConfigurationError(
3301
+ `Input '${this.input}' must be a string, got ${typeof text}`
3302
+ );
3303
+ }
3304
+ let regex;
3305
+ try {
3306
+ regex = new RegExp(this.pattern, this.flags);
3307
+ } catch (error) {
3308
+ const errorMessage = error instanceof Error ? error.message : String(error);
3309
+ throw new Error(`Invalid regex pattern: ${errorMessage}`);
3310
+ }
3311
+ const matches = text.match(regex) || [];
3312
+ let result;
3313
+ if (this.matchIndex !== void 0 && this.matchIndex >= 0) {
3314
+ result = matches[this.matchIndex] || null;
3315
+ this.log(`Extracted match at index ${this.matchIndex}: ${result}`);
3316
+ } else {
3317
+ result = matches;
3318
+ this.log(`Extracted ${matches.length} match(es) from input`);
3319
+ }
3320
+ this.setOutput(context, this.outputKey, result);
3321
+ this._status = "SUCCESS" /* SUCCESS */;
3322
+ return "SUCCESS" /* SUCCESS */;
3323
+ } catch (error) {
3324
+ const errorMessage = error instanceof Error ? error.message : String(error);
3325
+ this.log(`RegexExtract failed: ${errorMessage}`);
3326
+ this._status = "FAILURE" /* FAILURE */;
3327
+ return "FAILURE" /* FAILURE */;
3328
+ }
3329
+ }
3330
+ };
3331
+
3332
+ // src/integrations/piece-executor.ts
3333
+ var PROVIDER_TO_PIECE = {
3334
+ // Google services
3335
+ "google": "google-sheets",
3336
+ "google-sheets": "google-sheets",
3337
+ "google-drive": "google-drive",
3338
+ "google-calendar": "google-calendar",
3339
+ "gmail": "gmail",
3340
+ // Communication
3341
+ "slack": "slack",
3342
+ "discord": "discord",
3343
+ "telegram": "telegram-bot",
3344
+ "twilio": "twilio",
3345
+ // AI/ML
3346
+ "openai": "openai",
3347
+ "anthropic": "anthropic",
3348
+ // CRM & Sales
3349
+ "hubspot": "hubspot",
3350
+ "salesforce": "salesforce",
3351
+ // Productivity
3352
+ "notion": "notion",
3353
+ "airtable": "airtable",
3354
+ "asana": "asana",
3355
+ "trello": "trello",
3356
+ // Development
3357
+ "github": "github",
3358
+ "gitlab": "gitlab",
3359
+ // E-commerce
3360
+ "shopify": "shopify",
3361
+ "stripe": "stripe",
3362
+ // Other
3363
+ "http": "http",
3364
+ "webhook": "http"
3365
+ };
3366
+ var pieceCache = /* @__PURE__ */ new Map();
3367
+ function getPiecePackageName(provider) {
3368
+ const pieceName = PROVIDER_TO_PIECE[provider.toLowerCase()];
3369
+ if (pieceName) {
3370
+ return `@activepieces/piece-${pieceName}`;
3371
+ }
3372
+ return `@activepieces/piece-${provider.toLowerCase()}`;
3373
+ }
3374
+ var PIECE_EXPORT_NAMES = {
3375
+ "@activepieces/piece-google-sheets": "googleSheets",
3376
+ "@activepieces/piece-slack": "slack",
3377
+ "@activepieces/piece-openai": "openai",
3378
+ "@activepieces/piece-discord": "discord",
3379
+ "@activepieces/piece-notion": "notion",
3380
+ "@activepieces/piece-github": "github",
3381
+ "@activepieces/piece-http": "http"
3382
+ };
3383
+ async function loadPiece(packageName) {
3384
+ if (pieceCache.has(packageName)) {
3385
+ return pieceCache.get(packageName);
3386
+ }
3387
+ try {
3388
+ const pieceModule = await import(packageName);
3389
+ let piece = pieceModule;
3390
+ const exportName = PIECE_EXPORT_NAMES[packageName];
3391
+ if (exportName && pieceModule[exportName]) {
3392
+ piece = pieceModule[exportName];
3393
+ } else {
3394
+ for (const key of Object.keys(pieceModule)) {
3395
+ const exported = pieceModule[key];
3396
+ if (exported && typeof exported === "object" && "_actions" in exported) {
3397
+ piece = exported;
3398
+ break;
3399
+ }
3400
+ }
3401
+ }
3402
+ pieceCache.set(packageName, piece);
3403
+ return piece;
3404
+ } catch (error) {
3405
+ const message = error instanceof Error ? error.message : String(error);
3406
+ if (message.includes("Cannot find module")) {
3407
+ throw new Error(
3408
+ `Active Pieces package '${packageName}' is not installed. Run: npm install ${packageName}`
3409
+ );
3410
+ }
3411
+ throw new Error(`Failed to load piece package '${packageName}': ${message}`);
3412
+ }
3413
+ }
3414
+ async function executePieceAction(request) {
3415
+ const { provider, action, inputs, auth } = request;
3416
+ const packageName = getPiecePackageName(provider);
3417
+ const piece = await loadPiece(packageName);
3418
+ let actions;
3419
+ if (piece._actions && typeof piece._actions === "object") {
3420
+ actions = piece._actions;
3421
+ } else if (piece.actions && typeof piece.actions === "object") {
3422
+ actions = piece.actions;
3423
+ } else if (piece.default && typeof piece.default === "object") {
3424
+ const defaultExport = piece.default;
3425
+ actions = defaultExport._actions || defaultExport.actions;
3426
+ }
3427
+ if (!actions) {
3428
+ throw new Error(
3429
+ `Piece '${packageName}' does not export actions. This may not be a valid Active Pieces package.`
3430
+ );
3431
+ }
3432
+ const actionDef = actions[action];
3433
+ if (!actionDef) {
3434
+ const availableActions = Object.keys(actions).join(", ");
3435
+ throw new Error(
3436
+ `Action '${action}' not found in piece '${packageName}'. Available actions: ${availableActions}`
3437
+ );
3438
+ }
3439
+ if (typeof actionDef.run !== "function") {
3440
+ throw new Error(
3441
+ `Action '${action}' in piece '${packageName}' does not have a run method`
3442
+ );
3443
+ }
3444
+ const runFn = actionDef.run;
3445
+ const result = await runFn({
3446
+ auth,
3447
+ propsValue: inputs,
3448
+ // Additional context that some pieces might need
3449
+ store: createMockStore(),
3450
+ files: createMockFiles()
3451
+ });
3452
+ return result;
3453
+ }
3454
+ function createMockStore() {
3455
+ const storage = {};
3456
+ return {
3457
+ async get(key) {
3458
+ return storage[key];
3459
+ },
3460
+ async put(key, value) {
3461
+ storage[key] = value;
3462
+ },
3463
+ async delete(key) {
3464
+ delete storage[key];
3465
+ }
3466
+ };
3467
+ }
3468
+ function createMockFiles() {
3469
+ return {
3470
+ async write(params) {
3471
+ console.log(`[PieceExecutor] Mock file write: ${params.fileName}`);
3472
+ return `mock://files/${params.fileName}`;
3473
+ }
3474
+ };
3475
+ }
3476
+ async function listPieceActions(provider) {
3477
+ const packageName = getPiecePackageName(provider);
3478
+ const piece = await loadPiece(packageName);
3479
+ let actions;
3480
+ if (piece._actions && typeof piece._actions === "object") {
3481
+ actions = piece._actions;
3482
+ } else if (piece.actions && typeof piece.actions === "object") {
3483
+ actions = piece.actions;
3484
+ } else if (piece.default && typeof piece.default === "object") {
3485
+ const defaultExport = piece.default;
3486
+ actions = defaultExport._actions || defaultExport.actions;
3487
+ }
3488
+ if (!actions) {
3489
+ return [];
3490
+ }
3491
+ return Object.entries(actions).map(([name, def]) => {
3492
+ const actionDef = def;
3493
+ return {
3494
+ name,
3495
+ displayName: actionDef.displayName,
3496
+ description: actionDef.description
3497
+ };
3498
+ });
3499
+ }
3500
+ async function isPieceInstalled(provider) {
3501
+ const packageName = getPiecePackageName(provider);
3502
+ try {
3503
+ await loadPiece(packageName);
3504
+ return true;
3505
+ } catch {
3506
+ return false;
3507
+ }
3508
+ }
3509
+ function clearPieceCache() {
3510
+ pieceCache.clear();
3511
+ }
3512
+
3513
+ // src/integrations/integration-action.ts
3514
+ var IntegrationAction = class extends ActionNode {
3515
+ provider;
3516
+ action;
3517
+ inputs;
3518
+ connectionId;
3519
+ storeResult;
3520
+ resultKey;
3521
+ constructor(config) {
3522
+ super(config);
3523
+ if (!config.provider) {
3524
+ throw new ConfigurationError("IntegrationAction requires provider");
3525
+ }
3526
+ if (!config.action) {
3527
+ throw new ConfigurationError("IntegrationAction requires action");
3528
+ }
3529
+ this.provider = config.provider;
3530
+ this.action = config.action;
3531
+ this.inputs = config.inputs || {};
3532
+ this.connectionId = config.connectionId;
3533
+ this.storeResult = config.storeResult !== false;
3534
+ this.resultKey = config.resultKey || `${this.id}.result`;
3535
+ }
3536
+ async executeTick(context) {
3537
+ const integrationContext = context;
3538
+ if (!integrationContext.tokenProvider) {
3539
+ this._lastError = "No token provider configured in context. Set context.tokenProvider to fetch OAuth tokens.";
3540
+ this.log(`Error: ${this._lastError}`);
3541
+ return "FAILURE" /* FAILURE */;
3542
+ }
3543
+ try {
3544
+ const resolvedInputs = this.resolveInputs(context);
3545
+ this.log(`Resolved inputs: ${JSON.stringify(resolvedInputs)}`);
3546
+ const auth = await integrationContext.tokenProvider(
3547
+ context,
3548
+ this.provider,
3549
+ this.connectionId
3550
+ );
3551
+ this.log(`Got authentication for provider: ${this.provider}`);
3552
+ const result = await this.executeAction(context, resolvedInputs, auth);
3553
+ if (this.storeResult) {
3554
+ context.blackboard.set(this.resultKey, result);
3555
+ this.log(`Stored result in blackboard: ${this.resultKey}`);
3556
+ }
3557
+ this.log(
3558
+ `Integration action completed: ${this.provider}/${this.action}`
3559
+ );
3560
+ return "SUCCESS" /* SUCCESS */;
3561
+ } catch (error) {
3562
+ this._lastError = error instanceof Error ? error.message : String(error);
3563
+ this.log(`Integration action failed: ${this._lastError}`);
3564
+ return "FAILURE" /* FAILURE */;
3565
+ }
3566
+ }
3567
+ /**
3568
+ * Dual-mode execution:
3569
+ * - Activity mode: Use context.activities (deterministic for Temporal)
3570
+ * - Standalone mode: Inline executePieceAction (for testing)
3571
+ */
3572
+ async executeAction(context, inputs, auth) {
3573
+ const request = {
3574
+ provider: this.provider,
3575
+ action: this.action,
3576
+ inputs,
3577
+ auth
3578
+ };
3579
+ if (context.activities?.executePieceAction) {
3580
+ this.log(`Executing via activity: ${this.provider}/${this.action}`);
3581
+ return context.activities.executePieceAction(request);
3582
+ }
3583
+ this.log(`Executing inline (standalone): ${this.provider}/${this.action}`);
3584
+ return executePieceAction(request);
3585
+ }
3586
+ /**
3587
+ * Resolve variable references in inputs
3588
+ * Supports ${input.key}, ${bb.key}, ${env.KEY}, ${param.key}
3589
+ */
3590
+ resolveInputs(context) {
3591
+ const varCtx = {
3592
+ blackboard: context.blackboard,
3593
+ input: context.input,
3594
+ testData: context.testData
3595
+ };
3596
+ return resolveValue(this.inputs, varCtx, { preserveUndefined: false });
3597
+ }
3598
+ };
3599
+ var envTokenProvider = async (_context, provider) => {
3600
+ const normalizedProvider = provider.toUpperCase().replace(/-/g, "_");
3601
+ const accessToken = process.env[`${normalizedProvider}_ACCESS_TOKEN`];
3602
+ if (accessToken) {
3603
+ return { access_token: accessToken };
3604
+ }
3605
+ const apiKey = process.env[`${normalizedProvider}_API_KEY`];
3606
+ if (apiKey) {
3607
+ return { api_key: apiKey };
3608
+ }
3609
+ throw new Error(
3610
+ `No token found for provider ${provider}. Set ${normalizedProvider}_ACCESS_TOKEN or ${normalizedProvider}_API_KEY environment variable.`
3611
+ );
3612
+ };
3613
+
3614
+ // src/actions/python-script.ts
3615
+ var PythonScript = class extends ActionNode {
3616
+ code;
3617
+ packages;
3618
+ timeout;
3619
+ allowedEnvVars;
3620
+ constructor(config) {
3621
+ super(config);
3622
+ if (!config.code) {
3623
+ throw new ConfigurationError("PythonScript requires code");
3624
+ }
3625
+ this.code = config.code;
3626
+ this.packages = config.packages || [];
3627
+ this.timeout = config.timeout || 6e4;
3628
+ this.allowedEnvVars = config.allowedEnvVars || [];
3629
+ }
3630
+ async executeTick(context) {
3631
+ if (!context.activities?.executePythonScript) {
3632
+ this._lastError = "PythonScript requires activities.executePythonScript to be configured. This activity executes Python code in a separate worker process.";
3633
+ this.log(`Error: ${this._lastError}`);
3634
+ return "FAILURE" /* FAILURE */;
3635
+ }
3636
+ try {
3637
+ const request = {
3638
+ code: this.resolveCode(context),
3639
+ blackboard: context.blackboard.toJSON(),
3640
+ input: context.input ? { ...context.input } : void 0,
3641
+ env: this.getAllowedEnv(),
3642
+ timeout: this.timeout
3643
+ };
3644
+ this.log(`Executing Python script (${this.code.length} chars, timeout: ${this.timeout}ms)`);
3645
+ const result = await context.activities.executePythonScript(request);
3646
+ for (const [key, value] of Object.entries(result.blackboard)) {
3647
+ context.blackboard.set(key, value);
3648
+ }
3649
+ this.log(`Python script completed, ${Object.keys(result.blackboard).length} blackboard keys updated`);
3650
+ if (result.stdout) {
3651
+ this.log(`Python stdout: ${result.stdout}`);
3652
+ }
3653
+ if (result.stderr) {
3654
+ this.log(`Python stderr: ${result.stderr}`);
3655
+ }
3656
+ return "SUCCESS" /* SUCCESS */;
3657
+ } catch (error) {
3658
+ this._lastError = error instanceof Error ? error.message : String(error);
3659
+ this.log(`Python script failed: ${this._lastError}`);
3660
+ return "FAILURE" /* FAILURE */;
3661
+ }
3662
+ }
3663
+ /**
3664
+ * Resolve variable references in the Python code
3665
+ * Allows dynamic code templates like:
3666
+ * code: "bb['output_key'] = '${input.prefix}_result'"
3667
+ */
3668
+ resolveCode(context) {
3669
+ const varCtx = {
3670
+ blackboard: context.blackboard,
3671
+ input: context.input,
3672
+ testData: context.testData
3673
+ };
3674
+ return resolveString(this.code, varCtx);
3675
+ }
3676
+ /**
3677
+ * Get allowed environment variables to pass to Python
3678
+ */
3679
+ getAllowedEnv() {
3680
+ const env = {};
3681
+ for (const varName of this.allowedEnvVars) {
3682
+ const value = process.env[varName];
3683
+ if (value !== void 0) {
3684
+ env[varName] = value;
3685
+ }
3686
+ }
3687
+ return env;
3688
+ }
3689
+ };
3690
+
3691
+ // src/actions/parse-file.ts
3692
+ var ParseFile = class extends ActionNode {
3693
+ file;
3694
+ format;
3695
+ sheetName;
3696
+ columnMapping;
3697
+ outputKey;
3698
+ options;
3699
+ constructor(config) {
3700
+ super(config);
3701
+ if (!config.file) {
3702
+ throw new ConfigurationError("ParseFile requires file");
3703
+ }
3704
+ if (!config.outputKey) {
3705
+ throw new ConfigurationError("ParseFile requires outputKey");
3706
+ }
3707
+ this.file = config.file;
3708
+ this.format = config.format || "auto";
3709
+ this.sheetName = config.sheetName;
3710
+ this.columnMapping = config.columnMapping;
3711
+ this.outputKey = config.outputKey;
3712
+ this.options = config.options;
3713
+ }
3714
+ async executeTick(context) {
3715
+ if (!context.activities?.parseFile) {
3716
+ this._lastError = "ParseFile requires activities.parseFile to be configured. This activity handles file I/O outside the workflow sandbox.";
3717
+ this.log(`Error: ${this._lastError}`);
3718
+ return "FAILURE" /* FAILURE */;
3719
+ }
3720
+ try {
3721
+ const varCtx = {
3722
+ blackboard: context.blackboard,
3723
+ input: context.input,
3724
+ testData: context.testData
3725
+ };
3726
+ const resolvedFile = resolveValue(this.file, varCtx);
3727
+ const request = {
3728
+ file: resolvedFile,
3729
+ format: this.format,
3730
+ sheetName: this.sheetName,
3731
+ columnMapping: this.columnMapping,
3732
+ options: this.options
3733
+ };
3734
+ this.log(`Parsing file: ${resolvedFile} (format: ${this.format || "auto"})`);
3735
+ const result = await context.activities.parseFile(request);
3736
+ context.blackboard.set(this.outputKey, result.data);
3737
+ this.log(
3738
+ `Parsed ${result.rowCount} rows from ${resolvedFile}, columns: [${result.columns.join(", ")}]`
3739
+ );
3740
+ return "SUCCESS" /* SUCCESS */;
3741
+ } catch (error) {
3742
+ this._lastError = error instanceof Error ? error.message : String(error);
3743
+ this.log(`Parse file failed: ${this._lastError}`);
3744
+ return "FAILURE" /* FAILURE */;
3745
+ }
3746
+ }
3747
+ };
3748
+
3749
+ // src/actions/generate-file.ts
3750
+ var GenerateFile = class extends ActionNode {
3751
+ format;
3752
+ dataKey;
3753
+ columns;
3754
+ filename;
3755
+ storage;
3756
+ outputKey;
3757
+ constructor(config) {
3758
+ super(config);
3759
+ if (!config.format) {
3760
+ throw new ConfigurationError("GenerateFile requires format");
3761
+ }
3762
+ if (!config.dataKey) {
3763
+ throw new ConfigurationError("GenerateFile requires dataKey");
3764
+ }
3765
+ if (!config.filename) {
3766
+ throw new ConfigurationError("GenerateFile requires filename");
3767
+ }
3768
+ if (!config.storage) {
3769
+ throw new ConfigurationError("GenerateFile requires storage");
3770
+ }
3771
+ if (!config.outputKey) {
3772
+ throw new ConfigurationError("GenerateFile requires outputKey");
3773
+ }
3774
+ this.format = config.format;
3775
+ this.dataKey = config.dataKey;
3776
+ this.columns = config.columns;
3777
+ this.filename = config.filename;
3778
+ this.storage = config.storage;
3779
+ this.outputKey = config.outputKey;
3780
+ }
3781
+ async executeTick(context) {
3782
+ if (!context.activities?.generateFile) {
3783
+ this._lastError = "GenerateFile requires activities.generateFile to be configured. This activity handles file I/O outside the workflow sandbox.";
3784
+ this.log(`Error: ${this._lastError}`);
3785
+ return "FAILURE" /* FAILURE */;
3786
+ }
3787
+ try {
3788
+ const varCtx = {
3789
+ blackboard: context.blackboard,
3790
+ input: context.input,
3791
+ testData: context.testData
3792
+ };
3793
+ const data = context.blackboard.get(this.dataKey);
3794
+ if (!Array.isArray(data)) {
3795
+ this._lastError = `Data at '${this.dataKey}' is not an array (got ${typeof data})`;
3796
+ this.log(`Error: ${this._lastError}`);
3797
+ return "FAILURE" /* FAILURE */;
3798
+ }
3799
+ const resolvedFilename = resolveValue(this.filename, varCtx);
3800
+ const request = {
3801
+ format: this.format,
3802
+ data,
3803
+ columns: this.columns,
3804
+ filename: resolvedFilename,
3805
+ storage: this.storage
3806
+ };
3807
+ this.log(
3808
+ `Generating ${this.format} file: ${resolvedFilename} (${data.length} rows, storage: ${this.storage})`
3809
+ );
3810
+ const result = await context.activities.generateFile(request);
3811
+ context.blackboard.set(this.outputKey, result);
3812
+ this.log(
3813
+ `Generated file: ${result.filename} (${result.size} bytes, path: ${result.path})`
3814
+ );
3815
+ return "SUCCESS" /* SUCCESS */;
3816
+ } catch (error) {
3817
+ this._lastError = error instanceof Error ? error.message : String(error);
3818
+ this.log(`Generate file failed: ${this._lastError}`);
3819
+ return "FAILURE" /* FAILURE */;
3820
+ }
3821
+ }
3822
+ };
3823
+
3824
+ // src/actions/http-request.ts
3825
+ var HttpRequest = class extends ActionNode {
3826
+ url;
3827
+ method;
3828
+ headers;
3829
+ body;
3830
+ responseType;
3831
+ timeout;
3832
+ retry;
3833
+ outputKey;
3834
+ constructor(config) {
3835
+ super(config);
3836
+ if (!config.url) {
3837
+ throw new ConfigurationError("HttpRequest requires url");
3838
+ }
3839
+ if (!config.outputKey) {
3840
+ throw new ConfigurationError("HttpRequest requires outputKey");
3841
+ }
3842
+ this.url = config.url;
3843
+ this.method = config.method || "GET";
3844
+ this.headers = config.headers;
3845
+ this.body = config.body;
3846
+ this.responseType = config.responseType || "json";
3847
+ this.timeout = config.timeout;
3848
+ this.retry = config.retry;
3849
+ this.outputKey = config.outputKey;
3850
+ }
3851
+ async executeTick(context) {
3852
+ if (!context.activities?.fetchUrl) {
3853
+ this._lastError = "HttpRequest requires activities.fetchUrl to be configured. This activity handles HTTP I/O outside the workflow sandbox.";
3854
+ this.log(`Error: ${this._lastError}`);
3855
+ return "FAILURE" /* FAILURE */;
3856
+ }
3857
+ try {
3858
+ const varCtx = {
3859
+ blackboard: context.blackboard,
3860
+ input: context.input,
3861
+ testData: context.testData
3862
+ };
3863
+ const resolvedUrl = resolveValue(this.url, varCtx);
3864
+ const resolvedHeaders = this.headers ? resolveValue(this.headers, varCtx) : void 0;
3865
+ const resolvedBody = this.body !== void 0 ? resolveValue(this.body, varCtx) : void 0;
3866
+ const request = {
3867
+ url: resolvedUrl,
3868
+ method: this.method || "GET",
3869
+ headers: resolvedHeaders,
3870
+ body: resolvedBody,
3871
+ timeout: this.timeout
3872
+ };
3873
+ this.log(`HTTP ${request.method} ${resolvedUrl}`);
3874
+ let response;
3875
+ let lastError;
3876
+ const maxAttempts = this.retry?.maxAttempts || 1;
3877
+ const backoffMs = this.retry?.backoffMs || 1e3;
3878
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3879
+ try {
3880
+ response = await context.activities.fetchUrl(request);
3881
+ lastError = void 0;
3882
+ break;
3883
+ } catch (error) {
3884
+ lastError = error instanceof Error ? error : new Error(String(error));
3885
+ if (attempt < maxAttempts) {
3886
+ const delay = backoffMs * Math.pow(2, attempt - 1);
3887
+ this.log(`Request failed, retrying in ${delay}ms (attempt ${attempt}/${maxAttempts})`);
3888
+ await this.sleep(delay);
3889
+ }
3890
+ }
3891
+ }
3892
+ if (lastError) {
3893
+ throw lastError;
3894
+ }
3895
+ if (!response) {
3896
+ throw new Error("No response received");
3897
+ }
3898
+ let parsedData = response.data;
3899
+ if (this.responseType === "json" && typeof response.data === "string") {
3900
+ try {
3901
+ parsedData = JSON.parse(response.data);
3902
+ } catch {
3903
+ this.log("Response is not valid JSON, keeping as string");
3904
+ }
3905
+ }
3906
+ context.blackboard.set(this.outputKey, {
3907
+ status: response.status,
3908
+ headers: response.headers,
3909
+ data: parsedData
3910
+ });
3911
+ this.log(
3912
+ `HTTP ${request.method} ${resolvedUrl} -> ${response.status}`
3913
+ );
3914
+ return response.status >= 200 && response.status < 300 ? "SUCCESS" /* SUCCESS */ : "FAILURE" /* FAILURE */;
3915
+ } catch (error) {
3916
+ this._lastError = error instanceof Error ? error.message : String(error);
3917
+ this.log(`HTTP request failed: ${this._lastError}`);
3918
+ return "FAILURE" /* FAILURE */;
3919
+ }
3920
+ }
3921
+ /**
3922
+ * Sleep for the specified duration
3923
+ */
3924
+ sleep(ms) {
3925
+ return new Promise((resolve) => setTimeout(resolve, ms));
3926
+ }
3927
+ };
3928
+
3929
+ // src/data-store/types.ts
3930
+ function isDataRef(value) {
3931
+ if (typeof value !== "object" || value === null) {
3932
+ return false;
3933
+ }
3934
+ const obj = value;
3935
+ return typeof obj.store === "string" && ["gcs", "s3", "redis", "memory"].includes(obj.store) && typeof obj.key === "string" && obj.key.length > 0;
3936
+ }
3937
+
3938
+ // src/actions/code-execution.ts
3939
+ var CodeExecution = class extends ActionNode {
3940
+ code;
3941
+ language;
3942
+ timeout;
3943
+ packages;
3944
+ constructor(config) {
3945
+ super(config);
3946
+ if (!config.code) {
3947
+ throw new ConfigurationError("CodeExecution requires code");
3948
+ }
3949
+ if (!config.language) {
3950
+ throw new ConfigurationError("CodeExecution requires language");
3951
+ }
3952
+ if (!["javascript", "python"].includes(config.language)) {
3953
+ throw new ConfigurationError(
3954
+ `CodeExecution language must be 'javascript' or 'python', got: ${config.language}`
3955
+ );
3956
+ }
3957
+ this.code = config.code;
3958
+ this.language = config.language;
3959
+ this.timeout = config.timeout ?? 3e4;
3960
+ this.packages = config.packages ?? [];
3961
+ }
3962
+ async executeTick(context) {
3963
+ if (!context.activities?.executeCode) {
3964
+ this._lastError = "CodeExecution requires activities.executeCode to be configured. This activity handles sandboxed code execution via Microsandbox.";
3965
+ this.log(`Error: ${this._lastError}`);
3966
+ return "FAILURE" /* FAILURE */;
3967
+ }
3968
+ try {
3969
+ const dataRefs = {};
3970
+ const inlineContext = {};
3971
+ for (const [key, value] of Object.entries(context.blackboard.toJSON())) {
3972
+ if (isDataRef(value)) {
3973
+ dataRefs[key] = value;
3974
+ } else {
3975
+ inlineContext[key] = value;
3976
+ }
3977
+ }
3978
+ const request = {
3979
+ code: this.code,
3980
+ language: this.language,
3981
+ dataRefs,
3982
+ context: inlineContext,
3983
+ input: context.input ? { ...context.input } : void 0,
3984
+ timeout: this.timeout,
3985
+ packages: this.packages.length > 0 ? this.packages : void 0,
3986
+ workflowId: context.workflowInfo?.workflowId
3987
+ };
3988
+ this.log(
3989
+ `Executing ${this.language} code (timeout: ${this.timeout}ms, packages: ${this.packages.length})`
3990
+ );
3991
+ const result = await context.activities.executeCode(request);
3992
+ if (result.logs.length > 0) {
3993
+ for (const log of result.logs) {
3994
+ this.log(`[sandbox] ${log}`);
3995
+ }
3996
+ }
3997
+ for (const [key, value] of Object.entries(result.values)) {
3998
+ context.blackboard.set(key, value);
3999
+ }
4000
+ for (const [key, ref] of Object.entries(result.dataRefs)) {
4001
+ context.blackboard.set(key, ref);
4002
+ }
4003
+ this.log(
4004
+ `Code execution completed in ${result.executionTimeMs}ms (${Object.keys(result.values).length} values, ${Object.keys(result.dataRefs).length} refs)`
4005
+ );
4006
+ return "SUCCESS" /* SUCCESS */;
4007
+ } catch (error) {
4008
+ let actualMessage = error instanceof Error ? error.message : String(error);
4009
+ if (error && typeof error === "object") {
4010
+ const err = error;
4011
+ if (err.cause?.cause?.message) {
4012
+ actualMessage = err.cause.cause.message;
4013
+ } else if (err.cause?.message) {
4014
+ actualMessage = err.cause.message;
4015
+ }
4016
+ }
4017
+ this.log(`Code execution failed: ${actualMessage}`);
4018
+ throw new Error(`Code execution failed: ${actualMessage}`);
4019
+ }
4020
+ }
4021
+ };
4022
+
4023
+ // src/registry-utils.ts
4024
+ function registerStandardNodes(registry) {
4025
+ registry.register("Sequence", Sequence, { category: "composite" });
4026
+ registry.register("Selector", Selector, { category: "composite" });
4027
+ registry.register("Parallel", Parallel, { category: "composite" });
4028
+ registry.register("Conditional", Conditional, {
4029
+ category: "composite"
4030
+ });
4031
+ registry.register("ForEach", ForEach, { category: "composite" });
4032
+ registry.register("While", While, { category: "composite" });
4033
+ registry.register("Recovery", Recovery, { category: "composite" });
4034
+ registry.register("ReactiveSequence", ReactiveSequence, {
4035
+ category: "composite"
4036
+ });
4037
+ registry.register("MemorySequence", MemorySequence, {
4038
+ category: "composite"
4039
+ });
4040
+ registry.register("SubTree", SubTree, { category: "composite" });
4041
+ registry.register("Timeout", Timeout, { category: "decorator" });
4042
+ registry.register("Delay", Delay, { category: "decorator" });
4043
+ registry.register("Repeat", Repeat, { category: "decorator" });
4044
+ registry.register("Invert", Invert, { category: "decorator" });
4045
+ registry.register("ForceSuccess", ForceSuccess, {
4046
+ category: "decorator"
4047
+ });
4048
+ registry.register("ForceFailure", ForceFailure, {
4049
+ category: "decorator"
4050
+ });
4051
+ registry.register("RunOnce", RunOnce, { category: "decorator" });
4052
+ registry.register("KeepRunningUntilFailure", KeepRunningUntilFailure, {
4053
+ category: "decorator"
4054
+ });
4055
+ registry.register("Precondition", Precondition, {
4056
+ category: "decorator"
4057
+ });
4058
+ registry.register("SoftAssert", SoftAssert, { category: "decorator" });
4059
+ registry.register("PrintAction", PrintAction, { category: "action" });
4060
+ registry.register("MockAction", MockAction, { category: "action" });
4061
+ registry.register("SuccessNode", SuccessNode, { category: "action" });
4062
+ registry.register("FailureNode", FailureNode, { category: "action" });
4063
+ registry.register("RunningNode", RunningNode, { category: "action" });
4064
+ registry.register("CounterAction", CounterAction, {
4065
+ category: "action"
4066
+ });
4067
+ registry.register("CheckCondition", CheckCondition, {
4068
+ category: "condition"
4069
+ });
4070
+ registry.register("AlwaysCondition", AlwaysCondition, {
4071
+ category: "condition"
4072
+ });
4073
+ registry.register("WaitAction", WaitAction, { category: "action" });
4074
+ registry.register("LogMessage", LogMessage, { category: "action" });
4075
+ registry.register("RegexExtract", RegexExtract, { category: "action" });
4076
+ registry.register("IntegrationAction", IntegrationAction, { category: "action" });
4077
+ registry.register("PythonScript", PythonScript, { category: "action" });
4078
+ registry.register("ParseFile", ParseFile, { category: "action" });
4079
+ registry.register("GenerateFile", GenerateFile, { category: "action" });
4080
+ registry.register("HttpRequest", HttpRequest, { category: "action" });
4081
+ registry.register("CodeExecution", CodeExecution, { category: "action" });
4082
+ }
4083
+
4084
+ // src/data-store/memory-store.ts
4085
+ var MemoryDataStore = class {
4086
+ storage = /* @__PURE__ */ new Map();
4087
+ cleanupInterval = null;
4088
+ constructor(options) {
4089
+ const cleanupMs = options?.cleanupIntervalMs ?? 6e4;
4090
+ if (cleanupMs > 0) {
4091
+ this.cleanupInterval = setInterval(() => this.cleanup(), cleanupMs);
4092
+ if (this.cleanupInterval.unref) {
4093
+ this.cleanupInterval.unref();
4094
+ }
4095
+ }
4096
+ }
4097
+ async put(key, data, options) {
4098
+ const serialized = JSON.stringify(data);
4099
+ const sizeBytes = Buffer.byteLength(serialized, "utf8");
4100
+ const entry = {
4101
+ data,
4102
+ sizeBytes
4103
+ };
4104
+ if (options?.ttlSeconds) {
4105
+ entry.expiresAt = Date.now() + options.ttlSeconds * 1e3;
4106
+ }
4107
+ this.storage.set(key, entry);
4108
+ return {
4109
+ store: "memory",
4110
+ key,
4111
+ sizeBytes,
4112
+ expiresAt: entry.expiresAt
4113
+ };
4114
+ }
4115
+ async get(ref) {
4116
+ if (ref.store !== "memory") {
4117
+ throw new Error(`MemoryDataStore cannot retrieve from store: ${ref.store}`);
4118
+ }
4119
+ const entry = this.storage.get(ref.key);
4120
+ if (!entry) {
4121
+ throw new Error(`Data not found for key: ${ref.key}`);
4122
+ }
4123
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
4124
+ this.storage.delete(ref.key);
4125
+ throw new Error(`Data expired for key: ${ref.key}`);
4126
+ }
4127
+ return structuredClone(entry.data);
4128
+ }
4129
+ async delete(ref) {
4130
+ if (ref.store !== "memory") {
4131
+ throw new Error(`MemoryDataStore cannot delete from store: ${ref.store}`);
4132
+ }
4133
+ this.storage.delete(ref.key);
4134
+ }
4135
+ async exists(ref) {
4136
+ if (ref.store !== "memory") {
4137
+ return false;
4138
+ }
4139
+ const entry = this.storage.get(ref.key);
4140
+ if (!entry) {
4141
+ return false;
4142
+ }
4143
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
4144
+ this.storage.delete(ref.key);
4145
+ return false;
4146
+ }
4147
+ return true;
4148
+ }
4149
+ /**
4150
+ * Clear all stored data
4151
+ * Useful for test cleanup
4152
+ */
4153
+ clear() {
4154
+ this.storage.clear();
4155
+ }
4156
+ /**
4157
+ * Get the number of stored entries
4158
+ */
4159
+ size() {
4160
+ return this.storage.size;
4161
+ }
4162
+ /**
4163
+ * Get total bytes stored
4164
+ */
4165
+ totalBytes() {
4166
+ let total = 0;
4167
+ for (const entry of this.storage.values()) {
4168
+ total += entry.sizeBytes;
4169
+ }
4170
+ return total;
4171
+ }
4172
+ /**
4173
+ * Stop the cleanup interval
4174
+ * Call this when done with the store to prevent memory leaks in tests
4175
+ */
4176
+ dispose() {
4177
+ if (this.cleanupInterval) {
4178
+ clearInterval(this.cleanupInterval);
4179
+ this.cleanupInterval = null;
4180
+ }
4181
+ }
4182
+ /**
4183
+ * Remove expired entries
4184
+ */
4185
+ cleanup() {
4186
+ const now = Date.now();
4187
+ for (const [key, entry] of this.storage.entries()) {
4188
+ if (entry.expiresAt && now > entry.expiresAt) {
4189
+ this.storage.delete(key);
4190
+ }
4191
+ }
4192
+ }
4193
+ };
4194
+
4195
+ // src/debug/resume-point.ts
4196
+ var ResumePoint = class extends ActionNode {
4197
+ resumePointId;
4198
+ constructor(config) {
4199
+ super({ id: `resume-point-${config.id}` });
4200
+ this.resumePointId = config.id;
4201
+ }
4202
+ async executeTick(_context) {
4203
+ this._status = "SUCCESS" /* SUCCESS */;
4204
+ return "SUCCESS" /* SUCCESS */;
4205
+ }
4206
+ };
4207
+
4208
+ // src/yaml/parser.ts
4209
+ var yaml = __toESM(require("js-yaml"), 1);
4210
+
4211
+ // src/yaml/errors.ts
4212
+ var ValidationError = class extends Error {
4213
+ constructor(message, path2, suggestion) {
4214
+ super(message);
4215
+ this.path = path2;
4216
+ this.suggestion = suggestion;
4217
+ this.name = "ValidationError";
4218
+ }
4219
+ /**
4220
+ * Format error message with path and suggestion
4221
+ */
4222
+ format() {
4223
+ let formatted = this.message;
4224
+ if (this.path) {
4225
+ formatted = `${this.path}: ${formatted}`;
4226
+ }
4227
+ if (this.suggestion) {
4228
+ formatted += `
4229
+ Suggestion: ${this.suggestion}`;
4230
+ }
4231
+ return formatted;
4232
+ }
4233
+ };
4234
+ var YamlSyntaxError = class extends ValidationError {
4235
+ constructor(message, line, column, suggestion) {
4236
+ super(message, void 0, suggestion);
4237
+ this.line = line;
4238
+ this.column = column;
4239
+ this.name = "YamlSyntaxError";
4240
+ }
4241
+ format() {
4242
+ let formatted = this.message;
4243
+ if (this.line !== void 0) {
4244
+ formatted = `Line ${this.line}${this.column !== void 0 ? `, Column ${this.column}` : ""}: ${formatted}`;
4245
+ }
4246
+ if (this.suggestion) {
4247
+ formatted += `
4248
+ Suggestion: ${this.suggestion}`;
4249
+ }
4250
+ return formatted;
4251
+ }
4252
+ };
4253
+ var StructureValidationError = class extends ValidationError {
4254
+ constructor(message, path2, suggestion) {
4255
+ super(message, path2, suggestion);
4256
+ this.name = "StructureValidationError";
4257
+ }
4258
+ };
4259
+ var ConfigValidationError = class extends ValidationError {
4260
+ constructor(message, nodeType, path2, suggestion) {
4261
+ super(message, path2, suggestion);
4262
+ this.nodeType = nodeType;
4263
+ this.name = "ConfigValidationError";
4264
+ }
4265
+ format() {
4266
+ let formatted = `Invalid configuration for node type '${this.nodeType}'`;
4267
+ if (this.path) {
4268
+ formatted += ` at ${this.path}`;
4269
+ }
4270
+ formatted += `:
4271
+ ${this.message}`;
4272
+ if (this.suggestion) {
4273
+ formatted += `
4274
+ Suggestion: ${this.suggestion}`;
4275
+ }
4276
+ return formatted;
4277
+ }
4278
+ };
4279
+ var SemanticValidationError = class extends ValidationError {
4280
+ constructor(message, path2, suggestion) {
4281
+ super(message, path2, suggestion);
4282
+ this.name = "SemanticValidationError";
4283
+ }
4284
+ };
4285
+ var ValidationErrors = class extends Error {
4286
+ constructor(errors) {
4287
+ super(`Validation failed with ${errors.length} error(s)`);
4288
+ this.errors = errors;
4289
+ this.name = "ValidationErrors";
4290
+ }
4291
+ /**
4292
+ * Format all errors as a single message
4293
+ */
4294
+ format() {
4295
+ const header = `YAML validation failed
4296
+
4297
+ Issues found:`;
4298
+ const issues = this.errors.map((error, index) => {
4299
+ const formatted = error.format();
4300
+ return ` ${index + 1}. ${formatted.split("\n").join("\n ")}`;
4301
+ }).join("\n\n");
4302
+ return `${header}
4303
+ ${issues}`;
4304
+ }
4305
+ };
4306
+
4307
+ // src/yaml/validation/semantic-validator.ts
4308
+ var SemanticValidator = class {
4309
+ /**
4310
+ * Validate semantic rules for a tree definition
4311
+ *
4312
+ * @param definition - Tree definition to validate
4313
+ * @param registry - Registry to check node types and tree references
4314
+ * @returns Array of validation errors (empty if valid)
4315
+ */
4316
+ validate(definition, registry) {
4317
+ const errors = [];
4318
+ const seenIds = /* @__PURE__ */ new Set();
4319
+ const subTreePath = [];
4320
+ this.validateNode(definition, registry, seenIds, subTreePath, "", errors);
4321
+ return errors;
4322
+ }
4323
+ /**
4324
+ * Recursively validate a node and its children
4325
+ */
4326
+ validateNode(node, registry, seenIds, subTreePath, path2, errors) {
4327
+ const nodePath = path2 ? `${path2}.${node.id || node.type}` : node.id || node.type;
4328
+ if (!registry.has(node.type)) {
4329
+ errors.push(
4330
+ new SemanticValidationError(
4331
+ `Unknown node type '${node.type}'`,
4332
+ nodePath,
4333
+ `Available types: ${registry.getRegisteredTypes().join(", ")}`
4334
+ )
4335
+ );
4336
+ return;
4337
+ }
4338
+ if (node.id) {
4339
+ if (seenIds.has(node.id)) {
4340
+ errors.push(
4341
+ new SemanticValidationError(
4342
+ `Duplicate ID '${node.id}'`,
4343
+ nodePath,
4344
+ "Use unique IDs for each node in the tree"
4345
+ )
4346
+ );
4347
+ } else {
4348
+ seenIds.add(node.id);
4349
+ }
4350
+ }
4351
+ if (node.type === "SubTree") {
4352
+ const treeId = node.props?.treeId || "";
4353
+ if (!treeId) {
4354
+ errors.push(
4355
+ new SemanticValidationError(
4356
+ "SubTree node missing 'treeId' property",
4357
+ nodePath,
4358
+ "Specify which tree to reference with 'treeId' in props"
4359
+ )
4360
+ );
4361
+ } else {
4362
+ if (subTreePath.includes(treeId)) {
4363
+ errors.push(
4364
+ new SemanticValidationError(
4365
+ `Circular SubTree reference detected: ${subTreePath.join(" -> ")} -> ${treeId}`,
4366
+ nodePath,
4367
+ "Remove circular tree references"
4368
+ )
4369
+ );
4370
+ }
4371
+ if (registry.hasTree && !registry.hasTree(treeId)) {
4372
+ errors.push(
4373
+ new SemanticValidationError(
4374
+ `SubTree references unknown tree '${treeId}'`,
4375
+ nodePath,
4376
+ `Register the tree '${treeId}' before referencing it`
4377
+ )
4378
+ );
4379
+ }
4380
+ }
4381
+ }
4382
+ const metadata = registry.getMetadata(node.type);
4383
+ if (metadata) {
4384
+ if (metadata.category === "decorator") {
4385
+ const childCount = node.children?.length || 0;
4386
+ if (childCount !== 1) {
4387
+ errors.push(
4388
+ new SemanticValidationError(
4389
+ `Decorator '${node.type}' must have exactly 1 child (has ${childCount})`,
4390
+ nodePath,
4391
+ "Decorators wrap a single child node"
4392
+ )
4393
+ );
4394
+ }
4395
+ }
4396
+ if (node.type === "While") {
4397
+ const childCount = node.children?.length || 0;
4398
+ if (childCount !== 2) {
4399
+ errors.push(
4400
+ new SemanticValidationError(
4401
+ `While node requires exactly 2 children: condition and body (has ${childCount})`,
4402
+ nodePath,
4403
+ "First child is the condition, second is the body to execute"
4404
+ )
4405
+ );
4406
+ }
4407
+ }
4408
+ if (node.type === "Conditional") {
4409
+ const childCount = node.children?.length || 0;
4410
+ if (childCount < 2 || childCount > 3) {
4411
+ errors.push(
4412
+ new SemanticValidationError(
4413
+ `Conditional node requires 2-3 children: condition, then, optional else (has ${childCount})`,
4414
+ nodePath,
4415
+ "First child is condition, second is 'then' branch, third is optional 'else' branch"
4416
+ )
4417
+ );
4418
+ }
4419
+ }
4420
+ if (node.type === "ForEach") {
4421
+ const childCount = node.children?.length || 0;
4422
+ if (childCount === 0) {
4423
+ errors.push(
4424
+ new SemanticValidationError(
4425
+ "ForEach node requires at least 1 child (the body to execute for each item)",
4426
+ nodePath,
4427
+ "Add a child node to execute for each item in the collection"
4428
+ )
4429
+ );
4430
+ }
4431
+ }
4432
+ }
4433
+ if (node.children && Array.isArray(node.children)) {
4434
+ const newSubTreePath = node.type === "SubTree" && node.props?.treeId ? [...subTreePath, node.props.treeId] : subTreePath;
4435
+ node.children.forEach((child, index) => {
4436
+ this.validateNode(
4437
+ child,
4438
+ registry,
4439
+ seenIds,
4440
+ newSubTreePath,
4441
+ `${nodePath}.children[${index}]`,
4442
+ errors
4443
+ );
4444
+ });
4445
+ }
4446
+ }
4447
+ };
4448
+ var semanticValidator = new SemanticValidator();
4449
+
4450
+ // src/yaml/parser.ts
4451
+ function parseYaml(yamlString) {
4452
+ try {
4453
+ const parsed = yaml.load(yamlString);
4454
+ if (!parsed || typeof parsed !== "object") {
4455
+ throw new YamlSyntaxError(
4456
+ "Invalid YAML: expected an object",
4457
+ void 0,
4458
+ void 0,
4459
+ "Ensure your YAML defines a tree structure with 'type' field"
4460
+ );
4461
+ }
4462
+ return parsed;
4463
+ } catch (error) {
4464
+ if (error instanceof YamlSyntaxError) {
4465
+ throw error;
4466
+ }
4467
+ if (error instanceof yaml.YAMLException) {
4468
+ throw new YamlSyntaxError(
4469
+ error.message,
4470
+ error.mark?.line,
4471
+ error.mark?.column,
4472
+ "Check YAML syntax (indentation, colons, quotes)"
4473
+ );
4474
+ }
4475
+ throw new YamlSyntaxError(
4476
+ `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`,
4477
+ void 0,
4478
+ void 0,
4479
+ "Ensure your YAML is well-formed"
4480
+ );
4481
+ }
4482
+ }
4483
+ function validateStructure(definition) {
4484
+ try {
4485
+ return treeDefinitionSchema.parse(definition);
4486
+ } catch (error) {
4487
+ throw new StructureValidationError(
4488
+ `Invalid tree structure: ${error instanceof Error ? error.message : String(error)}`,
4489
+ void 0,
4490
+ "Ensure all nodes have 'type' field and children are arrays"
4491
+ );
4492
+ }
4493
+ }
4494
+ function loadTreeFromYaml(yamlString, registry, options = {}) {
4495
+ const { validate = true, failFast = true, autoGenerateIds = true } = options;
4496
+ const parsed = parseYaml(yamlString);
4497
+ if (validate) {
4498
+ const definition = validateStructure(parsed);
4499
+ if (failFast) {
4500
+ const semanticErrors = semanticValidator.validate(definition, registry);
4501
+ if (semanticErrors.length > 0) {
4502
+ if (failFast) {
4503
+ throw semanticErrors[0];
4504
+ } else {
4505
+ throw new ValidationErrors(semanticErrors);
4506
+ }
4507
+ }
4508
+ }
4509
+ return registry.createTree(definition);
4510
+ }
4511
+ return registry.createTree(parsed);
4512
+ }
4513
+ function validateYaml(yamlString, registry, options = {}) {
4514
+ const { collectAllErrors = false, checkReferences = true } = options;
4515
+ const errors = [];
4516
+ try {
4517
+ const parsed = parseYaml(yamlString);
4518
+ const definition = validateStructure(parsed);
4519
+ if (checkReferences) {
4520
+ const semanticErrors = semanticValidator.validate(definition, registry);
4521
+ errors.push(...semanticErrors);
4522
+ }
4523
+ if (errors.length === 0 || collectAllErrors) {
4524
+ const result = registry.safeCreateTree(definition);
4525
+ if (!result.success) {
4526
+ errors.push(
4527
+ new StructureValidationError(
4528
+ result.error.message,
4529
+ void 0,
4530
+ "Check node configurations match their schema requirements"
4531
+ )
4532
+ );
4533
+ }
4534
+ }
4535
+ return {
4536
+ valid: errors.length === 0,
4537
+ errors
4538
+ };
4539
+ } catch (error) {
4540
+ if (error instanceof ValidationErrors) {
4541
+ return {
4542
+ valid: false,
4543
+ errors: error.errors
4544
+ };
4545
+ }
4546
+ if (error instanceof ValidationError) {
4547
+ errors.push(error);
4548
+ } else {
4549
+ errors.push(
4550
+ new YamlSyntaxError(
4551
+ error instanceof Error ? error.message : String(error)
4552
+ )
4553
+ );
4554
+ }
4555
+ return {
4556
+ valid: false,
4557
+ errors
4558
+ };
4559
+ }
4560
+ }
4561
+ function toYaml(definition) {
4562
+ return yaml.dump(definition, {
4563
+ indent: 2,
4564
+ lineWidth: 80,
4565
+ noRefs: true
4566
+ });
4567
+ }
4568
+
4569
+ // src/yaml/loader.ts
4570
+ var import_promises = require("fs/promises");
4571
+ async function loadTreeFromFile(filePath, registry, options = {}) {
4572
+ try {
4573
+ const yamlContent = await (0, import_promises.readFile)(filePath, "utf-8");
4574
+ return loadTreeFromYaml(yamlContent, registry, options);
4575
+ } catch (error) {
4576
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
4577
+ throw new Error(`File not found: ${filePath}`);
4578
+ }
4579
+ throw error;
4580
+ }
4581
+ }
4582
+
4583
+ // src/templates/template-loader.ts
4584
+ var fs = __toESM(require("fs"), 1);
4585
+ var path = __toESM(require("path"), 1);
4586
+ async function loadTemplatesFromDirectory(registry, options) {
4587
+ const { templatesDir, idPrefix = "" } = options;
4588
+ const loadedIds = [];
4589
+ if (!fs.existsSync(templatesDir)) {
4590
+ console.warn(
4591
+ `[TemplateLoader] Templates directory not found: ${templatesDir}`
4592
+ );
4593
+ return loadedIds;
4594
+ }
4595
+ const files = fs.readdirSync(templatesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
4596
+ for (const file of files) {
4597
+ const filePath = path.join(templatesDir, file);
4598
+ try {
4599
+ const yamlContent = fs.readFileSync(filePath, "utf-8");
4600
+ const rootNode = loadTreeFromYaml(yamlContent, registry);
4601
+ const tree = new BehaviorTree(rootNode);
4602
+ const baseName = path.basename(file, path.extname(file));
4603
+ const templateId = idPrefix + baseName;
4604
+ registry.registerTree(templateId, tree, filePath);
4605
+ loadedIds.push(templateId);
4606
+ console.log(`[TemplateLoader] Registered template: ${templateId}`);
4607
+ } catch (error) {
4608
+ const errorMessage = error instanceof Error ? error.message : String(error);
4609
+ throw new Error(
4610
+ `Failed to load template '${file}': ${errorMessage}`
4611
+ );
4612
+ }
4613
+ }
4614
+ console.log(
4615
+ `[TemplateLoader] Loaded ${loadedIds.length} template(s) from ${templatesDir}`
4616
+ );
4617
+ return loadedIds;
4618
+ }
4619
+ function loadTemplate(registry, filePath, templateId) {
4620
+ const yamlContent = fs.readFileSync(filePath, "utf-8");
4621
+ const rootNode = loadTreeFromYaml(yamlContent, registry);
4622
+ const tree = new BehaviorTree(rootNode);
4623
+ const id = templateId || path.basename(filePath, path.extname(filePath));
4624
+ registry.registerTree(id, tree, filePath);
4625
+ console.log(`[TemplateLoader] Registered template: ${id} from ${filePath}`);
4626
+ return id;
4627
+ }
4628
+ function hasTemplate(registry, templateId) {
4629
+ return registry.hasTree(templateId);
4630
+ }
4631
+ function getTemplateIds(registry) {
4632
+ return registry.getAllTreeIds();
4633
+ }
4634
+
4635
+ // src/observability/execution-tracker.ts
4636
+ var ExecutionTracker = class {
4637
+ nodeStates = /* @__PURE__ */ new Map();
4638
+ timeline = [];
4639
+ errors = [];
4640
+ pathTaken = [];
4641
+ startedAt = Date.now();
4642
+ currentNodeId = null;
4643
+ totalNodes;
4644
+ finished = false;
4645
+ constructor(totalNodes) {
4646
+ this.totalNodes = totalNodes;
4647
+ }
4648
+ /**
4649
+ * Process a node event and update internal state
4650
+ * Called by NodeEventEmitter subscription
4651
+ */
4652
+ onNodeEvent(event) {
4653
+ const state = this.getOrCreateState(
4654
+ event.nodeId,
4655
+ event.nodeType,
4656
+ event.nodeName,
4657
+ event.nodePath
4658
+ );
4659
+ const eventType = event.type;
4660
+ switch (eventType) {
4661
+ case "tick_start" /* TICK_START */:
4662
+ this.handleTickStart(state, event);
4663
+ break;
4664
+ case "tick_end" /* TICK_END */:
4665
+ this.handleTickEnd(state, event);
4666
+ break;
4667
+ case "error" /* ERROR */:
4668
+ this.handleError(state, event);
4669
+ break;
4670
+ case "log" /* LOG */:
4671
+ break;
4672
+ case "halt" /* HALT */:
4673
+ case "reset" /* RESET */:
4674
+ break;
4675
+ }
4676
+ }
4677
+ handleTickStart(state, event) {
4678
+ state.status = "running";
4679
+ state.tickCount++;
4680
+ state.lastTickAt = event.timestamp;
4681
+ this.currentNodeId = event.nodeId;
4682
+ this.timeline.push({
4683
+ nodeId: event.nodeId,
4684
+ nodeType: event.nodeType,
4685
+ nodeName: event.nodeName,
4686
+ nodePath: event.nodePath ?? "",
4687
+ event: "start",
4688
+ timestamp: event.timestamp
4689
+ });
4690
+ }
4691
+ handleTickEnd(state, event) {
4692
+ const status = event.data?.status;
4693
+ const durationMs = event.data?.durationMs;
4694
+ if (status === "SUCCESS") {
4695
+ state.status = "success";
4696
+ if (!this.pathTaken.includes(event.nodeId)) {
4697
+ this.pathTaken.push(event.nodeId);
4698
+ }
4699
+ } else if (status === "FAILURE") {
4700
+ state.status = "failure";
4701
+ } else if (status === "RUNNING") {
4702
+ state.status = "running";
4703
+ } else {
4704
+ state.status = "idle";
4705
+ }
4706
+ if (durationMs !== void 0) {
4707
+ state.durationMs = durationMs;
4708
+ }
4709
+ this.timeline.push({
4710
+ nodeId: event.nodeId,
4711
+ nodeType: event.nodeType,
4712
+ nodeName: event.nodeName,
4713
+ nodePath: event.nodePath ?? "",
4714
+ event: "end",
4715
+ timestamp: event.timestamp,
4716
+ status: state.status,
4717
+ durationMs: state.durationMs
4718
+ });
4719
+ if (this.currentNodeId === event.nodeId && state.status !== "running") {
4720
+ this.currentNodeId = null;
4721
+ }
4722
+ }
4723
+ handleError(state, event) {
4724
+ state.status = "failure";
4725
+ const errorData = event.data?.error;
4726
+ state.lastError = errorData?.message ?? "Unknown error";
4727
+ const structuredError = {
4728
+ nodeId: event.nodeId,
4729
+ nodeType: event.nodeType,
4730
+ nodeName: event.nodeName,
4731
+ nodePath: event.nodePath ?? "",
4732
+ message: errorData?.message ?? "Unknown error",
4733
+ stack: errorData?.stack,
4734
+ timestamp: event.timestamp,
4735
+ blackboardSnapshot: event.data?.blackboard ?? {},
4736
+ nodeInput: errorData?.input,
4737
+ recoverable: this.isRecoverable(event.nodeType),
4738
+ suggestedFix: this.suggestFix(event)
4739
+ };
4740
+ this.errors.push(structuredError);
4741
+ this.timeline.push({
4742
+ nodeId: event.nodeId,
4743
+ nodeType: event.nodeType,
4744
+ nodeName: event.nodeName,
4745
+ nodePath: event.nodePath ?? "",
4746
+ event: "error",
4747
+ timestamp: event.timestamp,
4748
+ error: {
4749
+ message: structuredError.message,
4750
+ stack: structuredError.stack
4751
+ }
4752
+ });
4753
+ }
4754
+ /**
4755
+ * Mark execution as finished
4756
+ */
4757
+ markFinished() {
4758
+ this.finished = true;
4759
+ this.currentNodeId = null;
4760
+ }
4761
+ /**
4762
+ * Get overall execution progress
4763
+ */
4764
+ getProgress() {
4765
+ const states = [...this.nodeStates.values()];
4766
+ const completed = states.filter((n) => n.status === "success").length;
4767
+ const failed = states.filter((n) => n.status === "failure").length;
4768
+ let status;
4769
+ if (failed > 0) {
4770
+ status = "failed";
4771
+ } else if (this.finished) {
4772
+ status = "completed";
4773
+ } else if (this.currentNodeId) {
4774
+ status = "running";
4775
+ } else {
4776
+ status = "completed";
4777
+ }
4778
+ return {
4779
+ totalNodes: this.totalNodes,
4780
+ completedNodes: completed,
4781
+ failedNodes: failed,
4782
+ currentNodeId: this.currentNodeId,
4783
+ currentNodeType: this.nodeStates.get(this.currentNodeId ?? "")?.type ?? null,
4784
+ pathTaken: [...this.pathTaken],
4785
+ startedAt: this.startedAt,
4786
+ lastActivityAt: this.timeline[this.timeline.length - 1]?.timestamp ?? this.startedAt,
4787
+ status
4788
+ };
4789
+ }
4790
+ /**
4791
+ * Get all node states
4792
+ */
4793
+ getNodeStates() {
4794
+ return new Map(this.nodeStates);
4795
+ }
4796
+ /**
4797
+ * Get node states as a plain object (for JSON serialization)
4798
+ */
4799
+ getNodeStatesObject() {
4800
+ const result = {};
4801
+ for (const [id, state] of this.nodeStates) {
4802
+ result[id] = { ...state };
4803
+ }
4804
+ return result;
4805
+ }
4806
+ /**
4807
+ * Get all errors that occurred during execution
4808
+ */
4809
+ getErrors() {
4810
+ return [...this.errors];
4811
+ }
4812
+ /**
4813
+ * Get the full execution timeline
4814
+ */
4815
+ getTimeline() {
4816
+ return [...this.timeline];
4817
+ }
4818
+ /**
4819
+ * Get state for a specific node
4820
+ */
4821
+ getNodeState(nodeId) {
4822
+ return this.nodeStates.get(nodeId);
4823
+ }
4824
+ getOrCreateState(id, type, name, path2) {
4825
+ if (!this.nodeStates.has(id)) {
4826
+ this.nodeStates.set(id, {
4827
+ id,
4828
+ type,
4829
+ name,
4830
+ path: path2 ?? "",
4831
+ status: "idle",
4832
+ tickCount: 0
4833
+ });
4834
+ }
4835
+ return this.nodeStates.get(id);
4836
+ }
4837
+ /**
4838
+ * Check if an error type is potentially recoverable with retry
4839
+ */
4840
+ isRecoverable(nodeType) {
4841
+ const recoverableTypes = [
4842
+ "FetchUrl",
4843
+ "HttpRequest",
4844
+ "CodeExecution",
4845
+ "ParseFile",
4846
+ "GenerateFile",
4847
+ "IntegrationAction"
4848
+ ];
4849
+ return recoverableTypes.includes(nodeType);
4850
+ }
4851
+ /**
4852
+ * Suggest a fix based on error patterns
4853
+ */
4854
+ suggestFix(event) {
4855
+ const msg = event.data?.error?.message ?? "";
4856
+ const nodeType = event.nodeType;
4857
+ if (msg.includes("timeout") || msg.includes("TIMEOUT") || msg.includes("timed out")) {
4858
+ return "Consider increasing timeout or adding retry decorator";
4859
+ }
4860
+ if (msg.includes("undefined") || msg.includes("null") || msg.includes("Cannot read property")) {
4861
+ return "Check if required blackboard key exists before accessing";
4862
+ }
4863
+ if (msg.includes("network") || msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) {
4864
+ return "Check URL accessibility, consider adding retry decorator";
4865
+ }
4866
+ if (msg.includes("401") || msg.includes("403") || msg.includes("unauthorized")) {
4867
+ return "Check authentication credentials and permissions";
4868
+ }
4869
+ if (msg.includes("not found") || msg.includes("ENOENT") || msg.includes("404")) {
4870
+ return "Verify file path or URL exists";
4871
+ }
4872
+ if (nodeType === "CodeExecution") {
4873
+ if (msg.includes("SyntaxError")) {
4874
+ return "Fix syntax error in code";
4875
+ }
4876
+ if (msg.includes("ReferenceError")) {
4877
+ return "Variable not defined - check getBB() keys";
4878
+ }
4879
+ }
4880
+ return void 0;
4881
+ }
4882
+ };
4883
+
4884
+ // src/observability/sinks.ts
4885
+ function createObservabilitySinkHandler(config = {}) {
4886
+ return {
4887
+ events: {
4888
+ push: {
4889
+ fn: (workflowInfo, event) => {
4890
+ const { workflowId, runId } = workflowInfo;
4891
+ if (config.logEvents) {
4892
+ console.log(
4893
+ `[Sink] [${workflowId}] ${event.type}: ${event.nodeId} (${event.nodeType})`
4894
+ );
4895
+ }
4896
+ if (config.onEvent) {
4897
+ Promise.resolve(config.onEvent(workflowId, runId, event)).catch(
4898
+ (err) => console.error("[Sink] Error in onEvent handler:", err)
4899
+ );
4900
+ }
4901
+ if (event.type === "error" && config.onError) {
4902
+ Promise.resolve(config.onError(workflowId, runId, event)).catch(
4903
+ (err) => console.error("[Sink] Error in onError handler:", err)
4904
+ );
4905
+ }
4906
+ },
4907
+ // Don't call during replay - events were already processed
4908
+ callDuringReplay: false
4909
+ }
4910
+ }
4911
+ };
4912
+ }
4913
+ // Annotate the CommonJS export names for ESM import in node:
4914
+ 0 && (module.exports = {
4915
+ ActionNode,
4916
+ AlwaysCondition,
4917
+ BaseNode,
4918
+ BehaviorTree,
4919
+ CheckCondition,
4920
+ CodeExecution,
4921
+ CompositeNode,
4922
+ ConditionNode,
4923
+ Conditional,
4924
+ ConfigValidationError,
4925
+ ConfigurationError,
4926
+ CounterAction,
4927
+ DecoratorNode,
4928
+ Delay,
4929
+ ExecutionTracker,
4930
+ FailureNode,
4931
+ Fallback,
4932
+ ForEach,
4933
+ ForceFailure,
4934
+ ForceSuccess,
4935
+ GenerateFile,
4936
+ HttpRequest,
4937
+ IntegrationAction,
4938
+ Invert,
4939
+ KeepRunningUntilFailure,
4940
+ LogMessage,
4941
+ MemoryDataStore,
4942
+ MemorySequence,
4943
+ MockAction,
4944
+ NodeEventEmitter,
4945
+ NodeEventType,
4946
+ NodeStatus,
4947
+ Parallel,
4948
+ ParseFile,
4949
+ Precondition,
4950
+ PrintAction,
4951
+ PythonScript,
4952
+ ReactiveSequence,
4953
+ Recovery,
4954
+ RegexExtract,
4955
+ Registry,
4956
+ Repeat,
4957
+ ResumePoint,
4958
+ RunOnce,
4959
+ RunningNode,
4960
+ SchemaRegistry,
4961
+ ScopedBlackboard,
4962
+ Selector,
4963
+ SemanticValidationError,
4964
+ Sequence,
4965
+ SequenceWithMemory,
4966
+ SoftAssert,
4967
+ StructureValidationError,
4968
+ SubTree,
4969
+ SuccessNode,
4970
+ Timeout,
4971
+ ValidationError,
4972
+ ValidationErrors,
4973
+ WaitAction,
4974
+ While,
4975
+ YamlSyntaxError,
4976
+ clearPieceCache,
4977
+ createNodeSchema,
4978
+ createObservabilitySinkHandler,
4979
+ envTokenProvider,
4980
+ executePieceAction,
4981
+ extractVariables,
4982
+ getTemplateIds,
4983
+ hasTemplate,
4984
+ hasVariables,
4985
+ isDataRef,
4986
+ isPieceInstalled,
4987
+ listPieceActions,
4988
+ loadTemplate,
4989
+ loadTemplatesFromDirectory,
4990
+ loadTreeFromFile,
4991
+ loadTreeFromYaml,
4992
+ nodeConfigurationSchema,
4993
+ parseYaml,
4994
+ registerStandardNodes,
4995
+ resolveString,
4996
+ resolveValue,
4997
+ safeValidateConfiguration,
4998
+ schemaRegistry,
4999
+ semanticValidator,
5000
+ toYaml,
5001
+ treeDefinitionSchema,
5002
+ validateChildCount,
5003
+ validateChildCountRange,
5004
+ validateCompositeChildren,
5005
+ validateConfiguration,
5006
+ validateDecoratorChildren,
5007
+ validateTreeDefinition,
5008
+ validateYaml,
5009
+ validations,
5010
+ zodErrorToConfigurationError
5011
+ });