@q1k-oss/btree-workflows 0.0.1 → 0.0.2

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.
Files changed (206) hide show
  1. package/README.md +13 -13
  2. package/dist/index.cjs +5011 -0
  3. package/dist/index.d.cts +3320 -0
  4. package/dist/index.d.ts +3320 -0
  5. package/dist/index.js +4879 -0
  6. package/package.json +33 -3
  7. package/.claude/settings.local.json +0 -31
  8. package/CLAUDE.md +0 -181
  9. package/behaviour-tree-workflows-landing/index.html +0 -16
  10. package/behaviour-tree-workflows-landing/package-lock.json +0 -2074
  11. package/behaviour-tree-workflows-landing/package.json +0 -31
  12. package/behaviour-tree-workflows-landing/public/favicon.svg +0 -17
  13. package/behaviour-tree-workflows-landing/src/App.css +0 -103
  14. package/behaviour-tree-workflows-landing/src/App.tsx +0 -176
  15. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.css +0 -89
  16. package/behaviour-tree-workflows-landing/src/components/BlackboardInspector.tsx +0 -64
  17. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.css +0 -64
  18. package/behaviour-tree-workflows-landing/src/components/ExampleSelector.tsx +0 -34
  19. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.css +0 -107
  20. package/behaviour-tree-workflows-landing/src/components/ExecutionLog.tsx +0 -85
  21. package/behaviour-tree-workflows-landing/src/components/Header.css +0 -50
  22. package/behaviour-tree-workflows-landing/src/components/Header.tsx +0 -26
  23. package/behaviour-tree-workflows-landing/src/components/StatusBadge.css +0 -45
  24. package/behaviour-tree-workflows-landing/src/components/StatusBadge.tsx +0 -15
  25. package/behaviour-tree-workflows-landing/src/components/Toolbar.css +0 -74
  26. package/behaviour-tree-workflows-landing/src/components/Toolbar.tsx +0 -53
  27. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.css +0 -67
  28. package/behaviour-tree-workflows-landing/src/components/TreeVisualizer.tsx +0 -192
  29. package/behaviour-tree-workflows-landing/src/components/YamlEditor.css +0 -18
  30. package/behaviour-tree-workflows-landing/src/components/YamlEditor.tsx +0 -96
  31. package/behaviour-tree-workflows-landing/src/lib/count-nodes.ts +0 -11
  32. package/behaviour-tree-workflows-landing/src/lib/execution-engine.ts +0 -96
  33. package/behaviour-tree-workflows-landing/src/lib/tree-layout.ts +0 -136
  34. package/behaviour-tree-workflows-landing/src/lib/yaml-examples.ts +0 -549
  35. package/behaviour-tree-workflows-landing/src/main.tsx +0 -9
  36. package/behaviour-tree-workflows-landing/src/stubs/activepieces.ts +0 -18
  37. package/behaviour-tree-workflows-landing/src/stubs/fs.ts +0 -24
  38. package/behaviour-tree-workflows-landing/src/stubs/path.ts +0 -16
  39. package/behaviour-tree-workflows-landing/src/stubs/temporal-activity.ts +0 -6
  40. package/behaviour-tree-workflows-landing/src/stubs/temporal-workflow.ts +0 -22
  41. package/behaviour-tree-workflows-landing/tsconfig.json +0 -25
  42. package/behaviour-tree-workflows-landing/vite.config.ts +0 -40
  43. package/demo-google-sheets.ts +0 -181
  44. package/demo-runtime-variables.ts +0 -174
  45. package/demo-template.ts +0 -208
  46. package/docs/ARCHITECTURE_SUMMARY.md +0 -613
  47. package/docs/NODE_REFERENCE.md +0 -504
  48. package/docs/README.md +0 -53
  49. package/docs/custom-nodes-architecture.md +0 -826
  50. package/docs/observability.md +0 -175
  51. package/docs/yaml-specification.md +0 -990
  52. package/examples/temporal/README.md +0 -117
  53. package/examples/temporal/activities.ts +0 -373
  54. package/examples/temporal/client.ts +0 -115
  55. package/examples/temporal/python-worker/activities.py +0 -339
  56. package/examples/temporal/python-worker/requirements.txt +0 -12
  57. package/examples/temporal/python-worker/worker.py +0 -106
  58. package/examples/temporal/worker.ts +0 -66
  59. package/examples/temporal/workflows.ts +0 -6
  60. package/examples/temporal/yaml-workflow-loader.ts +0 -105
  61. package/examples/yaml-test.ts +0 -97
  62. package/examples/yaml-workflows/01-simple-sequence.yaml +0 -25
  63. package/examples/yaml-workflows/02-parallel-timeout.yaml +0 -45
  64. package/examples/yaml-workflows/03-ecommerce-checkout.yaml +0 -94
  65. package/examples/yaml-workflows/04-ai-agent-workflow.yaml +0 -346
  66. package/examples/yaml-workflows/05-order-processing.yaml +0 -146
  67. package/examples/yaml-workflows/06-activity-test.yaml +0 -71
  68. package/examples/yaml-workflows/07-activity-simple-test.yaml +0 -43
  69. package/examples/yaml-workflows/08-file-processing.yaml +0 -141
  70. package/examples/yaml-workflows/09-http-request.yaml +0 -137
  71. package/examples/yaml-workflows/README.md +0 -211
  72. package/src/actions/code-execution.schema.ts +0 -27
  73. package/src/actions/code-execution.ts +0 -218
  74. package/src/actions/generate-file.test.ts +0 -516
  75. package/src/actions/generate-file.ts +0 -166
  76. package/src/actions/http-request.test.ts +0 -784
  77. package/src/actions/http-request.ts +0 -228
  78. package/src/actions/index.ts +0 -20
  79. package/src/actions/parse-file.test.ts +0 -448
  80. package/src/actions/parse-file.ts +0 -139
  81. package/src/actions/python-script.test.ts +0 -439
  82. package/src/actions/python-script.ts +0 -154
  83. package/src/base-node.test.ts +0 -511
  84. package/src/base-node.ts +0 -605
  85. package/src/behavior-tree.test.ts +0 -431
  86. package/src/behavior-tree.ts +0 -283
  87. package/src/blackboard.test.ts +0 -222
  88. package/src/blackboard.ts +0 -192
  89. package/src/composites/conditional.schema.ts +0 -19
  90. package/src/composites/conditional.test.ts +0 -309
  91. package/src/composites/conditional.ts +0 -129
  92. package/src/composites/for-each.schema.ts +0 -23
  93. package/src/composites/for-each.test.ts +0 -254
  94. package/src/composites/for-each.ts +0 -132
  95. package/src/composites/index.ts +0 -15
  96. package/src/composites/memory-sequence.schema.ts +0 -19
  97. package/src/composites/memory-sequence.test.ts +0 -223
  98. package/src/composites/memory-sequence.ts +0 -98
  99. package/src/composites/parallel.schema.ts +0 -28
  100. package/src/composites/parallel.test.ts +0 -502
  101. package/src/composites/parallel.ts +0 -157
  102. package/src/composites/reactive-sequence.schema.ts +0 -19
  103. package/src/composites/reactive-sequence.test.ts +0 -170
  104. package/src/composites/reactive-sequence.ts +0 -85
  105. package/src/composites/recovery.schema.ts +0 -19
  106. package/src/composites/recovery.test.ts +0 -366
  107. package/src/composites/recovery.ts +0 -90
  108. package/src/composites/selector.schema.ts +0 -19
  109. package/src/composites/selector.test.ts +0 -387
  110. package/src/composites/selector.ts +0 -85
  111. package/src/composites/sequence.schema.ts +0 -19
  112. package/src/composites/sequence.test.ts +0 -337
  113. package/src/composites/sequence.ts +0 -72
  114. package/src/composites/sub-tree.schema.ts +0 -21
  115. package/src/composites/sub-tree.test.ts +0 -893
  116. package/src/composites/sub-tree.ts +0 -177
  117. package/src/composites/while.schema.ts +0 -24
  118. package/src/composites/while.test.ts +0 -381
  119. package/src/composites/while.ts +0 -149
  120. package/src/data-store/index.ts +0 -10
  121. package/src/data-store/memory-store.ts +0 -161
  122. package/src/data-store/types.ts +0 -94
  123. package/src/debug/breakpoint.test.ts +0 -47
  124. package/src/debug/breakpoint.ts +0 -30
  125. package/src/debug/index.ts +0 -17
  126. package/src/debug/resume-point.test.ts +0 -49
  127. package/src/debug/resume-point.ts +0 -29
  128. package/src/decorators/delay.schema.ts +0 -21
  129. package/src/decorators/delay.test.ts +0 -261
  130. package/src/decorators/delay.ts +0 -140
  131. package/src/decorators/force-result.schema.ts +0 -32
  132. package/src/decorators/force-result.test.ts +0 -133
  133. package/src/decorators/force-result.ts +0 -63
  134. package/src/decorators/index.ts +0 -13
  135. package/src/decorators/invert.schema.ts +0 -19
  136. package/src/decorators/invert.test.ts +0 -135
  137. package/src/decorators/invert.ts +0 -42
  138. package/src/decorators/keep-running.schema.ts +0 -20
  139. package/src/decorators/keep-running.test.ts +0 -105
  140. package/src/decorators/keep-running.ts +0 -49
  141. package/src/decorators/precondition.schema.ts +0 -19
  142. package/src/decorators/precondition.test.ts +0 -351
  143. package/src/decorators/precondition.ts +0 -139
  144. package/src/decorators/repeat.schema.ts +0 -21
  145. package/src/decorators/repeat.test.ts +0 -187
  146. package/src/decorators/repeat.ts +0 -94
  147. package/src/decorators/run-once.schema.ts +0 -19
  148. package/src/decorators/run-once.test.ts +0 -140
  149. package/src/decorators/run-once.ts +0 -61
  150. package/src/decorators/soft-assert.schema.ts +0 -19
  151. package/src/decorators/soft-assert.test.ts +0 -107
  152. package/src/decorators/soft-assert.ts +0 -68
  153. package/src/decorators/timeout.schema.ts +0 -21
  154. package/src/decorators/timeout.test.ts +0 -274
  155. package/src/decorators/timeout.ts +0 -159
  156. package/src/errors.test.ts +0 -63
  157. package/src/errors.ts +0 -34
  158. package/src/events.test.ts +0 -347
  159. package/src/events.ts +0 -183
  160. package/src/index.ts +0 -80
  161. package/src/integrations/index.ts +0 -30
  162. package/src/integrations/integration-action.test.ts +0 -571
  163. package/src/integrations/integration-action.ts +0 -233
  164. package/src/integrations/piece-executor.ts +0 -320
  165. package/src/observability/execution-tracker.ts +0 -320
  166. package/src/observability/index.ts +0 -23
  167. package/src/observability/sinks.ts +0 -138
  168. package/src/observability/types.ts +0 -130
  169. package/src/registry-utils.ts +0 -147
  170. package/src/registry.test.ts +0 -466
  171. package/src/registry.ts +0 -334
  172. package/src/schemas/base.schema.ts +0 -104
  173. package/src/schemas/index.ts +0 -223
  174. package/src/schemas/integration.test.ts +0 -238
  175. package/src/schemas/tree-definition.schema.ts +0 -170
  176. package/src/schemas/validation.test.ts +0 -146
  177. package/src/schemas/validation.ts +0 -122
  178. package/src/scripting/index.ts +0 -22
  179. package/src/templates/template-loader.test.ts +0 -281
  180. package/src/templates/template-loader.ts +0 -152
  181. package/src/temporal-integration.test.ts +0 -213
  182. package/src/test-nodes.ts +0 -259
  183. package/src/types.ts +0 -503
  184. package/src/utilities/index.ts +0 -17
  185. package/src/utilities/log-message.test.ts +0 -275
  186. package/src/utilities/log-message.ts +0 -134
  187. package/src/utilities/regex-extract.test.ts +0 -138
  188. package/src/utilities/regex-extract.ts +0 -108
  189. package/src/utilities/variable-resolver.test.ts +0 -416
  190. package/src/utilities/variable-resolver.ts +0 -318
  191. package/src/utils/error-handler.test.ts +0 -117
  192. package/src/utils/error-handler.ts +0 -48
  193. package/src/utils/signal-check.test.ts +0 -234
  194. package/src/utils/signal-check.ts +0 -140
  195. package/src/yaml/errors.ts +0 -143
  196. package/src/yaml/index.ts +0 -30
  197. package/src/yaml/loader.ts +0 -39
  198. package/src/yaml/parser.ts +0 -286
  199. package/src/yaml/validation/semantic-validator.ts +0 -196
  200. package/templates/google-sheets/insert-row.yaml +0 -76
  201. package/templates/notification-sender.yaml +0 -33
  202. package/templates/order-validation.yaml +0 -44
  203. package/tsconfig.json +0 -24
  204. package/vitest.config.ts +0 -25
  205. package/workflows/order-processor.yaml +0 -59
  206. package/workflows/process-order-workflow.yaml +0 -142
