@noetic-tools/core 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.
Files changed (207) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +38 -0
  3. package/dist/adapters/in-memory-fs-adapter.d.ts +3 -0
  4. package/dist/adapters/in-memory-fs-adapter.js +245 -0
  5. package/dist/adapters/in-memory-shell-adapter.d.ts +9 -0
  6. package/dist/adapters/in-memory-shell-adapter.js +20 -0
  7. package/dist/adapters/in-memory-subprocess-adapter.d.ts +5 -0
  8. package/dist/adapters/in-memory-subprocess-adapter.js +126 -0
  9. package/dist/adapters/local-fs-adapter.d.ts +3 -0
  10. package/dist/adapters/local-fs-adapter.js +43 -0
  11. package/dist/adapters/local-shell-adapter.d.ts +51 -0
  12. package/dist/adapters/local-shell-adapter.js +167 -0
  13. package/dist/adapters/local-subprocess-adapter.d.ts +16 -0
  14. package/dist/adapters/local-subprocess-adapter.js +187 -0
  15. package/dist/adapters/openrouter.d.ts +57 -0
  16. package/dist/adapters/openrouter.js +248 -0
  17. package/dist/builders/channel-builder.d.ts +36 -0
  18. package/dist/builders/channel-builder.js +15 -0
  19. package/dist/builders/control-flow-builders.d.ts +79 -0
  20. package/dist/builders/control-flow-builders.js +53 -0
  21. package/dist/builders/every.d.ts +45 -0
  22. package/dist/builders/every.js +60 -0
  23. package/dist/builders/layer-provides-builders.d.ts +32 -0
  24. package/dist/builders/layer-provides-builders.js +33 -0
  25. package/dist/builders/loop-builder.d.ts +23 -0
  26. package/dist/builders/loop-builder.js +46 -0
  27. package/dist/builders/memory-builder.d.ts +11 -0
  28. package/dist/builders/memory-builder.js +15 -0
  29. package/dist/builders/provide-builder.d.ts +20 -0
  30. package/dist/builders/provide-builder.js +34 -0
  31. package/dist/builders/spawn-builder.d.ts +20 -0
  32. package/dist/builders/spawn-builder.js +33 -0
  33. package/dist/builders/step-builders.d.ts +62 -0
  34. package/dist/builders/step-builders.js +99 -0
  35. package/dist/builders/tool-builder.d.ts +52 -0
  36. package/dist/builders/tool-builder.js +59 -0
  37. package/dist/conditions/conditions.d.ts +114 -0
  38. package/dist/conditions/conditions.js +271 -0
  39. package/dist/conditions/cosine-similarity.d.ts +11 -0
  40. package/dist/conditions/cosine-similarity.js +28 -0
  41. package/dist/conditions/index.d.ts +3 -0
  42. package/dist/conditions/index.js +2 -0
  43. package/dist/errors/noetic-config-error.d.ts +41 -0
  44. package/dist/errors/noetic-config-error.js +44 -0
  45. package/dist/errors/noetic-error.d.ts +18 -0
  46. package/dist/errors/noetic-error.js +52 -0
  47. package/dist/index.d.ts +152 -0
  48. package/dist/index.js +112 -0
  49. package/dist/internal-test.d.ts +11 -0
  50. package/dist/internal-test.js +11 -0
  51. package/dist/interpreter/clone-guard.d.ts +4 -0
  52. package/dist/interpreter/clone-guard.js +18 -0
  53. package/dist/interpreter/collect-tools.d.ts +12 -0
  54. package/dist/interpreter/collect-tools.js +70 -0
  55. package/dist/interpreter/execute-branch.d.ts +3 -0
  56. package/dist/interpreter/execute-branch.js +10 -0
  57. package/dist/interpreter/execute-every.d.ts +15 -0
  58. package/dist/interpreter/execute-every.js +143 -0
  59. package/dist/interpreter/execute-fork.d.ts +3 -0
  60. package/dist/interpreter/execute-fork.js +215 -0
  61. package/dist/interpreter/execute-llm.d.ts +4 -0
  62. package/dist/interpreter/execute-llm.js +217 -0
  63. package/dist/interpreter/execute-loop.d.ts +3 -0
  64. package/dist/interpreter/execute-loop.js +171 -0
  65. package/dist/interpreter/execute-provide.d.ts +10 -0
  66. package/dist/interpreter/execute-provide.js +64 -0
  67. package/dist/interpreter/execute-run.d.ts +3 -0
  68. package/dist/interpreter/execute-run.js +39 -0
  69. package/dist/interpreter/execute-spawn.d.ts +11 -0
  70. package/dist/interpreter/execute-spawn.js +113 -0
  71. package/dist/interpreter/execute-tool.d.ts +5 -0
  72. package/dist/interpreter/execute-tool.js +81 -0
  73. package/dist/interpreter/execute.d.ts +14 -0
  74. package/dist/interpreter/execute.js +119 -0
  75. package/dist/interpreter/framework-cast.d.ts +10 -0
  76. package/dist/interpreter/framework-cast.js +16 -0
  77. package/dist/interpreter/message-helpers.d.ts +9 -0
  78. package/dist/interpreter/message-helpers.js +42 -0
  79. package/dist/interpreter/typeguards.d.ts +25 -0
  80. package/dist/interpreter/typeguards.js +50 -0
  81. package/dist/memory/budget.d.ts +23 -0
  82. package/dist/memory/budget.js +117 -0
  83. package/dist/memory/function-call-utils.d.ts +6 -0
  84. package/dist/memory/function-call-utils.js +25 -0
  85. package/dist/memory/layer-api.d.ts +14 -0
  86. package/dist/memory/layer-api.js +91 -0
  87. package/dist/memory/layer-lifecycle.d.ts +160 -0
  88. package/dist/memory/layer-lifecycle.js +552 -0
  89. package/dist/memory/layer-usage.d.ts +15 -0
  90. package/dist/memory/layer-usage.js +48 -0
  91. package/dist/memory/layers/durable-task-state.d.ts +79 -0
  92. package/dist/memory/layers/durable-task-state.js +118 -0
  93. package/dist/memory/layers/file-reference.d.ts +55 -0
  94. package/dist/memory/layers/file-reference.js +538 -0
  95. package/dist/memory/layers/history-window.d.ts +29 -0
  96. package/dist/memory/layers/history-window.js +114 -0
  97. package/dist/memory/layers/observational-memory.d.ts +53 -0
  98. package/dist/memory/layers/observational-memory.js +110 -0
  99. package/dist/memory/layers/plan.d.ts +55 -0
  100. package/dist/memory/layers/plan.js +369 -0
  101. package/dist/memory/layers/static-content.d.ts +27 -0
  102. package/dist/memory/layers/static-content.js +35 -0
  103. package/dist/memory/layers/steering.d.ts +51 -0
  104. package/dist/memory/layers/steering.js +258 -0
  105. package/dist/memory/layers/tool-memory-layer.d.ts +16 -0
  106. package/dist/memory/layers/tool-memory-layer.js +43 -0
  107. package/dist/memory/layers/working-memory.d.ts +44 -0
  108. package/dist/memory/layers/working-memory.js +92 -0
  109. package/dist/memory/projector.d.ts +10 -0
  110. package/dist/memory/projector.js +19 -0
  111. package/dist/memory/scope.d.ts +3 -0
  112. package/dist/memory/scope.js +31 -0
  113. package/dist/memory/strip-unresolved.d.ts +13 -0
  114. package/dist/memory/strip-unresolved.js +35 -0
  115. package/dist/observability/genai-attributes.d.ts +11 -0
  116. package/dist/observability/genai-attributes.js +12 -0
  117. package/dist/observability/span-impl.d.ts +20 -0
  118. package/dist/observability/span-impl.js +33 -0
  119. package/dist/observability/trace-exporter.d.ts +15 -0
  120. package/dist/observability/trace-exporter.js +26 -0
  121. package/dist/patterns/flow.d.ts +52 -0
  122. package/dist/patterns/flow.js +103 -0
  123. package/dist/patterns/interview.d.ts +53 -0
  124. package/dist/patterns/interview.js +113 -0
  125. package/dist/patterns/plans.d.ts +45 -0
  126. package/dist/patterns/plans.js +125 -0
  127. package/dist/patterns/ralph-wiggum.d.ts +20 -0
  128. package/dist/patterns/ralph-wiggum.js +37 -0
  129. package/dist/patterns/react.d.ts +18 -0
  130. package/dist/patterns/react.js +39 -0
  131. package/dist/portable.d.ts +26 -0
  132. package/dist/portable.js +34 -0
  133. package/dist/runtime/agent-harness.d.ts +206 -0
  134. package/dist/runtime/agent-harness.js +1170 -0
  135. package/dist/runtime/broadcaster-utils.d.ts +30 -0
  136. package/dist/runtime/broadcaster-utils.js +53 -0
  137. package/dist/runtime/channel-store.d.ts +18 -0
  138. package/dist/runtime/channel-store.js +187 -0
  139. package/dist/runtime/context-impl.d.ts +80 -0
  140. package/dist/runtime/context-impl.js +140 -0
  141. package/dist/runtime/cwd-helpers.d.ts +42 -0
  142. package/dist/runtime/cwd-helpers.js +68 -0
  143. package/dist/runtime/detached-handle.d.ts +14 -0
  144. package/dist/runtime/detached-handle.js +56 -0
  145. package/dist/runtime/event-broadcaster.d.ts +61 -0
  146. package/dist/runtime/event-broadcaster.js +187 -0
  147. package/dist/runtime/exec-context-factory.d.ts +25 -0
  148. package/dist/runtime/exec-context-factory.js +30 -0
  149. package/dist/runtime/in-memory-storage.d.ts +9 -0
  150. package/dist/runtime/in-memory-storage.js +33 -0
  151. package/dist/runtime/item-log-impl.d.ts +11 -0
  152. package/dist/runtime/item-log-impl.js +25 -0
  153. package/dist/runtime/message-queue.d.ts +30 -0
  154. package/dist/runtime/message-queue.js +46 -0
  155. package/dist/runtime/session-runner.d.ts +77 -0
  156. package/dist/runtime/session-runner.js +253 -0
  157. package/dist/runtime/session-streams.d.ts +11 -0
  158. package/dist/runtime/session-streams.js +204 -0
  159. package/dist/runtime/tool-memory.d.ts +6 -0
  160. package/dist/runtime/tool-memory.js +22 -0
  161. package/dist/schemas/item.d.ts +35 -0
  162. package/dist/schemas/item.js +155 -0
  163. package/dist/types/channel.d.ts +29 -0
  164. package/dist/types/channel.js +1 -0
  165. package/dist/types/common.d.ts +111 -0
  166. package/dist/types/common.js +1 -0
  167. package/dist/types/context.d.ts +93 -0
  168. package/dist/types/context.js +1 -0
  169. package/dist/types/detached.d.ts +17 -0
  170. package/dist/types/detached.js +7 -0
  171. package/dist/types/embed.d.ts +5 -0
  172. package/dist/types/embed.js +1 -0
  173. package/dist/types/error.d.ts +51 -0
  174. package/dist/types/error.js +1 -0
  175. package/dist/types/fs-adapter.d.ts +51 -0
  176. package/dist/types/fs-adapter.js +3 -0
  177. package/dist/types/harness-result.d.ts +43 -0
  178. package/dist/types/harness-result.js +2 -0
  179. package/dist/types/items.d.ts +115 -0
  180. package/dist/types/items.js +2 -0
  181. package/dist/types/memory.d.ts +336 -0
  182. package/dist/types/memory.js +13 -0
  183. package/dist/types/mutable-context.d.ts +13 -0
  184. package/dist/types/mutable-context.js +1 -0
  185. package/dist/types/observability.d.ts +30 -0
  186. package/dist/types/observability.js +1 -0
  187. package/dist/types/runtime.d.ts +255 -0
  188. package/dist/types/runtime.js +1 -0
  189. package/dist/types/shell-adapter.d.ts +34 -0
  190. package/dist/types/shell-adapter.js +10 -0
  191. package/dist/types/steering.d.ts +89 -0
  192. package/dist/types/steering.js +14 -0
  193. package/dist/types/step.d.ts +206 -0
  194. package/dist/types/step.js +1 -0
  195. package/dist/types/subprocess-adapter.d.ts +41 -0
  196. package/dist/types/subprocess-adapter.js +1 -0
  197. package/dist/types/tool-context.d.ts +34 -0
  198. package/dist/types/tool-context.js +1 -0
  199. package/dist/unstable.d.ts +28 -0
  200. package/dist/unstable.js +24 -0
  201. package/dist/until/combinators.d.ts +17 -0
  202. package/dist/until/combinators.js +49 -0
  203. package/dist/until/never.d.ts +9 -0
  204. package/dist/until/never.js +12 -0
  205. package/dist/until/predicates.d.ts +43 -0
  206. package/dist/until/predicates.js +126 -0
  207. package/package.json +81 -0
