@noetic-tools/core 0.0.1 → 0.1.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.
Files changed (254) hide show
  1. package/README.md +31 -11
  2. package/dist/adapters/in-memory-fs-adapter.d.ts +1 -1
  3. package/dist/adapters/in-memory-fs-adapter.js +1 -1
  4. package/dist/adapters/in-memory-shell-adapter.d.ts +1 -1
  5. package/dist/adapters/in-memory-subprocess/manifest-persistence.d.ts +8 -0
  6. package/dist/adapters/in-memory-subprocess/manifest-persistence.js +56 -0
  7. package/dist/adapters/in-memory-subprocess/metadata.d.ts +12 -0
  8. package/dist/adapters/in-memory-subprocess/metadata.js +58 -0
  9. package/dist/adapters/in-memory-subprocess/step-completion.d.ts +3 -0
  10. package/dist/adapters/in-memory-subprocess/step-completion.js +86 -0
  11. package/dist/adapters/in-memory-subprocess/types.d.ts +88 -0
  12. package/dist/adapters/in-memory-subprocess/types.js +2 -0
  13. package/dist/adapters/in-memory-subprocess-adapter.d.ts +12 -4
  14. package/dist/adapters/in-memory-subprocess-adapter.js +164 -55
  15. package/dist/adapters/openrouter.d.ts +16 -7
  16. package/dist/adapters/openrouter.js +68 -7
  17. package/dist/builders/channel-builder.d.ts +1 -1
  18. package/dist/builders/control-flow-builders.d.ts +3 -3
  19. package/dist/builders/control-flow-builders.js +9 -4
  20. package/dist/builders/every.d.ts +3 -3
  21. package/dist/builders/every.js +5 -2
  22. package/dist/builders/layer-provides-builders.d.ts +1 -32
  23. package/dist/builders/layer-provides-builders.js +1 -33
  24. package/dist/builders/loop-builder.d.ts +2 -2
  25. package/dist/builders/loop-builder.js +5 -2
  26. package/dist/builders/memory-builder.d.ts +1 -1
  27. package/dist/builders/memory-builder.js +1 -1
  28. package/dist/builders/provide-builder.d.ts +2 -2
  29. package/dist/builders/provide-builder.js +5 -2
  30. package/dist/builders/spawn-builder.d.ts +22 -9
  31. package/dist/builders/spawn-builder.js +17 -3
  32. package/dist/builders/step-builders.d.ts +51 -30
  33. package/dist/builders/step-builders.js +31 -12
  34. package/dist/builders/tool-builder.d.ts +7 -3
  35. package/dist/builders/tool-builder.js +3 -1
  36. package/dist/builders/workflow-hydrator.d.ts +38 -0
  37. package/dist/builders/workflow-hydrator.js +336 -0
  38. package/dist/conditions/conditions.d.ts +4 -4
  39. package/dist/conditions/conditions.js +2 -2
  40. package/dist/errors/noetic-error.d.ts +1 -1
  41. package/dist/errors/noetic-error.js +2 -0
  42. package/dist/{runtime → harness}/agent-harness.d.ts +65 -45
  43. package/dist/harness/agent-harness.js +576 -0
  44. package/dist/harness/deps/adapters.d.ts +4 -0
  45. package/dist/harness/deps/adapters.js +4 -0
  46. package/dist/harness/deps/interpreter.d.ts +5 -0
  47. package/dist/harness/deps/interpreter.js +4 -0
  48. package/dist/harness/deps/memory.d.ts +5 -0
  49. package/dist/harness/deps/memory.js +4 -0
  50. package/dist/harness/deps/runtime.d.ts +10 -0
  51. package/dist/harness/deps/runtime.js +7 -0
  52. package/dist/harness/deps/types.d.ts +17 -0
  53. package/dist/harness/deps/types.js +1 -0
  54. package/dist/harness/model-call.d.ts +50 -0
  55. package/dist/harness/model-call.js +694 -0
  56. package/dist/harness/model-schema.d.ts +19 -0
  57. package/dist/harness/model-schema.js +46 -0
  58. package/dist/harness/step-bootstrap.d.ts +25 -0
  59. package/dist/harness/step-bootstrap.js +170 -0
  60. package/dist/index.d.ts +114 -76
  61. package/dist/index.js +74 -50
  62. package/dist/internal-test.d.ts +4 -4
  63. package/dist/internal-test.js +4 -4
  64. package/dist/interpreter/action-deps.d.ts +12 -0
  65. package/dist/interpreter/action-deps.js +10 -0
  66. package/dist/interpreter/action-types.d.ts +8 -0
  67. package/dist/interpreter/action-types.js +1 -0
  68. package/dist/interpreter/collect-tools.d.ts +2 -2
  69. package/dist/interpreter/collect-tools.js +6 -2
  70. package/dist/interpreter/execute-action.d.ts +22 -0
  71. package/dist/interpreter/execute-action.js +527 -0
  72. package/dist/interpreter/{execute-every.d.ts → execute-control.d.ts} +8 -2
  73. package/dist/interpreter/execute-control.js +530 -0
  74. package/dist/interpreter/execute.d.ts +16 -3
  75. package/dist/interpreter/execute.js +154 -51
  76. package/dist/interpreter/message-helpers.d.ts +1 -9
  77. package/dist/interpreter/message-helpers.js +1 -42
  78. package/dist/interpreter/step-dispatcher.d.ts +61 -0
  79. package/dist/interpreter/step-dispatcher.js +67 -0
  80. package/dist/interpreter/typeguards.d.ts +8 -15
  81. package/dist/interpreter/typeguards.js +4 -21
  82. package/dist/memory/budget.d.ts +2 -2
  83. package/dist/memory/budget.js +1 -1
  84. package/dist/{runtime → memory}/exec-context-factory.d.ts +2 -2
  85. package/dist/{runtime → memory}/exec-context-factory.js +1 -1
  86. package/dist/memory/flow-schema.d.ts +52 -0
  87. package/dist/memory/flow-schema.js +102 -0
  88. package/dist/memory/function-call-utils.d.ts +1 -1
  89. package/dist/memory/function-call-utils.js +4 -2
  90. package/dist/memory/layer-api.d.ts +4 -4
  91. package/dist/memory/layer-api.js +2 -2
  92. package/dist/memory/layer-lifecycle.d.ts +6 -6
  93. package/dist/memory/layer-lifecycle.js +6 -6
  94. package/dist/memory/layer-provides.d.ts +32 -0
  95. package/dist/memory/layer-provides.js +33 -0
  96. package/dist/memory/layer-usage.d.ts +4 -3
  97. package/dist/memory/layer-usage.js +9 -3
  98. package/dist/memory/layers/durable-task-state.d.ts +8 -8
  99. package/dist/memory/layers/durable-task-state.js +2 -2
  100. package/dist/memory/layers/file-reference.d.ts +1 -1
  101. package/dist/memory/layers/file-reference.js +303 -247
  102. package/dist/memory/layers/history-window.d.ts +1 -1
  103. package/dist/memory/layers/history-window.js +4 -4
  104. package/dist/memory/layers/observational-memory.d.ts +5 -5
  105. package/dist/memory/layers/observational-memory.js +2 -3
  106. package/dist/memory/layers/plan.d.ts +2 -2
  107. package/dist/memory/layers/plan.js +15 -10
  108. package/dist/memory/layers/static-content.d.ts +2 -2
  109. package/dist/memory/layers/static-content.js +1 -1
  110. package/dist/memory/layers/steering.d.ts +7 -7
  111. package/dist/memory/layers/steering.js +5 -5
  112. package/dist/memory/layers/tool-memory-layer.d.ts +2 -2
  113. package/dist/memory/layers/tool-memory-layer.js +1 -1
  114. package/dist/memory/layers/working-memory.d.ts +7 -7
  115. package/dist/memory/layers/working-memory.js +4 -4
  116. package/dist/memory/projector.d.ts +2 -2
  117. package/dist/memory/scope.d.ts +1 -1
  118. package/dist/memory/strip-unresolved.d.ts +1 -1
  119. package/dist/observability/span-impl.d.ts +1 -1
  120. package/dist/observability/trace-exporter.d.ts +2 -2
  121. package/dist/observability/trace-exporter.js +1 -1
  122. package/dist/patterns/dynamic-workflow.d.ts +55 -0
  123. package/dist/patterns/dynamic-workflow.js +194 -0
  124. package/dist/patterns/flow.d.ts +2 -52
  125. package/dist/patterns/flow.js +1 -103
  126. package/dist/patterns/interview.d.ts +2 -2
  127. package/dist/patterns/interview.js +5 -5
  128. package/dist/patterns/plans.d.ts +6 -6
  129. package/dist/patterns/plans.js +4 -4
  130. package/dist/patterns/ralph-wiggum.d.ts +4 -4
  131. package/dist/patterns/ralph-wiggum.js +5 -5
  132. package/dist/patterns/react.d.ts +3 -3
  133. package/dist/patterns/react.js +5 -5
  134. package/dist/portable/adapters.d.ts +6 -0
  135. package/dist/portable/adapters.js +6 -0
  136. package/dist/portable/ask-user.d.ts +4 -0
  137. package/dist/portable/ask-user.js +2 -0
  138. package/dist/portable/builders.d.ts +12 -0
  139. package/dist/portable/builders.js +12 -0
  140. package/dist/portable/memory-layers.d.ts +12 -0
  141. package/dist/portable/memory-layers.js +12 -0
  142. package/dist/portable/runtime.d.ts +4 -0
  143. package/dist/portable/runtime.js +4 -0
  144. package/dist/portable/until.d.ts +4 -0
  145. package/dist/portable/until.js +4 -0
  146. package/dist/portable.d.ts +8 -26
  147. package/dist/portable.js +8 -34
  148. package/dist/runtime/broadcaster-utils.d.ts +7 -3
  149. package/dist/runtime/broadcaster-utils.js +11 -6
  150. package/dist/runtime/channel-store.d.ts +1 -1
  151. package/dist/runtime/channel-store.js +2 -2
  152. package/dist/runtime/context-deps.d.ts +4 -0
  153. package/dist/runtime/context-deps.js +2 -0
  154. package/dist/runtime/context-impl.d.ts +42 -12
  155. package/dist/runtime/context-impl.js +52 -5
  156. package/dist/runtime/context-type-deps.d.ts +14 -0
  157. package/dist/runtime/context-type-deps.js +1 -0
  158. package/dist/runtime/cwd-helpers.d.ts +1 -1
  159. package/dist/runtime/detached-handle.d.ts +52 -4
  160. package/dist/runtime/detached-handle.js +218 -14
  161. package/dist/runtime/durable/checkpoint-store.d.ts +38 -0
  162. package/dist/runtime/durable/checkpoint-store.js +101 -0
  163. package/dist/runtime/durable/detached-signal.d.ts +37 -0
  164. package/dist/runtime/durable/detached-signal.js +52 -0
  165. package/dist/runtime/durable/harness-checkpoints.d.ts +51 -0
  166. package/dist/runtime/durable/harness-checkpoints.js +104 -0
  167. package/dist/runtime/durable/index.d.ts +10 -0
  168. package/dist/runtime/durable/index.js +5 -0
  169. package/dist/runtime/durable/runnable-loop.d.ts +100 -0
  170. package/dist/runtime/durable/runnable-loop.js +90 -0
  171. package/dist/runtime/durable/session-seed.d.ts +28 -0
  172. package/dist/runtime/durable/session-seed.js +27 -0
  173. package/dist/runtime/durable/stall-nudge.d.ts +82 -0
  174. package/dist/runtime/durable/stall-nudge.js +87 -0
  175. package/dist/runtime/event-broadcaster.d.ts +1 -1
  176. package/dist/runtime/in-memory-storage.d.ts +1 -1
  177. package/dist/runtime/in-memory-storage.js +1 -1
  178. package/dist/runtime/item-log-impl.d.ts +3 -3
  179. package/dist/runtime/item-log-impl.js +1 -1
  180. package/dist/runtime/message-queue.d.ts +2 -2
  181. package/dist/runtime/session-runner.d.ts +7 -7
  182. package/dist/runtime/session-runner.js +3 -3
  183. package/dist/runtime/session-streams.d.ts +3 -3
  184. package/dist/runtime/session-streams.js +1 -1
  185. package/dist/runtime/step-registry.d.ts +42 -0
  186. package/dist/runtime/step-registry.js +77 -0
  187. package/dist/runtime/tool-memory.d.ts +3 -3
  188. package/dist/schemas/item.d.ts +1 -1
  189. package/dist/schemas/workflow.d.ts +139 -0
  190. package/dist/schemas/workflow.js +232 -0
  191. package/dist/types/ask-user-types.d.ts +57 -0
  192. package/dist/types/ask-user-types.js +74 -0
  193. package/dist/types/checkpoint.d.ts +98 -0
  194. package/dist/types/checkpoint.js +81 -0
  195. package/dist/types/common.d.ts +1 -50
  196. package/dist/types/context-parts/item-log.d.ts +6 -0
  197. package/dist/types/context-parts/item-log.js +1 -0
  198. package/dist/types/context-parts/layer-usage.d.ts +18 -0
  199. package/dist/types/context-parts/layer-usage.js +1 -0
  200. package/dist/types/context.d.ts +140 -31
  201. package/dist/types/error.d.ts +15 -0
  202. package/dist/types/harness-result.d.ts +2 -2
  203. package/dist/types/items.d.ts +18 -2
  204. package/dist/types/memory-context.d.ts +39 -0
  205. package/dist/types/memory-context.js +1 -0
  206. package/dist/types/memory.d.ts +38 -68
  207. package/dist/types/mutable-context.d.ts +3 -3
  208. package/dist/types/runtime.d.ts +14 -13
  209. package/dist/types/steering.d.ts +2 -2
  210. package/dist/types/step-registrar.d.ts +46 -0
  211. package/dist/types/step-registrar.js +56 -0
  212. package/dist/types/step.d.ts +34 -9
  213. package/dist/types/subprocess-adapter.d.ts +119 -3
  214. package/dist/types/subprocess-adapter.js +2 -0
  215. package/dist/types/tool-context.d.ts +6 -6
  216. package/dist/types/tool.d.ts +64 -0
  217. package/dist/types/tool.js +1 -0
  218. package/dist/unstable.d.ts +9 -9
  219. package/dist/unstable.js +7 -7
  220. package/dist/until/combinators.d.ts +1 -1
  221. package/dist/until/never.d.ts +1 -1
  222. package/dist/until/predicates.d.ts +13 -4
  223. package/dist/until/predicates.js +16 -2
  224. package/dist/util/message-helpers.d.ts +17 -0
  225. package/dist/util/message-helpers.js +64 -0
  226. package/package.json +6 -4
  227. package/dist/adapters/local-fs-adapter.d.ts +0 -3
  228. package/dist/adapters/local-fs-adapter.js +0 -43
  229. package/dist/adapters/local-shell-adapter.d.ts +0 -51
  230. package/dist/adapters/local-shell-adapter.js +0 -167
  231. package/dist/adapters/local-subprocess-adapter.d.ts +0 -16
  232. package/dist/adapters/local-subprocess-adapter.js +0 -187
  233. package/dist/conditions/index.d.ts +0 -3
  234. package/dist/conditions/index.js +0 -2
  235. package/dist/interpreter/execute-branch.d.ts +0 -3
  236. package/dist/interpreter/execute-branch.js +0 -10
  237. package/dist/interpreter/execute-every.js +0 -143
  238. package/dist/interpreter/execute-fork.d.ts +0 -3
  239. package/dist/interpreter/execute-fork.js +0 -215
  240. package/dist/interpreter/execute-llm.d.ts +0 -4
  241. package/dist/interpreter/execute-llm.js +0 -217
  242. package/dist/interpreter/execute-loop.d.ts +0 -3
  243. package/dist/interpreter/execute-loop.js +0 -171
  244. package/dist/interpreter/execute-provide.d.ts +0 -10
  245. package/dist/interpreter/execute-provide.js +0 -64
  246. package/dist/interpreter/execute-run.d.ts +0 -3
  247. package/dist/interpreter/execute-run.js +0 -39
  248. package/dist/interpreter/execute-spawn.d.ts +0 -11
  249. package/dist/interpreter/execute-spawn.js +0 -113
  250. package/dist/interpreter/execute-tool.d.ts +0 -5
  251. package/dist/interpreter/execute-tool.js +0 -81
  252. package/dist/runtime/agent-harness.js +0 -1170
  253. /package/dist/{interpreter → util}/framework-cast.d.ts +0 -0
  254. /package/dist/{interpreter → util}/framework-cast.js +0 -0
