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