@@ -1,281 +0,0 @@
1
- /**
2
- * Template Loader Tests
3
- * TDD: Write tests first, then implement to make them pass
4
- */
5
-
6
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
7
- import * as fs from "fs";
8
- import * as path from "path";
9
- import * as os from "os";
10
- import { Registry } from "../registry.js";
11
- import { registerStandardNodes } from "../registry-utils.js";
12
- import {
13
- loadTemplatesFromDirectory,
14
- loadTemplate,
15
- } from "./template-loader.js";
16
-
17
- describe("Template Loader", () => {
18
- let registry: Registry;
19
- let tempDir: string;
20
-
21
- beforeEach(() => {
22
- // Create fresh registry with standard nodes
23
- registry = new Registry();
24
- registerStandardNodes(registry);
25
-
26
- // Create temp directory for test templates
27
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "btree-templates-"));
28
- });
29
-
30
- afterEach(() => {
31
- // Clean up temp directory
32
- fs.rmSync(tempDir, { recursive: true, force: true });
33
- });
34
-
35
- describe("loadTemplatesFromDirectory", () => {
36
- it("should load all YAML files from directory", async () => {
37
- // Create test template files
38
- const template1 = `
39
- type: Sequence
40
- id: template-one
41
- children:
42
- - type: LogMessage
43
- id: log1
44
- props:
45
- message: "Template 1"
46
- `;
47
- const template2 = `
48
- type: Sequence
49
- id: template-two
50
- children:
51
- - type: LogMessage
52
- id: log2
53
- props:
54
- message: "Template 2"
55
- `;
56
-
57
- fs.writeFileSync(path.join(tempDir, "template-one.yaml"), template1);
58
- fs.writeFileSync(path.join(tempDir, "template-two.yaml"), template2);
59
-
60
- const loadedIds = await loadTemplatesFromDirectory(registry, {
61
- templatesDir: tempDir,
62
- });
63
-
64
- expect(loadedIds).toHaveLength(2);
65
- expect(loadedIds).toContain("template-one");
66
- expect(loadedIds).toContain("template-two");
67
- });
68
-
69
- it("should register templates in the registry", async () => {
70
- const template = `
71
- type: Sequence
72
- id: my-template
73
- children:
74
- - type: LogMessage
75
- id: log
76
- props:
77
- message: "Hello"
78
- `;
79
- fs.writeFileSync(path.join(tempDir, "my-template.yaml"), template);
80
-
81
- await loadTemplatesFromDirectory(registry, { templatesDir: tempDir });
82
-
83
- expect(registry.hasTree("my-template")).toBe(true);
84
- });
85
-
86
- it("should use filename as template ID", async () => {
87
- const template = `
88
- type: Sequence
89
- id: internal-id
90
- children:
91
- - type: LogMessage
92
- id: log
93
- props:
94
- message: "Test"
95
- `;
96
- fs.writeFileSync(path.join(tempDir, "external-name.yaml"), template);
97
-
98
- await loadTemplatesFromDirectory(registry, { templatesDir: tempDir });
99
-
100
- // Template ID should be filename, not internal id
101
- expect(registry.hasTree("external-name")).toBe(true);
102
- });
103
-
104
- it("should support .yml extension", async () => {
105
- const template = `
106
- type: Sequence
107
- id: yml-template
108
- children:
109
- - type: LogMessage
110
- id: log
111
- props:
112
- message: "YML extension"
113
- `;
114
- fs.writeFileSync(path.join(tempDir, "yml-template.yml"), template);
115
-
116
- const loadedIds = await loadTemplatesFromDirectory(registry, {
117
- templatesDir: tempDir,
118
- });
119
-
120
- expect(loadedIds).toContain("yml-template");
121
- });
122
-
123
- it("should apply ID prefix when provided", async () => {
124
- const template = `
125
- type: Sequence
126
- id: test
127
- children:
128
- - type: LogMessage
129
- id: log
130
- props:
131
- message: "Prefixed"
132
- `;
133
- fs.writeFileSync(path.join(tempDir, "my-template.yaml"), template);
134
-
135
- await loadTemplatesFromDirectory(registry, {
136
- templatesDir: tempDir,
137
- idPrefix: "tpl:",
138
- });
139
-
140
- expect(registry.hasTree("tpl:my-template")).toBe(true);
141
- });
142
-
143
- it("should return empty array for empty directory", async () => {
144
- const loadedIds = await loadTemplatesFromDirectory(registry, {
145
- templatesDir: tempDir,
146
- });
147
-
148
- expect(loadedIds).toHaveLength(0);
149
- });
150
-
151
- it("should ignore non-YAML files", async () => {
152
- fs.writeFileSync(path.join(tempDir, "readme.md"), "# README");
153
- fs.writeFileSync(path.join(tempDir, "config.json"), "{}");
154
-
155
- const template = `
156
- type: Sequence
157
- id: only-yaml
158
- children:
159
- - type: LogMessage
160
- id: log
161
- props:
162
- message: "Only YAML"
163
- `;
164
- fs.writeFileSync(path.join(tempDir, "valid.yaml"), template);
165
-
166
- const loadedIds = await loadTemplatesFromDirectory(registry, {
167
- templatesDir: tempDir,
168
- });
169
-
170
- expect(loadedIds).toHaveLength(1);
171
- expect(loadedIds).toContain("valid");
172
- });
173
-
174
- it("should throw error for invalid YAML syntax", async () => {
175
- fs.writeFileSync(path.join(tempDir, "invalid.yaml"), "invalid: yaml: content:");
176
-
177
- await expect(
178
- loadTemplatesFromDirectory(registry, { templatesDir: tempDir })
179
- ).rejects.toThrow();
180
- });
181
-
182
- it("should throw error for invalid tree structure", async () => {
183
- const invalidTemplate = `
184
- type: UnknownNodeType
185
- id: invalid
186
- `;
187
- fs.writeFileSync(path.join(tempDir, "invalid.yaml"), invalidTemplate);
188
-
189
- await expect(
190
- loadTemplatesFromDirectory(registry, { templatesDir: tempDir })
191
- ).rejects.toThrow();
192
- });
193
- });
194
-
195
- describe("loadTemplate", () => {
196
- it("should load a single template file", () => {
197
- const template = `
198
- type: Sequence
199
- id: single
200
- children:
201
- - type: LogMessage
202
- id: log
203
- props:
204
- message: "Single template"
205
- `;
206
- const filePath = path.join(tempDir, "single.yaml");
207
- fs.writeFileSync(filePath, template);
208
-
209
- const id = loadTemplate(registry, filePath);
210
-
211
- expect(id).toBe("single");
212
- expect(registry.hasTree("single")).toBe(true);
213
- });
214
-
215
- it("should use custom ID when provided", () => {
216
- const template = `
217
- type: Sequence
218
- id: default
219
- children:
220
- - type: LogMessage
221
- id: log
222
- props:
223
- message: "Custom ID"
224
- `;
225
- const filePath = path.join(tempDir, "default.yaml");
226
- fs.writeFileSync(filePath, template);
227
-
228
- const id = loadTemplate(registry, filePath, "custom-id");
229
-
230
- expect(id).toBe("custom-id");
231
- expect(registry.hasTree("custom-id")).toBe(true);
232
- });
233
-
234
- it("should throw error for non-existent file", () => {
235
- expect(() => {
236
- loadTemplate(registry, "/non/existent/path.yaml");
237
- }).toThrow();
238
- });
239
- });
240
-
241
- describe("Template execution via SubTree", () => {
242
- it("should allow SubTree to reference loaded template", async () => {
243
- // Create and load a template
244
- const template = `
245
- type: Sequence
246
- id: reusable
247
- children:
248
- - type: LogMessage
249
- id: log
250
- props:
251
- message: "Reusable template executed"
252
- `;
253
- const filePath = path.join(tempDir, "reusable.yaml");
254
- fs.writeFileSync(filePath, template);
255
- loadTemplate(registry, filePath);
256
-
257
- // Verify template can be cloned (as SubTree would do)
258
- const clonedTree = registry.cloneTree("reusable");
259
- expect(clonedTree).toBeDefined();
260
- expect(clonedTree.getRoot()).toBeDefined();
261
- });
262
-
263
- it("should track source file for templates", () => {
264
- const template = `
265
- type: Sequence
266
- id: tracked
267
- children:
268
- - type: LogMessage
269
- id: log
270
- props:
271
- message: "Tracked"
272
- `;
273
- const filePath = path.join(tempDir, "tracked.yaml");
274
- fs.writeFileSync(filePath, template);
275
- loadTemplate(registry, filePath);
276
-
277
- const sourceFile = registry.getTreeSourceFile("tracked");
278
- expect(sourceFile).toBe(filePath);
279
- });
280
- });
281
- });
@@ -1,152 +0,0 @@
1
- /**
2
- * Template Loader
3
- * Load YAML template files from a directory and register them in the btree Registry.
4
- * Templates can then be referenced by SubTree nodes using their template ID.
5
- */
6
-
7
- import * as fs from "fs";
8
- import * as path from "path";
9
- import { Registry } from "../registry.js";
10
- import { loadTreeFromYaml } from "../yaml/index.js";
11
- import { BehaviorTree } from "../behavior-tree.js";
12
-
13
- /**
14
- * Options for loading templates from a directory
15
- */
16
- export interface TemplateLoaderOptions {
17
- /** Path to the templates directory */
18
- templatesDir: string;
19
- /** Optional prefix to add to all template IDs (e.g., "tpl:") */
20
- idPrefix?: string;
21
- }
22
-
23
- /**
24
- * Load all template YAML files from a directory and register them in the registry.
25
- *
26
- * Each YAML file becomes a registered BehaviorTree that can be referenced
27
- * by SubTree nodes using the filename (without extension) as the tree ID.
28
- *
29
- * @param registry - The Registry instance to register templates in
30
- * @param options - Configuration options
31
- * @returns Array of template IDs that were loaded
32
- *
33
- * @example
34
- * ```typescript
35
- * const registry = new Registry();
36
- * registerStandardNodes(registry);
37
- *
38
- * const loadedIds = await loadTemplatesFromDirectory(registry, {
39
- * templatesDir: './templates',
40
- * idPrefix: 'tpl:'
41
- * });
42
- *
43
- * // Templates can now be used via SubTree:
44
- * // type: SubTree
45
- * // props:
46
- * // treeId: "tpl:order-validation"
47
- * ```
48
- */
49
- export async function loadTemplatesFromDirectory(
50
- registry: Registry,
51
- options: TemplateLoaderOptions
52
- ): Promise<string[]> {
53
- const { templatesDir, idPrefix = "" } = options;
54
- const loadedIds: string[] = [];
55
-
56
- // Ensure directory exists
57
- if (!fs.existsSync(templatesDir)) {
58
- console.warn(
59
- `[TemplateLoader] Templates directory not found: ${templatesDir}`
60
- );
61
- return loadedIds;
62
- }
63
-
64
- // Find all YAML files in directory
65
- const files = fs
66
- .readdirSync(templatesDir)
67
- .filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
68
-
69
- // Load each file
70
- for (const file of files) {
71
- const filePath = path.join(templatesDir, file);
72
-
73
- try {
74
- // Read and parse YAML content
75
- const yamlContent = fs.readFileSync(filePath, "utf-8");
76
- const rootNode = loadTreeFromYaml(yamlContent, registry);
77
- const tree = new BehaviorTree(rootNode);
78
-
79
- // Derive template ID from filename (without extension)
80
- const baseName = path.basename(file, path.extname(file));
81
- const templateId = idPrefix + baseName;
82
-
83
- // Register in registry
84
- registry.registerTree(templateId, tree, filePath);
85
- loadedIds.push(templateId);
86
-
87
- console.log(`[TemplateLoader] Registered template: ${templateId}`);
88
- } catch (error) {
89
- const errorMessage =
90
- error instanceof Error ? error.message : String(error);
91
- throw new Error(
92
- `Failed to load template '${file}': ${errorMessage}`
93
- );
94
- }
95
- }
96
-
97
- console.log(
98
- `[TemplateLoader] Loaded ${loadedIds.length} template(s) from ${templatesDir}`
99
- );
100
- return loadedIds;
101
- }
102
-
103
- /**
104
- * Load a single template file and register it in the registry.
105
- *
106
- * @param registry - The Registry instance to register the template in
107
- * @param filePath - Path to the YAML template file
108
- * @param templateId - Optional custom ID for the template (defaults to filename)
109
- * @returns The template ID that was registered
110
- *
111
- * @example
112
- * ```typescript
113
- * const id = loadTemplate(registry, './templates/order-validation.yaml');
114
- * // Template is now available as "order-validation"
115
- *
116
- * const customId = loadTemplate(registry, './special.yaml', 'my-custom-id');
117
- * // Template is now available as "my-custom-id"
118
- * ```
119
- */
120
- export function loadTemplate(
121
- registry: Registry,
122
- filePath: string,
123
- templateId?: string
124
- ): string {
125
- // Read and parse YAML content
126
- const yamlContent = fs.readFileSync(filePath, "utf-8");
127
- const rootNode = loadTreeFromYaml(yamlContent, registry);
128
- const tree = new BehaviorTree(rootNode);
129
-
130
- // Derive template ID from filename if not provided
131
- const id = templateId || path.basename(filePath, path.extname(filePath));
132
-
133
- // Register in registry
134
- registry.registerTree(id, tree, filePath);
135
-
136
- console.log(`[TemplateLoader] Registered template: ${id} from ${filePath}`);
137
- return id;
138
- }
139
-
140
- /**
141
- * Check if a template exists in the registry
142
- */
143
- export function hasTemplate(registry: Registry, templateId: string): boolean {
144
- return registry.hasTree(templateId);
145
- }
146
-
147
- /**
148
- * Get all registered template IDs
149
- */
150
- export function getTemplateIds(registry: Registry): string[] {
151
- return registry.getAllTreeIds();
152
- }
@@ -1,213 +0,0 @@
1
- /**
2
- * Temporal Integration Tests
3
- * Validates that behavior trees work correctly as Temporal workflows
4
- */
5
-
6
- import { describe, it, expect, beforeAll, afterAll } from "vitest";
7
- import { TestWorkflowEnvironment } from "@temporalio/testing";
8
- import { BehaviorTree } from "./behavior-tree.js";
9
- import { Sequence } from "./composites/sequence.js";
10
- import { Selector } from "./composites/selector.js";
11
- import { Parallel, ParallelStrategy } from "./composites/parallel.js";
12
- import { PrintAction, MockAction, CounterAction } from "./test-nodes.js";
13
- import { Timeout } from "./decorators/timeout.js";
14
- import { Delay } from "./decorators/delay.js";
15
- import { NodeStatus } from "./types.js";
16
- import { Registry } from "./registry.js";
17
-
18
- describe.skip("Temporal Integration", () => {
19
- let testEnv: TestWorkflowEnvironment;
20
-
21
- beforeAll(async () => {
22
- testEnv = await TestWorkflowEnvironment.createLocal();
23
- }, 60000); // 60 second timeout for creating test environment
24
-
25
- afterAll(async () => {
26
- await testEnv?.teardown();
27
- });
28
-
29
- it("should execute simple sequence workflow", async () => {
30
- const root = new Sequence({ id: "root" });
31
- root.addChild(new PrintAction({ id: "step1", message: "Hello" }));
32
- root.addChild(new PrintAction({ id: "step2", message: "World" }));
33
-
34
- const tree = new BehaviorTree(root);
35
- const workflow = tree.toWorkflow();
36
-
37
- const treeRegistry = new Registry();
38
- const result = await workflow({ input: {}, treeRegistry });
39
-
40
- expect(result.status).toBe(NodeStatus.SUCCESS);
41
- expect(result.output).toBeDefined();
42
- });
43
-
44
- it("should handle selector (fallback) logic", async () => {
45
- const root = new Selector({ id: "root" });
46
- root.addChild(new MockAction({ id: "fail1", returnStatus: NodeStatus.FAILURE }));
47
- root.addChild(new MockAction({ id: "fail2", returnStatus: NodeStatus.FAILURE }));
48
- root.addChild(new MockAction({ id: "success", returnStatus: NodeStatus.SUCCESS }));
49
-
50
- const tree = new BehaviorTree(root);
51
- const workflow = tree.toWorkflow();
52
-
53
- const treeRegistry = new Registry();
54
- const result = await workflow({ input: {}, treeRegistry });
55
-
56
- expect(result.status).toBe(NodeStatus.SUCCESS);
57
- });
58
-
59
- it("should execute parallel nodes concurrently", async () => {
60
- const root = new Parallel({
61
- id: "root",
62
- strategy: ParallelStrategy.JOIN_ALL,
63
- threshold: 3,
64
- });
65
- root.addChild(new MockAction({ id: "task1", returnStatus: NodeStatus.SUCCESS }));
66
- root.addChild(new MockAction({ id: "task2", returnStatus: NodeStatus.SUCCESS }));
67
- root.addChild(new MockAction({ id: "task3", returnStatus: NodeStatus.SUCCESS }));
68
-
69
- const tree = new BehaviorTree(root);
70
- const workflow = tree.toWorkflow();
71
-
72
- const treeRegistry = new Registry();
73
- const result = await workflow({ input: {}, treeRegistry });
74
-
75
- expect(result.status).toBe(NodeStatus.SUCCESS);
76
- });
77
-
78
- it("should handle blackboard input and output", async () => {
79
- const root = new Sequence({ id: "root" });
80
- root.addChild(new CounterAction({ id: "increment1", counterKey: "count", increment: 5 }));
81
- root.addChild(new CounterAction({ id: "increment2", counterKey: "count", increment: 3 }));
82
-
83
- const tree = new BehaviorTree(root);
84
- const workflow = tree.toWorkflow();
85
-
86
- const treeRegistry = new Registry();
87
- const result = await workflow({
88
- input: { count: 10 },
89
- treeRegistry
90
- });
91
-
92
- expect(result.status).toBe(NodeStatus.SUCCESS);
93
- expect(result.output.count).toBe(18); // 10 + 5 + 3
94
- });
95
-
96
- // NOTE: Retry decorator removed - use Temporal's native RetryPolicy instead
97
-
98
- it("should handle timeout decorator", async () => {
99
- const timeout = new Timeout({
100
- id: "timeout",
101
- timeoutMs: 100,
102
- });
103
- const fastAction = new MockAction({
104
- id: "fast",
105
- returnStatus: NodeStatus.SUCCESS,
106
- ticksBeforeComplete: 1,
107
- });
108
- timeout.setChild(fastAction);
109
-
110
- const tree = new BehaviorTree(timeout);
111
- const workflow = tree.toWorkflow();
112
-
113
- const treeRegistry = new Registry();
114
- const result = await workflow({ input: {}, treeRegistry });
115
-
116
- expect(result.status).toBe(NodeStatus.SUCCESS);
117
- });
118
-
119
- it("should handle delay decorator", async () => {
120
- const delay = new Delay({
121
- id: "delay",
122
- delayMs: 50,
123
- });
124
- const action = new PrintAction({
125
- id: "action",
126
- message: "Delayed action",
127
- });
128
- delay.setChild(action);
129
-
130
- const tree = new BehaviorTree(delay);
131
- const workflow = tree.toWorkflow();
132
-
133
- const treeRegistry = new Registry();
134
- const startTime = Date.now();
135
- const result = await workflow({ input: {}, treeRegistry });
136
- const duration = Date.now() - startTime;
137
-
138
- expect(result.status).toBe(NodeStatus.SUCCESS);
139
- expect(duration).toBeGreaterThanOrEqual(50);
140
- });
141
-
142
- it("should handle complex nested trees", async () => {
143
- // Create a complex tree: Sequence -> (Selector, Parallel, Action)
144
- const root = new Sequence({ id: "root" });
145
-
146
- const selector = new Selector({ id: "selector" });
147
- selector.addChild(new MockAction({ id: "fail", returnStatus: NodeStatus.FAILURE }));
148
- selector.addChild(new MockAction({ id: "success", returnStatus: NodeStatus.SUCCESS }));
149
-
150
- const parallel = new Parallel({
151
- id: "parallel",
152
- strategy: ParallelStrategy.JOIN_ALL,
153
- threshold: 2,
154
- });
155
- parallel.addChild(new PrintAction({ id: "p1", message: "Parallel 1" }));
156
- parallel.addChild(new PrintAction({ id: "p2", message: "Parallel 2" }));
157
-
158
- const action = new MockAction({ id: "finalAction", returnStatus: NodeStatus.SUCCESS });
159
-
160
- root.addChildren([selector, parallel, action]);
161
-
162
- const tree = new BehaviorTree(root);
163
- const workflow = tree.toWorkflow();
164
-
165
- const treeRegistry = new Registry();
166
- const result = await workflow({ input: {}, treeRegistry });
167
-
168
- expect(result.status).toBe(NodeStatus.SUCCESS);
169
- });
170
-
171
- it("should preserve workflow info in context", async () => {
172
- let capturedWorkflowInfo: any = null;
173
-
174
- const action = new MockAction({
175
- id: "capture",
176
- returnStatus: NodeStatus.SUCCESS,
177
- });
178
-
179
- // Override tick to capture workflow info
180
- const originalTick = action.tick.bind(action);
181
- action.tick = async (context) => {
182
- capturedWorkflowInfo = context.workflowInfo;
183
- return originalTick(context);
184
- };
185
-
186
- const tree = new BehaviorTree(action);
187
- const workflow = tree.toWorkflow();
188
-
189
- const treeRegistry = new Registry();
190
- const result = await workflow({ input: {}, treeRegistry });
191
-
192
- expect(result.status).toBe(NodeStatus.SUCCESS);
193
- // In test environment, workflowInfo might not be available
194
- // This test just validates the structure doesn't break
195
- });
196
-
197
- it("should handle session ID properly", async () => {
198
- const root = new PrintAction({ id: "root", message: "Session test" });
199
-
200
- const tree = new BehaviorTree(root);
201
- const workflow = tree.toWorkflow();
202
-
203
- const treeRegistry = new Registry();
204
- const customSessionId = "test-session-123";
205
- const result = await workflow({
206
- input: {},
207
- treeRegistry,
208
- sessionId: customSessionId,
209
- });
210
-
211
- expect(result.status).toBe(NodeStatus.SUCCESS);
212
- });
213
- });