@@ -1,4 +1,7 @@
1
- import { NoeticConfigError } from '../errors/noetic-config-error';
1
+ import { NoeticConfigError } from '../errors/noetic-config-error.js';
2
+ import { getDefaultRegistrar } from '../types/step-registrar.js';
3
+ //#endregion
4
+ //#region Builders
2
5
  export const step = {
3
6
  /**
4
7
  * Creates a pure async computation step.
@@ -7,9 +10,13 @@ export const step = {
7
10
  * @param opts.id - Unique step identifier used in traces and error messages.
8
11
  * @param opts.execute - Async function `(input, ctx) => output` that performs the work.
9
12
  * @param opts.retry - Optional retry policy controlling attempts, backoff, and delay.
10
- * @returns A `StepRun` that can be composed into larger pipelines.
13
+ * @param opts.subprocess - Optional per-step subprocess adapter override.
14
+ * @returns A `StepRun` that can be composed into larger pipelines. The step
15
+ * is auto-registered in the shared step registry so the subprocess
16
+ * adapter can dispatch it by id.
11
17
  * @throws `NoeticConfigError` with code `EMPTY_STEP_ID` if `id` is empty.
12
18
  * @throws `NoeticConfigError` with code `MISSING_EXECUTE_FUNCTION` if `execute` is not provided.
19
+ * @throws `NoeticConfigError` with code `DUPLICATE_STEP_ID` if another step with the same id is already registered with a different body.
13
20
  */
14
21
  run(opts) {
15
22
  if (!opts.id || opts.id.trim() === '') {
@@ -26,24 +33,27 @@ export const step = {
26
33
  hint: 'Provide an async execute function, e.g. execute: async (input, ctx) => result.',
27
34
  });
28
35
  }
29
- return {
36
+ const built = {
30
37
  kind: 'run',
31
38
  ...opts,
32
39
  };
40
+ getDefaultRegistrar().register(built);
41
+ return built;
33
42
  },
34
43
  /**
35
44
  * Creates an LLM model call step with optional tools and structured output.
36
45
  *
37
46
  * @public
38
47
  * @param opts.id - Unique step identifier used in traces and error messages.
39
- * @param opts.model - Model identifier string (e.g. `'anthropic/claude-sonnet-4-20250514'`).
40
- * @param opts.instructions - Optional system prompt / instructions for the model.
41
- * @param opts.tools - Optional tools available to the model during this call.
48
+ * @param opts.model - Model identifier, eager string or `(ctx) => string` getter (resolved at step execution time).
49
+ * @param opts.instructions - Optional system prompt; eager string or `(ctx) => string | undefined` getter.
50
+ * @param opts.tools - Optional tools; eager array or `(ctx) => Tool[] | undefined` getter.
42
51
  * @param opts.output - Optional Zod schema enabling structured output parsing.
43
52
  * @param opts.params - Optional model parameters (temperature, topP, maxTokens, stopSequences).
44
- * @returns A `StepLLM` that can be composed into larger pipelines.
53
+ * @returns A `StepLLM` that can be composed into larger pipelines. The step
54
+ * is auto-registered in the shared step registry.
45
55
  * @throws `NoeticConfigError` with code `EMPTY_STEP_ID` if `id` is empty.
46
- * @throws `NoeticConfigError` with code `MISSING_MODEL` if `model` is empty.
56
+ * @throws `NoeticConfigError` with code `MISSING_MODEL` if an eager `model` string is empty. Function-form models are validated at step execution.
47
57
  */
48
58
  llm(opts) {
49
59
  if (!opts.id || opts.id.trim() === '') {
@@ -53,17 +63,22 @@ export const step = {
53
63
  hint: 'Pass a unique string as the id field, e.g. step.llm({ id: "my-llm", ... }).',
54
64
  });
55
65
  }
56
- if (!opts.model || opts.model.trim() === '') {
66
+ // Only validate eager models here. Function-form models are validated
67
+ // post-resolution in executeLLM so the same MISSING_MODEL error surfaces
68
+ // whether the caller passes a string or a getter.
69
+ if (typeof opts.model === 'string' && opts.model.trim() === '') {
57
70
  throw new NoeticConfigError({
58
71
  code: 'MISSING_MODEL',
59
72
  message: 'step.llm() requires a non-empty model.',
60
73
  hint: "Pass a model identifier, e.g. model: 'anthropic/claude-sonnet-4-20250514'.",
61
74
  });
62
75
  }
63
- return {
76
+ const built = {
64
77
  kind: 'llm',
65
78
  ...opts,
66
79
  };
80
+ getDefaultRegistrar().register(built);
81
+ return built;
67
82
  },
68
83
  /**
69
84
  * Creates a tool execution step that invokes a typed tool definition.
@@ -72,7 +87,8 @@ export const step = {
72
87
  * @param opts.id - Unique step identifier used in traces and error messages.
73
88
  * @param opts.tool - The tool definition with typed input/output schemas.
74
89
  * @param opts.args - Optional partial args that override or supplement LLM-provided arguments.
75
- * @returns A `StepTool` that can be composed into larger pipelines.
90
+ * @returns A `StepTool` that can be composed into larger pipelines. The step
91
+ * is auto-registered in the shared step registry.
76
92
  * @throws `NoeticConfigError` with code `EMPTY_STEP_ID` if `id` is empty.
77
93
  * @throws `NoeticConfigError` with code `MISSING_TOOL` if `tool` is not provided.
78
94
  */
@@ -91,9 +107,12 @@ export const step = {
91
107
  hint: 'Provide a tool definition created with the tool() builder.',
92
108
  });
93
109
  }