package/dist/index.js ADDED
@@ -0,0 +1,112 @@
1
+ //#region Adapters
2
+ /** @public */
3
+ export { createInMemoryFsAdapter } from './adapters/in-memory-fs-adapter';
4
+ /** @public */
5
+ export { createInMemoryShellAdapter } from './adapters/in-memory-shell-adapter';
6
+ /** @public */
7
+ export { createInMemorySubprocessAdapter } from './adapters/in-memory-subprocess-adapter';
8
+ /** @public */
9
+ export { createLocalFsAdapter } from './adapters/local-fs-adapter';
10
+ /** @public */
11
+ export { createLocalShellAdapter, } from './adapters/local-shell-adapter';
12
+ /** @public */
13
+ export { createLocalSubprocessAdapter, defaultProcessSignaller, } from './adapters/local-subprocess-adapter';
14
+ /** @public */
15
+ export { createOpenRouterEmbed } from './adapters/openrouter';
16
+ //#endregion
17
+ //#region Builders
18
+ /** @public */
19
+ export { channel } from './builders/channel-builder';
20
+ /** @public */
21
+ export { branch, fork } from './builders/control-flow-builders';
22
+ /** @public */
23
+ export { every } from './builders/every';
24
+ /** @public */
25
+ export { layerData, layerFn } from './builders/layer-provides-builders';
26
+ /** @public */
27
+ export { loop } from './builders/loop-builder';
28
+ /** @public */
29
+ export { memory } from './builders/memory-builder';
30
+ /** @public */
31
+ export { provide } from './builders/provide-builder';
32
+ /** @public */
33
+ export { spawn } from './builders/spawn-builder';
34
+ /** @public */
35
+ export { step } from './builders/step-builders';
36
+ /** @public */
37
+ export { tool, toolWithGenerator } from './builders/tool-builder';
38
+ /** @public */
39
+ export { aiCondition, allCondition, anyCondition, cosineSimilarity, embeddingMatch, otherwise, semanticRoute, semanticSwitch, when, } from './conditions';
40
+ //#endregion
41
+ //#region Errors
42
+ /** @public */
43
+ export { isNoeticConfigError, NoeticConfigError } from './errors/noetic-config-error';
44
+ /** @public */
45
+ export { isNoeticError, NoeticErrorImpl } from './errors/noetic-error';
46
+ //#endregion
47
+ //#region Execution
48
+ /** @public */
49
+ export { execute } from './interpreter/execute';
50
+ /** @public */
51
+ export { durableTaskState } from './memory/layers/durable-task-state';
52
+ /** @public */
53
+ export { fileReference } from './memory/layers/file-reference';
54
+ /** @public */
55
+ export { historyWindow } from './memory/layers/history-window';
56
+ /** @public */
57
+ export { observationalMemory } from './memory/layers/observational-memory';
58
+ /** @public */
59
+ export { PlanPhase, planMemory } from './memory/layers/plan';
60
+ /** @public */
61
+ export { staticContent } from './memory/layers/static-content';
62
+ /** @public */
63
+ export { steering } from './memory/layers/steering';
64
+ /** @public */
65
+ export { toolMemoryLayer } from './memory/layers/tool-memory-layer';
66
+ /** @public */
67
+ export { workingMemory } from './memory/layers/working-memory';
68
+ /** @public */
69
+ export { stripUnresolvedToolCalls } from './memory/strip-unresolved';
70
+ //#endregion
71
+ //#region Observability
72
+ /** @public */
73
+ export { InMemoryExporter, NoopExporter } from './observability/trace-exporter';
74
+ /** @public */
75
+ export { createInMemoryStorage } from './runtime/in-memory-storage';
76
+ /** @public */
77
+ export { FlowSchema, flowDepth, validateFlow, walkFlow } from './patterns/flow';
78
+ /** @public */
79
+ export { interview } from './patterns/interview';
80
+ /** @public */
81
+ export { adaptivePlan, compilePlan, PlanNodeSchema } from './patterns/plans';
82
+ /** @public */
83
+ export { ralphWiggum } from './patterns/ralph-wiggum';
84
+ /** @public */
85
+ export { react } from './patterns/react';
86
+ //#endregion
87
+ //#region Runtime
88
+ /** @public */
89
+ export { AgentHarness } from './runtime/agent-harness';
90
+ //#endregion
91
+ //#region Schemas
92
+ /** @public */
93
+ export { defaultItemSchemaRegistry, ItemSchema, ItemSchemaRegistry } from './schemas/item';
94
+ //#endregion
95
+ //#region Types — Context
96
+ /** @public */
97
+ export { getToolCwd, setToolCwd, snapshotCwdState } from './runtime/cwd-helpers';
98
+ /** @public */
99
+ export { DetachedStatus } from './types/detached';
100
+ /** @public */
101
+ export { Slot } from './types/memory';
102
+ /** @public */
103
+ export { LedgerEntryKind, SteeringAction } from './types/steering';
104
+ /** @public */
105
+ export { TIMEOUT_ERROR_PREFIX } from './types/shell-adapter';
106
+ //#endregion
107
+ //#region Until
108
+ /** @public */
109
+ export { all, any } from './until/combinators';
110
+ /** @public */
111
+ export { until } from './until/predicates';
112
+ //#endregion
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @internal
3
+ * Test-only exports for cross-package testing.
4
+ *
5
+ * These are implementation classes that tests need to construct mock instances.
6
+ * Application code should NEVER import from this path.
7
+ */
8
+ export { SpanImpl } from './observability/span-impl';
9
+ export { ChannelStore } from './runtime/channel-store';
10
+ export { ContextImpl } from './runtime/context-impl';
11
+ export { ItemLogImpl } from './runtime/item-log-impl';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @internal
3
+ * Test-only exports for cross-package testing.
4
+ *
5
+ * These are implementation classes that tests need to construct mock instances.
6
+ * Application code should NEVER import from this path.
7
+ */
8
+ export { SpanImpl } from './observability/span-impl';
9
+ export { ChannelStore } from './runtime/channel-store';
10
+ export { ContextImpl } from './runtime/context-impl';
11
+ export { ItemLogImpl } from './runtime/item-log-impl';
@@ -0,0 +1,4 @@
1
+ export declare function cloneWithGuard<T>(value: T, label: string, opts?: {
2
+ warnBytes?: number;
3
+ maxBytes?: number;
4
+ }): T;
@@ -0,0 +1,18 @@
1
+ const DEFAULT_WARN_BYTES = 10 * 1024 * 1024; // 10MB
2
+ function estimateSize(value) {
3
+ try {
4
+ return JSON.stringify(value)?.length ?? 0;
5
+ }
6
+ catch {
7
+ return 0;
8
+ }
9
+ }
10
+ export function cloneWithGuard(value, label, opts) {
11
+ const warnBytes = opts?.warnBytes ?? DEFAULT_WARN_BYTES;
12
+ const size = estimateSize(value);
13
+ if (size > warnBytes) {
14
+ console.warn(`[noetic] ${label}: state size (~${Math.round(size / 1024 / 1024)}MB) exceeds ${Math.round(warnBytes / 1024 / 1024)}MB threshold. ` +
15
+ 'Consider reducing state size to avoid performance issues.');
16
+ }
17
+ return structuredClone(value);
18
+ }
@@ -0,0 +1,12 @@
1
+ import type { Tool } from '../types/common';
2
+ import type { Step } from '../types/step';
3
+ /**
4
+ * Recursively walks a step tree and collects all Tool instances
5
+ * declared on LLM steps. Used to build the unified tool set
6
+ * that gets sent with every LLM call for prompt cache efficiency.
7
+ */
8
+ export declare function collectAllTools<TMemory = unknown, I = unknown, O = unknown>(step: Step<TMemory, I, O>): Tool[];
9
+ /**
10
+ * Deduplicates tools by name, keeping the first occurrence.
11
+ */
12
+ export declare function deduplicateTools(tools: ReadonlyArray<Tool>): Tool[];
@@ -0,0 +1,70 @@
1
+ import { frameworkCast } from './framework-cast';
2
+ /**
3
+ * Recursively walks a step tree and collects all Tool instances
4
+ * declared on LLM steps. Used to build the unified tool set
5
+ * that gets sent with every LLM call for prompt cache efficiency.
6
+ */
7
+ export function collectAllTools(step) {
8
+ const tools = [];
9
+ // walkStep uses Step with defaults (unknown generics); the cast is safe because
10
+ // we only read the `.tools`, `.child`, `.steps`, `._optimizable` structural fields.
11
+ walkStep(frameworkCast(step), tools);
12
+ return deduplicateTools(tools);
13
+ }
14
+ /**
15
+ * Deduplicates tools by name, keeping the first occurrence.
16
+ */
17
+ export function deduplicateTools(tools) {
18
+ const seen = new Map();
19
+ for (const tool of tools) {
20
+ if (!seen.has(tool.name)) {
21
+ seen.set(tool.name, tool);
22
+ }
23
+ }
24
+ return [
25
+ ...seen.values(),
26
+ ];
27
+ }
28
+ function walkStep(step, out) {
29
+ switch (step.kind) {
30
+ case 'llm':
31
+ if (step.tools) {
32
+ out.push(...step.tools);
33
+ }
34
+ return;
35
+ case 'run':
36
+ case 'tool':
37
+ return;
38
+ case 'branch':
39
+ if (step._optimizable) {
40
+ for (const child of step._optimizable) {
41
+ walkStep(child, out);
42
+ }
43
+ }
44
+ return;
45
+ case 'fork':
46
+ if (step._optimizable) {
47
+ for (const child of step._optimizable) {
48
+ walkStep(child, out);
49
+ }
50
+ }
51
+ return;
52
+ case 'provide':
53
+ walkStep(step.child, out);
54
+ return;
55
+ case 'spawn':
56
+ walkStep(step.child, out);
57
+ return;
58
+ case 'loop':
59
+ for (const child of step.steps) {
60
+ walkStep(child, out);
61
+ }
62
+ return;
63
+ case 'every':
64
+ walkStep(step.step, out);
65
+ return;
66
+ default:
67
+ step;
68
+ return;
69
+ }
70
+ }
@@ -0,0 +1,3 @@
1
+ import type { Context } from '../types/context';
2
+ import type { ExecuteStepFn, StepBranch } from '../types/step';
3
+ export declare function executeBranch<TMemory, I, O>(step: StepBranch<TMemory, I, O>, input: I, ctx: Context<TMemory>, executeStep: ExecuteStepFn): Promise<O>;
@@ -0,0 +1,10 @@
1
+ import { frameworkCast } from './framework-cast';
2
+ export async function executeBranch(step, input, ctx, executeStep) {
3
+ const selected = await step.route(input, ctx);
4
+ if (selected === null) {
5
+ // Requires I assignable to O for null route — when no branch is selected,
6
+ // the input passes through. Callers must ensure I is compatible with O.
7
+ return frameworkCast(input);
8
+ }
9
+ return executeStep(selected, input, ctx);
10
+ }
@@ -0,0 +1,15 @@
1
+ import type { Context } from '../types/context';
2
+ import type { ExecuteStepFn, StepEvery } from '../types/step';
3
+ /**
4
+ * Executes an `every` step: runs the body step forever, paced by `ms ± jitter`,
5
+ * woken early by `wakeOn`. Throws `cancelled` when the context is aborted.
6
+ *
7
+ * On body throw, behavior depends on `onError`:
8
+ * - `'continue'` (default): emit `every.iteration.error` span event and proceed
9
+ * to the park step as if no error occurred.
10
+ * - `'fail'`: re-throw, terminating the operator.
11
+ *
12
+ * The body's output is discarded — `every` runs forever and does not accumulate
13
+ * iteration outputs. Only ever returns by throwing on cancellation or `fail`.
14
+ */
15
+ export declare function executeEvery<TMemory, I, O>(step: StepEvery<TMemory, I, O>, input: I, ctx: Context<TMemory>, executeStep: ExecuteStepFn): Promise<O>;
@@ -0,0 +1,143 @@
1
+ import { isNoeticError, NoeticErrorImpl } from '../errors/noetic-error';
2
+ import { getContextChannelStore } from './typeguards';
3
+ //#endregion
4
+ //#region Helper Functions
5
+ /** Returns the next park duration in ms, applying random jitter clamped to `[ms - jitter, ms + jitter]`. */
6
+ function nextParkMs(ms, jitter) {
7
+ if (jitter <= 0) {
8
+ return ms;
9
+ }
10
+ // Math.random() returns [0, 1); shift to [-1, 1) so jitter is symmetric.
11
+ const offset = (Math.random() * 2 - 1) * jitter;
12
+ const value = ms + offset;
13
+ if (value < 0) {
14
+ return 0;
15
+ }
16
+ return value;
17
+ }
18
+ /**
19
+ * Parks for `ms` ms, returning early if `wakeOn` receives a message or the
20
+ * context is aborted. Always resolves; never throws (cancellation surfaces on
21
+ * the next iteration's abort check).
22
+ */
23
+ function park(parkCtx) {
24
+ const duration = nextParkMs(parkCtx.ms, parkCtx.jitter);
25
+ return new Promise((resolve) => {
26
+ let settled = false;
27
+ let timer = null;
28
+ let abortPoll = null;
29
+ let wakeUnsub = null;
30
+ const settle = () => {
31
+ if (settled) {
32
+ return;
33
+ }
34
+ settled = true;
35
+ if (timer !== null) {
36
+ clearTimeout(timer);
37
+ }
38
+ if (abortPoll !== null) {
39
+ clearInterval(abortPoll);
40
+ }
41
+ if (wakeUnsub !== null) {
42
+ wakeUnsub();
43
+ }
44
+ resolve();
45
+ };
46
+ timer = setTimeout(settle, duration);
47
+ // Poll the abort flag at a coarse interval to wake parking when the
48
+ // context is aborted. The Context interface exposes only `aborted` as a
49
+ // boolean, so this is the simplest interruption strategy that respects
50
+ // the public contract.
51
+ abortPoll = setInterval(() => {
52
+ if (parkCtx.ctx.aborted) {
53
+ settle();
54
+ }
55
+ }, 5);
56
+ if (!parkCtx.wakeOn) {
57
+ return;
58
+ }
59
+ const { channelStore } = parkCtx;
60
+ if (channelStore) {
61
+ // Non-consuming subscription so the body still observes the message that
62
+ // woke us — `recv()` would dequeue queue-mode messages and leave the
63
+ // body's drain loop empty.
64
+ wakeUnsub = channelStore.subscribeWake(parkCtx.wakeOn, settle);
65
+ return;
66
+ }
67
+ // Contexts without a ContextImpl/channel store have no body draining the
68
+ // channel anyway; the destructive `recv` is acceptable in that edge case.
69
+ parkCtx.ctx
70
+ .recv(parkCtx.wakeOn, {
71
+ timeout: Math.max(duration, 1) + 1e2,
72
+ })
73
+ .then(settle)
74
+ .catch(() => {
75
+ // Channel timeout / store error — another path settles us.
76
+ });
77
+ });
78
+ }
79
+ function recordIterationError(ctx, error) {
80
+ const message = error instanceof Error ? error.message : String(error);
81
+ const stack = error instanceof Error && error.stack ? error.stack : '';
82
+ ctx.span.addEvent('every.iteration.error', {
83
+ message,
84
+ stack,
85
+ });
86
+ }
87
+ function throwCancelled(reason) {
88
+ throw new NoeticErrorImpl({
89
+ kind: 'cancelled',
90
+ reason: reason ?? 'context aborted',
91
+ });
92
+ }
93
+ //#endregion
94
+ //#region Public API
95
+ /**
96
+ * Executes an `every` step: runs the body step forever, paced by `ms ± jitter`,
97
+ * woken early by `wakeOn`. Throws `cancelled` when the context is aborted.
98
+ *
99
+ * On body throw, behavior depends on `onError`:
100
+ * - `'continue'` (default): emit `every.iteration.error` span event and proceed
101
+ * to the park step as if no error occurred.
102
+ * - `'fail'`: re-throw, terminating the operator.
103
+ *
104
+ * The body's output is discarded — `every` runs forever and does not accumulate
105
+ * iteration outputs. Only ever returns by throwing on cancellation or `fail`.
106
+ */
107
+ export async function executeEvery(step, input, ctx, executeStep) {
108
+ const onError = step.onError ?? 'continue';
109
+ const jitter = step.jitter ?? 0;
110
+ // Resolve once — the channel store reference is stable for the lifetime
111
+ // of the every loop, no need to re-derive it per park.
112
+ const channelStore = getContextChannelStore(ctx);
113
+ while (true) {
114
+ if (ctx.aborted) {
115
+ throwCancelled(ctx.abortReason);
116
+ }
117
+ try {
118
+ await executeStep(step.step, input, ctx);
119
+ }
120
+ catch (e) {
121
+ if (onError === 'fail') {
122
+ throw e;
123
+ }
124
+ // 'continue' policy — but a cancellation should still terminate the
125
+ // operator promptly rather than be swallowed and re-parked.
126
+ if (isNoeticError(e) && e.noeticError.kind === 'cancelled') {
127
+ throw e;
128
+ }
129
+ recordIterationError(ctx, e);
130
+ }
131
+ if (ctx.aborted) {
132
+ throwCancelled(ctx.abortReason);
133
+ }
134
+ await park({
135
+ ms: step.ms,
136
+ jitter,
137
+ wakeOn: step.wakeOn,
138
+ ctx,
139
+ channelStore,
140
+ });
141
+ }
142
+ }
143
+ //#endregion
@@ -0,0 +1,3 @@
1
+ import type { Context } from '../types/context';
2
+ import type { ExecuteStepFn, StepFork } from '../types/step';
3
+ export declare function executeFork<TMemory, I, O>(step: StepFork<TMemory, I, O>, input: I, ctx: Context<TMemory>, executeStep: ExecuteStepFn): Promise<O>;
@@ -0,0 +1,215 @@
1
+ import { isNoeticError, NoeticErrorImpl } from '../errors/noetic-error';
2
+ import { ContextImpl } from '../runtime/context-impl';
3
+ import { snapshotCwdState } from '../runtime/cwd-helpers';
4
+ import { cloneWithGuard } from './clone-guard';
5
+ import { frameworkCast } from './framework-cast';
6
+ import { getContextChannelStore, isContextImpl } from './typeguards';
7
+ function createChildContexts(ctx, count, stepId) {
8
+ const threadId = isContextImpl(ctx) ? ctx.threadId : crypto.randomUUID();
9
+ const resourceId = isContextImpl(ctx) ? ctx.resourceId : undefined;
10
+ const channelStore = getContextChannelStore(ctx);
11
+ return Array.from({
12
+ length: count,
13
+ }, () => new ContextImpl({
14
+ harness: ctx.harness,
15
+ parent: ctx,
16
+ items: [
17
+ ...ctx.itemLog.items,
18
+ ],
19
+ state: cloneWithGuard(ctx.state, `Fork '${stepId}'`),
20
+ threadId,
21
+ resourceId,
22
+ channelStore,
23
+ cwdState: snapshotCwdState(ctx),
24
+ }));
25
+ }
26
+ export async function executeFork(step, input, ctx, executeStep) {
27
+ const baseCtx = frameworkCast(ctx);
28
+ const paths = step.paths(input, ctx);
29
+ if (paths.length === 0) {
30
+ if (step.mode === 'all') {
31
+ return step.merge([], ctx);
32
+ }
33
+ if (step.mode === 'settle') {
34
+ return step.merge([], ctx);
35
+ }
36
+ throw new NoeticErrorImpl({
37
+ kind: 'fork_partial',
38
+ stepId: step.id,
39
+ succeeded: [],
40
+ failed: [],
41
+ });
42
+ }
43
+ const childContexts = createChildContexts(baseCtx, paths.length, step.id);
44
+ const concurrency = step.concurrency ?? paths.length;
45
+ switch (step.mode) {
46
+ case 'all':
47
+ return executeAll(step, paths, input, ctx, childContexts, executeStep, concurrency);
48
+ case 'race':
49
+ return executeRace(step, paths, input, ctx, childContexts, executeStep, concurrency);
50
+ case 'settle':
51
+ return executeSettle(step, paths, input, ctx, childContexts, executeStep, concurrency);
52
+ default: {
53
+ const _exhaustive = step;
54
+ throw new NoeticErrorImpl({
55
+ kind: 'step_failed',
56
+ stepId: 'unknown',
57
+ cause: new Error('Unknown fork mode'),
58
+ retriesExhausted: false,
59
+ });
60
+ }
61
+ }
62
+ }
63
+ async function runWithConcurrency(tasks, concurrency) {
64
+ const results = new Array(tasks.length);
65
+ let nextIndex = 0;
66
+ async function runNext() {
67
+ while (nextIndex < tasks.length) {
68
+ const index = nextIndex++;
69
+ try {
70
+ const value = await tasks[index]();
71
+ results[index] = {
72
+ status: 'fulfilled',
73
+ value,
74
+ };
75
+ }
76
+ catch (reason) {
77
+ results[index] = {
78
+ status: 'rejected',
79
+ reason,
80
+ };
81
+ }
82
+ }
83
+ }
84
+ const workers = Array.from({
85
+ length: Math.min(concurrency, tasks.length),
86
+ }, () => runNext());
87
+ await Promise.all(workers);
88
+ return results;
89
+ }
90
+ function toNoeticError(err, stepId) {
91
+ if (isNoeticError(err)) {
92
+ return err.noeticError;
93
+ }
94
+ return {
95
+ kind: 'step_failed',
96
+ stepId,
97
+ cause: err instanceof Error ? err : new Error(String(err)),
98
+ retriesExhausted: false,
99
+ };
100
+ }
101
+ function classifyResults(settled, paths) {
102
+ const succeeded = [];
103
+ const failed = [];
104
+ settled.forEach((result, i) => {
105
+ if (result.status === 'fulfilled') {
106
+ succeeded.push({
107
+ stepId: paths[i].id,
108
+ value: result.value,
109
+ });
110
+ }
111
+ else {
112
+ failed.push({
113
+ stepId: paths[i].id,
114
+ error: toNoeticError(result.reason, paths[i].id),
115
+ });
116
+ }
117
+ });
118
+ return {
119
+ succeeded,
120
+ failed,
121
+ };
122
+ }
123
+ async function executeAll(step, paths, _input, ctx, childContexts, executeStep, concurrency) {
124
+ const tasks = paths.map((path, i) => () => executeStep(path, _input, frameworkCast(childContexts[i])));
125
+ const settled = await runWithConcurrency(tasks, concurrency);
126
+ const { succeeded, failed } = classifyResults(settled, paths);
127
+ if (failed.length > 0) {
128
+ throw new NoeticErrorImpl({
129
+ kind: 'fork_partial',
130
+ stepId: step.id,
131
+ succeeded,
132
+ failed,
133
+ });
134
+ }
135
+ const results = succeeded.map((s) => s.value);
136
+ return step.merge(results, ctx);
137
+ }
138
+ async function executeRace(step, paths, input, ctx, childContexts, executeStep, concurrency) {
139
+ const raceBaseCtx = frameworkCast(ctx);
140
+ return new Promise((resolve, reject) => {
141
+ let settled = false;
142
+ let failedCount = 0;
143
+ let nextIndex = 0;
144
+ const errors = [];
145
+ const totalPaths = paths.length;
146
+ function startNext() {
147
+ if (settled || nextIndex >= totalPaths) {
148
+ return;
149
+ }
150
+ const i = nextIndex++;
151
+ executeStep(paths[i], input, frameworkCast(childContexts[i]))
152
+ .then((result) => {
153
+ if (settled) {
154
+ return;
155
+ }
156
+ settled = true;
157
+ // Winner's state replaces parent's
158
+ if (isContextImpl(raceBaseCtx)) {
159
+ raceBaseCtx.state = childContexts[i].state;
160
+ }
161
+ // Resolve first, then abort losers (non-critical path)
162
+ resolve(result);
163
+ for (let j = 0; j < childContexts.length; j++) {
164
+ if (j !== i) {
165
+ childContexts[j].abort('race lost');
166
+ }
167
+ }
168
+ })
169
+ .catch((err) => {
170
+ failedCount++;
171
+ errors.push({
172
+ stepId: paths[i].id,
173
+ error: toNoeticError(err, paths[i].id),
174
+ });
175
+ // Start next task if available
176
+ if (!settled) {
177
+ startNext();
178
+ }
179
+ if (failedCount === totalPaths && !settled) {
180
+ settled = true;
181
+ reject(new NoeticErrorImpl({
182
+ kind: 'fork_partial',
183
+ stepId: step.id,
184
+ succeeded: [],
185
+ failed: errors,
186
+ }));
187
+ }
188
+ });
189
+ }
190
+ // Launch initial batch respecting concurrency
191
+ const initialBatch = Math.min(concurrency, totalPaths);
192
+ for (let i = 0; i < initialBatch; i++) {
193
+ startNext();
194
+ }
195
+ });
196
+ }
197
+ async function executeSettle(step, paths, input, _ctx, childContexts, executeStep, concurrency) {
198
+ const tasks = paths.map((path, i) => () => executeStep(path, input, frameworkCast(childContexts[i])));
199
+ const settled = await runWithConcurrency(tasks, concurrency);
200
+ const results = settled.map((result, i) => {
201
+ if (result.status === 'fulfilled') {
202
+ return {
203
+ stepId: paths[i].id,
204
+ status: 'fulfilled',
205
+ value: result.value,
206
+ };
207
+ }
208
+ return {
209
+ stepId: paths[i].id,
210
+ status: 'rejected',
211
+ error: toNoeticError(result.reason, paths[i].id),
212
+ };
213
+ });
214
+ return step.merge(results, _ctx);
215
+ }
@@ -0,0 +1,4 @@
1
+ import type { Context } from '../types/context';
2
+ import type { MemoryLayer } from '../types/memory';
3
+ import type { StepLLM } from '../types/step';
4
+ export declare function executeLLM<TMemory, I, O>(step: StepLLM<TMemory, I, O>, input: I, ctx: Context<TMemory>, layers?: MemoryLayer[]): Promise<O>;