94
- return {
110
+ const built = {
95
111
  kind: 'tool',
96
112
  ...opts,
97
113
  };
114
+ getDefaultRegistrar().register(built);
115
+ return built;
98
116
  },
99
117
  };
118
+ //#endregion
@@ -1,7 +1,7 @@
1
1
  import type { ZodTypeAny, z } from 'zod';
2
- import type { Tool } from '../types/common';
3
- import type { FunctionCallItem, FunctionCallOutputItem, Item, ItemSchemaExtensions, ToolResultExtensionItem } from '../types/items';
4
- import type { ToolExecutionContext } from '../types/tool-context';
2
+ import type { FunctionCallItem, FunctionCallOutputItem, Item, ItemSchemaExtensions, ToolResultExtensionItem } from '../types/items.js';
3
+ import type { Tool, ToolMemoryDeclaration } from '../types/tool.js';
4
+ import type { ToolExecutionContext } from '../types/tool-context.js';
5
5
  interface ToolConfig<I extends ZodTypeAny, O extends ZodTypeAny> {
6
6
  name: string;
7
7
  description: string;
@@ -18,6 +18,8 @@ interface ToolConfig<I extends ZodTypeAny, O extends ZodTypeAny> {
18
18
  }) => Item | ToolResultExtensionItem;
19
19
  execute: (args: z.infer<I>, toolCtx: ToolExecutionContext) => Promise<z.infer<O>>;
20
20
  needsApproval?: boolean;
21
+ /** Optional memory declaration — the runtime generates a MemoryLayer from this via toolMemoryLayer(). */
22
+ memory?: ToolMemoryDeclaration;
21
23
  }
22
24
  interface GeneratorToolConfig<I extends ZodTypeAny, E extends ZodTypeAny, O extends ZodTypeAny> {
23
25
  name: string;
@@ -36,6 +38,8 @@ interface GeneratorToolConfig<I extends ZodTypeAny, E extends ZodTypeAny, O exte
36
38
  }) => Item | ToolResultExtensionItem;
37
39
  execute: (args: z.infer<I>, toolCtx: ToolExecutionContext) => AsyncGenerator<z.infer<E>, z.infer<O>>;
38
40
  needsApproval?: boolean;
41
+ /** Optional memory declaration — the runtime generates a MemoryLayer from this via toolMemoryLayer(). */
42
+ memory?: ToolMemoryDeclaration;
39
43
  }
40
44
  /**
41
45
  * Creates a typed Tool with Zod schema inference for input and output.
@@ -1,4 +1,4 @@
1
- import { NoeticConfigError } from '../errors/noetic-config-error';
1
+ import { NoeticConfigError } from '../errors/noetic-config-error.js';
2
2
  //#endregion
3
3
  //#region Helpers
4
4
  function validateToolConfig(name, execute) {
@@ -35,6 +35,7 @@ export function tool(config) {
35
35
  decorateResultItem: config.decorateResultItem,
36
36
  execute: config.execute,
37
37
  needsApproval: config.needsApproval,
38
+ memory: config.memory,
38
39
  };
39
40
  }
40
41
  /**
@@ -54,6 +55,7 @@ export function toolWithGenerator(config) {
54
55
  decorateResultItem: config.decorateResultItem,
55
56
  execute: config.execute,
56
57
  needsApproval: config.needsApproval,
58
+ memory: config.memory,
57
59
  };
58
60
  }
59
61
  //#endregion
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Converts a validated `WorkflowDocument` into a live `Step` tree.
3
+ *
4
+ * Each node kind maps to the corresponding builder (`step.llm`, `fork`,
5
+ * `loop`, etc.) so hydrated steps are indistinguishable from programmatic
6
+ * ones — they register in the step registry, carry retry policies, etc.
7
+ */
8
+ import type { WorkflowDocument, WorkflowNode } from '../schemas/workflow.js';
9
+ import type { ContextMemory, MemoryLayer } from '../types/memory.js';
10
+ import type { ExecuteStepFn, Step } from '../types/step.js';
11
+ import type { Tool } from '../types/tool.js';
12
+ /** @public Context required to hydrate a JSON workflow into live steps. */
13
+ export interface HydrationContext {
14
+ tools: ReadonlyMap<string, Tool>;
15
+ executeStep: ExecuteStepFn;
16
+ layers?: ReadonlyMap<string, MemoryLayer>;
17
+ }
18
+ /**
19
+ * Hydrates a single `WorkflowNode` into a live `Step`.
20
+ *
21
+ * @public
22
+ * @param node - Validated workflow node.
23
+ * @param ctx - Hydration context with tool registry and step executor.
24
+ * @returns A live `Step` ready for execution.
25
+ * @throws `NoeticConfigError` with code `UNKNOWN_NODE_KIND` if the node kind is unrecognised.
26
+ * @throws `NoeticConfigError` with code `UNKNOWN_TOOL_REFERENCE` if a tool name cannot be resolved.
27
+ * @throws `NoeticConfigError` with code `UNKNOWN_UNTIL_PREDICATE` if an until predicate kind is unrecognised.
28
+ */
29
+ export declare function hydrateNode(node: WorkflowNode, ctx: HydrationContext): Step<ContextMemory, string, string>;
30
+ /**
31
+ * Hydrates a complete `WorkflowDocument` into a live `Step` tree.
32
+ *
33
+ * @public
34
+ * @param doc - Validated workflow document (version 1).
35
+ * @param ctx - Hydration context with tool registry and step executor.
36
+ * @returns The root `Step` of the hydrated workflow.
37
+ */
38
+ export declare function hydrateWorkflow(doc: WorkflowDocument, ctx: HydrationContext): Step<ContextMemory, string, string>;
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Converts a validated `WorkflowDocument` into a live `Step` tree.
3
+ *
4
+ * Each node kind maps to the corresponding builder (`step.llm`, `fork`,
5
+ * `loop`, etc.) so hydrated steps are indistinguishable from programmatic
6
+ * ones — they register in the step registry, carry retry policies, etc.
7
+ */
8
+ import { NoeticConfigError } from '../errors/noetic-config-error.js';
9
+ import { all, any } from '../until/combinators.js';
10
+ import { until } from '../until/predicates.js';
11
+ import { frameworkCast } from '../util/framework-cast.js';
12
+ import { branch, fork } from './control-flow-builders.js';
13
+ import { every } from './every.js';
14
+ import { loop } from './loop-builder.js';
15
+ import { provide } from './provide-builder.js';
16
+ import { spawn } from './spawn-builder.js';
17
+ import { step } from './step-builders.js';
18
+ //#endregion
19
+ //#region Until Predicate Hydration
20
+ function hydrateUntilPredicate(pred) {
21
+ switch (pred.kind) {
22
+ case 'maxSteps':
23
+ return until.maxSteps(pred.n);
24
+ case 'maxCost':
25
+ return until.maxCost(pred.usd);
26
+ case 'maxDuration':
27
+ return until.maxDuration(pred.ms);
28
+ case 'noToolCalls':
29
+ return until.noToolCalls();
30
+ case 'outputContains':
31
+ return until.outputContains(pred.marker);
32
+ case 'outputEquals':
33
+ return until.outputEquals(pred.sentinel);
34
+ case 'converged':
35
+ return until.converged({
36
+ threshold: pred.threshold,
37
+ });
38
+ case 'any':
39
+ return any(...pred.predicates.map(hydrateUntilPredicate));
40
+ case 'all':
41
+ return all(...pred.predicates.map(hydrateUntilPredicate));
42
+ default:
43
+ throw new NoeticConfigError({
44
+ code: 'UNKNOWN_UNTIL_PREDICATE',
45
+ message: `Unknown until predicate kind: '${frameworkCast(pred).kind}'.`,
46
+ hint: 'Supported kinds: maxSteps, maxCost, maxDuration, noToolCalls, outputContains, outputEquals, converged, any, all.',
47
+ });
48
+ }
49
+ }
50
+ //#endregion
51
+ //#region Node Hydrators
52
+ function hydrateLlmNode(node, ctx) {
53
+ if (node.kind !== 'llm') {
54
+ return frameworkCast(undefined);
55
+ }
56
+ const resolvedTools = resolveTools(node.tools, ctx.tools);
57
+ return step.llm({
58
+ id: node.id,
59
+ model: node.model ?? 'openai/gpt-4o',
60
+ instructions: node.instructions,
61
+ tools: resolvedTools.length > 0 ? resolvedTools : undefined,
62
+ params: node.params,
63
+ });
64
+ }
65
+ function hydrateToolNode(node, ctx) {
66
+ if (node.kind !== 'tool') {
67
+ return frameworkCast(undefined);
68
+ }
69
+ const resolved = ctx.tools.get(node.toolName);
70
+ if (!resolved) {
71
+ throw new NoeticConfigError({
72
+ code: 'UNKNOWN_TOOL_REFERENCE',
73
+ message: `Tool '${node.toolName}' referenced in workflow node '${node.id}' is not registered.`,
74
+ hint: `Available tools: ${[
75
+ ...ctx.tools.keys(),
76
+ ].join(', ') || '(none)'}`,
77
+ });
78
+ }
79
+ return frameworkCast(step.run({
80
+ id: node.id,
81
+ execute: async (_input, execCtx) => {
82
+ const args = node.args ?? {};
83
+ const callId = `call-${node.id}-${Date.now()}`;
84
+ const callItem = {
85
+ id: callId,
86
+ type: 'function_call',
87
+ status: 'completed',
88
+ name: node.toolName,
89
+ callId,
90
+ arguments: JSON.stringify(args),
91
+ };
92
+ execCtx.itemLog.append(callItem);
93
+ const toolCtx = {
94
+ ctx: execCtx,
95
+ harness: execCtx.harness,
96
+ fs: execCtx.fs,
97
+ shell: execCtx.shell,
98
+ memory: {
99
+ get: (_layerId) => undefined,
100
+ set: (_layerId, _state) => { },
101
+ },
102
+ assembledView: execCtx.itemLog.items,
103
+ lastStepMeta: execCtx.lastStepMeta,
104
+ };
105
+ const result = await resolved.execute(args, toolCtx);
106
+ return stringifyResult(result);
107
+ },
108
+ }));
109
+ }
110
+ function hydrateBranchNode(node, ctx) {
111
+ if (node.kind !== 'branch') {
112
+ return frameworkCast(undefined);
113
+ }
114
+ const hydratedRoutes = node.routes.map((r) => ({
115
+ match: r.match,
116
+ target: hydrateNode(r.target, ctx),
117
+ }));
118
+ const defaultTarget = node.default ? hydrateNode(node.default, ctx) : null;
119
+ const allTargets = hydratedRoutes.map((r) => r.target);
120
+ if (defaultTarget) {
121
+ allTargets.push(defaultTarget);
122
+ }
123
+ return branch({
124
+ id: node.id,
125
+ route: (input) => {
126
+ const trimmed = input.trim().toLowerCase();
127
+ for (const r of hydratedRoutes) {
128
+ if (trimmed.includes(r.match.toLowerCase())) {
129
+ return r.target;
130
+ }
131
+ }
132
+ return defaultTarget;
133
+ },
134
+ _optimizable: frameworkCast(allTargets),
135
+ });
136
+ }
137
+ function hydrateForkNode(node, ctx) {
138
+ if (node.kind !== 'fork') {
139
+ return frameworkCast(undefined);
140
+ }
141
+ const hydratedPaths = node.paths.map((p) => hydrateNode(p, ctx));
142
+ if (node.mode === 'race') {
143
+ return fork({
144
+ id: node.id,
145
+ mode: 'race',
146
+ paths: () => hydratedPaths,
147
+ concurrency: node.concurrency,
148
+ _optimizable: frameworkCast(hydratedPaths),
149
+ });
150
+ }
151
+ const mergeFn = buildMerge(node.merge ?? 'last');
152
+ if (node.mode === 'settle') {
153
+ return fork({
154
+ id: node.id,
155
+ mode: 'settle',
156
+ paths: () => hydratedPaths,
157
+ merge: (results) => {
158
+ const values = results.filter((r) => r.status === 'fulfilled').map((r) => r.value ?? '');
159
+ return mergeFn(values);
160
+ },
161
+ concurrency: node.concurrency,
162
+ _optimizable: frameworkCast(hydratedPaths),
163
+ });
164
+ }
165
+ return fork({
166
+ id: node.id,
167
+ mode: 'all',
168
+ paths: () => hydratedPaths,
169
+ merge: mergeFn,
170
+ concurrency: node.concurrency,
171
+ _optimizable: frameworkCast(hydratedPaths),
172
+ });
173
+ }
174
+ function hydrateSpawnNode(node, ctx) {
175
+ if (node.kind !== 'spawn') {
176
+ return frameworkCast(undefined);
177
+ }
178
+ return spawn({
179
+ id: node.id,
180
+ child: hydrateNode(node.child, ctx),
181
+ timeout: node.timeout,
182
+ });
183
+ }
184
+ function hydrateProvideNode(node, ctx) {
185
+ if (node.kind !== 'provide') {
186
+ return frameworkCast(undefined);
187
+ }
188
+ const resolvedLayers = ctx.layers
189
+ ? node.layers.map((name) => {
190
+ const layer = ctx.layers?.get(name);
191
+ if (!layer) {
192
+ throw new NoeticConfigError({
193
+ code: 'UNKNOWN_LAYER_REFERENCE',
194
+ message: `Memory layer '${name}' referenced in provide node '${node.id}' is not registered.`,
195
+ hint: `Available layers: ${[
196
+ ...(ctx.layers?.keys() ?? []),
197
+ ].join(', ') || '(none)'}. Pass named layers via HydrationContext.layers.`,
198
+ });
199
+ }
200
+ return layer;
201
+ })
202
+ : [];
203
+ return provide({
204
+ id: node.id,
205
+ child: hydrateNode(node.child, ctx),
206
+ memory: resolvedLayers,
207
+ });
208
+ }
209
+ function hydrateLoopNode(node, ctx) {
210
+ if (node.kind !== 'loop') {
211
+ return frameworkCast(undefined);
212
+ }
213
+ return loop({
214
+ id: node.id,
215
+ steps: [
216
+ hydrateNode(node.body, ctx),
217
+ ],
218
+ until: hydrateUntilPredicate(node.until),
219
+ maxIterations: node.maxIterations,
220
+ });
221
+ }
222
+ function hydrateSequenceNode(node, ctx) {
223
+ if (node.kind !== 'sequence') {
224
+ return frameworkCast(undefined);
225
+ }
226
+ const children = node.steps.map((s) => hydrateNode(s, ctx));
227
+ return frameworkCast(step.run({
228
+ id: node.id,
229
+ execute: async (input, execCtx) => {
230
+ let current = input;
231
+ for (const child of children) {
232
+ const childInput = stringifyResult(current);
233
+ current = await ctx.executeStep(child, childInput, execCtx);
234
+ }
235
+ return stringifyResult(current);
236
+ },
237
+ }));
238
+ }
239
+ function hydrateEveryNode(node, ctx) {
240
+ if (node.kind !== 'every') {
241
+ return frameworkCast(undefined);
242
+ }
243
+ return every({
244
+ id: node.id,
245
+ step: hydrateNode(node.step, ctx),
246
+ ms: node.ms,
247
+ onError: node.onError,
248
+ });
249
+ }
250
+ //#endregion
251
+ //#region Handler Registry
252
+ const NODE_HYDRATORS = {
253
+ llm: hydrateLlmNode,
254
+ tool: hydrateToolNode,
255
+ branch: hydrateBranchNode,
256
+ fork: hydrateForkNode,
257
+ spawn: hydrateSpawnNode,
258
+ provide: hydrateProvideNode,
259
+ loop: hydrateLoopNode,
260
+ sequence: hydrateSequenceNode,
261
+ every: hydrateEveryNode,
262
+ };
263
+ //#endregion
264
+ //#region Helpers
265
+ function resolveTools(toolNames, registry) {
266
+ if (!toolNames || toolNames.length === 0) {
267
+ return [];
268
+ }
269
+ return toolNames.map((name) => {
270
+ const resolved = registry.get(name);
271
+ if (!resolved) {
272
+ throw new NoeticConfigError({
273
+ code: 'UNKNOWN_TOOL_REFERENCE',
274
+ message: `Tool '${name}' referenced in workflow is not registered.`,
275
+ hint: `Available tools: ${[
276
+ ...registry.keys(),
277
+ ].join(', ') || '(none)'}`,
278
+ });
279
+ }
280
+ return resolved;
281
+ });
282
+ }
283
+ function buildMerge(strategy) {
284
+ if (strategy === 'first') {
285
+ return (r) => r[0] ?? '';
286
+ }
287
+ if (strategy === 'concat') {
288
+ return (r) => r.join('\n');
289
+ }
290
+ return (r) => r[r.length - 1] ?? '';
291
+ }
292
+ function stringifyResult(value) {
293
+ if (typeof value === 'string') {
294
+ return value;
295
+ }
296
+ if (value === null || value === undefined) {
297
+ return '';
298
+ }
299
+ return JSON.stringify(value);
300
+ }
301
+ //#endregion
302
+ //#region Public API
303
+ /**
304
+ * Hydrates a single `WorkflowNode` into a live `Step`.
305
+ *
306
+ * @public
307
+ * @param node - Validated workflow node.
308
+ * @param ctx - Hydration context with tool registry and step executor.
309
+ * @returns A live `Step` ready for execution.
310
+ * @throws `NoeticConfigError` with code `UNKNOWN_NODE_KIND` if the node kind is unrecognised.
311
+ * @throws `NoeticConfigError` with code `UNKNOWN_TOOL_REFERENCE` if a tool name cannot be resolved.
312
+ * @throws `NoeticConfigError` with code `UNKNOWN_UNTIL_PREDICATE` if an until predicate kind is unrecognised.
313
+ */
314
+ export function hydrateNode(node, ctx) {
315
+ const hydrator = NODE_HYDRATORS[node.kind];
316
+ if (!hydrator) {
317
+ throw new NoeticConfigError({
318
+ code: 'UNKNOWN_NODE_KIND',
319
+ message: `Unknown workflow node kind: '${node.kind}'.`,
320
+ hint: `Supported kinds: ${Object.keys(NODE_HYDRATORS).join(', ')}.`,
321
+ });
322
+ }
323
+ return hydrator(node, ctx);
324
+ }
325
+ /**
326
+ * Hydrates a complete `WorkflowDocument` into a live `Step` tree.
327
+ *
328
+ * @public
329
+ * @param doc - Validated workflow document (version 1).
330
+ * @param ctx - Hydration context with tool registry and step executor.
331
+ * @returns The root `Step` of the hydrated workflow.
332
+ */
333
+ export function hydrateWorkflow(doc, ctx) {
334
+ return hydrateNode(doc.root, ctx);
335
+ }
336
+ //#endregion
@@ -1,7 +1,7 @@
1
- import type { Context } from '../types/context';
2
- import type { EmbedFn } from '../types/embed';
3
- import type { ContextMemory, StorageAdapter } from '../types/memory';
4
- import type { Step } from '../types/step';
1
+ import type { Context } from '../types/context.js';
2
+ import type { EmbedFn } from '../types/embed.js';
3
+ import type { ContextMemory, StorageAdapter } from '../types/memory.js';
4
+ import type { Step } from '../types/step.js';
5
5
  export type Condition<I> = (input: I, ctx: Context) => Promise<boolean>;
6
6
  export interface WhenClause<TMemory = ContextMemory, I = unknown, O = unknown> {
7
7
  readonly kind: 'when';
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { createMessage, extractAssistantText, trackUsage } from '../interpreter/message-helpers';
3
- import { cosineSimilarity } from './cosine-similarity';
2
+ import { createMessage, extractAssistantText, trackUsage } from '../util/message-helpers.js';
3
+ import { cosineSimilarity } from './cosine-similarity.js';
4
4
  //#endregion
5
5
  //#region Helpers
6
6
  function serializeInput(input) {
@@ -1,4 +1,4 @@
1
- import type { NoeticError } from '../types/error';
1
+ import type { NoeticError } from '../types/error.js';
2
2
  /**
3
3
  * Runtime error thrown during step execution (LLM failures, fork failures, budget exceeded, etc.).
4
4
  *
@@ -43,6 +43,8 @@ function formatMessage(error) {
43
43
  return `Budget exceeded: ${error.field} limit ${error.limit}, actual ${error.actual}`;
44
44
  case 'steering_denied':
45
45
  return `Steering denied${error.guidance ? `: ${error.guidance}` : ''}`;
46
+ case 'handle_evicted':
47
+ return `Subprocess handle '${error.handleId}' evicted (step '${error.stepId}'): adapter returned null for ${error.gracePeriodMs}ms`;
46
48
  default: {
47
49
  const _exhaustive = error;
48
50
  void _exhaustive;