@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,17 +1,104 @@
1
- import { NoeticErrorImpl } from '../errors/noetic-error';
2
- import { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils';
3
- import { executeBranch } from './execute-branch';
4
- import { executeEvery } from './execute-every';
5
- import { executeFork } from './execute-fork';
6
- import { executeLLM } from './execute-llm';
7
- import { executeLoop } from './execute-loop';
8
- import { executeProvide } from './execute-provide';
9
- import { executeRun } from './execute-run';
10
- import { executeSpawn } from './execute-spawn';
11
- import { executeTool } from './execute-tool';
12
- import { frameworkCast } from './framework-cast';
13
- import { isMutableContext } from './typeguards';
1
+ import { NoeticErrorImpl } from '../errors/noetic-error.js';
2
+ import { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils.js';
3
+ import { ContextImpl } from '../runtime/context-impl.js';
4
+ import { DetachedHandleImpl } from '../runtime/detached-handle.js';
5
+ import { frameworkCast } from '../util/framework-cast.js';
6
+ import { executeLLM, executeProvide, executeRun, executeSpawn, executeTool, } from './execute-action.js';
7
+ import { executeBranch, executeEvery, executeFork, executeLoop } from './execute-control.js';
8
+ import { isMutableContext } from './typeguards.js';
9
+ //#region Constants
14
10
  const MAX_DEPTH = 64;
11
+ //#endregion
12
+ //#region Helpers
13
+ /**
14
+ * Return the underlying `ContextImpl` when the context is one we produced
15
+ * in-process. Out-of-process descendants reach us through the adapter
16
+ * boundary and are not `ContextImpl` instances, so frontier bookkeeping is
17
+ * a no-op for them.
18
+ */
19
+ function asContextImpl(ctx) {
20
+ return ctx instanceof ContextImpl ? ctx : null;
21
+ }
22
+ function harnessWithLayerStore(ctx) {
23
+ return frameworkCast(ctx.harness);
24
+ }
25
+ function buildSpawnOpts(ctx) {
26
+ const baseCtx = frameworkCast(ctx);
27
+ return {
28
+ itemSchemas: baseCtx.itemSchemas,
29
+ parentLayers: baseCtx.layers,
30
+ layerStore: harnessWithLayerStore(baseCtx).layerStateStore,
31
+ };
32
+ }
33
+ /**
34
+ * Resolve the subprocess adapter that should handle dispatch for this step.
35
+ * Precedence is `step.subprocess ?? ctx.harness.subprocess`. Per-call
36
+ * `detachedSpawn` overrides layer above this in the harness itself.
37
+ */
38
+ function resolveStepAdapter(step, ctx) {
39
+ if ((step.kind === 'run' || step.kind === 'spawn') && step.subprocess) {
40
+ return step.subprocess;
41
+ }
42
+ return ctx.harness.subprocess;
43
+ }
44
+ /**
45
+ * Dispatch a run/spawn step through the resolved subprocess adapter.
46
+ *
47
+ * The `_localExecutor` closure carries the in-process fallback path — the
48
+ * in-memory adapter invokes it directly, preserving the pre-refactor
49
+ * dispatch semantics including parent-context inheritance, layer lifecycle,
50
+ * and synchronous error propagation. Out-of-process adapters ignore
51
+ * `_localExecutor` and run the step in a child runtime.
52
+ */
53
+ async function dispatchViaAdapter(step, input, ctx, executor) {
54
+ const adapter = resolveStepAdapter(step, ctx);
55
+ const request = {
56
+ kind: 'step',
57
+ stepId: step.id,
58
+ serializedInput: input,
59
+ executionId: ctx.id,
60
+ overrides: {
61
+ threadId: ctx.threadId,
62
+ resourceId: ctx.resourceId,
63
+ },
64
+ _localExecutor: () => executor(),
65
+ };
66
+ const spawnPromise = adapter.spawn(request);
67
+ const handle = new DetachedHandleImpl({
68
+ id: ctx.id,
69
+ stepId: step.id,
70
+ adapter,
71
+ spawnPromise,
72
+ });
73
+ return handle.await();
74
+ }
75
+ /**
76
+ * @internal
77
+ * Dispatch a step at the per-kind handler level without adapter routing.
78
+ *
79
+ * Used as the top-level `_localExecutor` for `StepRun` / `StepSpawn`
80
+ * dispatches: the outer adapter call already recorded the request, so
81
+ * running the handler directly avoids a re-entrant adapter round-trip
82
+ * inside the same logical step. Nested step dispatches (e.g. `executeSpawn`
83
+ * descending into `step.child`) still go through `execute()` and route
84
+ * through the adapter as normal, so per-step overrides on descendants
85
+ * continue to work.
86
+ */
87
+ export async function executeNoAdapter(step, input, ctx) {
88
+ switch (step.kind) {
89
+ case 'run':
90
+ return executeRun(step, input, ctx);
91
+ case 'spawn':
92
+ return executeSpawn(step, input, ctx, (s, i, c) => execute(s, i, c), buildSpawnOpts(ctx));
93
+ default:
94
+ // Kinds that don't currently route through the adapter (llm, tool,
95
+ // branch, fork, provide, loop, every). Delegate back to `execute()`
96
+ // so they exercise the normal dispatch table, framework-event emits,
97
+ // abort checks, and depth guard.
98
+ return execute(step, input, ctx);
99
+ }
100
+ }
101
+ //#endregion
15
102
  /**
16
103
  * Executes a step within the interpreter, dispatching to the appropriate handler by step kind.
17
104
  *
@@ -61,45 +148,61 @@ export async function execute(step, input, ctx) {
61
148
  data: startedData,
62
149
  });
63
150
  }
151
+ // Push onto the durable frontier so checkpoints can record exactly which
152
+ // step was in flight when the snapshot fired. Steps with empty ids are
153
+ // skipped — they are not reattachable by id and would pollute the stack.
154
+ const impl = asContextImpl(baseCtx);
155
+ if (impl && step.id.length > 0) {
156
+ impl.enterStep({
157
+ stepId: step.id,
158
+ input,
159
+ });
160
+ }
64
161
  let result;
65
- switch (step.kind) {
66
- case 'run':
67
- result = await executeRun(step, input, ctx);
68
- break;
69
- case 'llm':
70
- result = await executeLLM(step, input, ctx, baseCtx.layers);
71
- break;
72
- case 'tool':
73
- result = await executeTool(step, input, ctx, baseCtx.harness);
74
- break;
75
- case 'branch':
76
- result = await executeBranch(step, input, ctx, (s, i, c) => execute(s, i, c));
77
- break;
78
- case 'fork':
79
- result = await executeFork(step, input, ctx, (s, i, c) => execute(s, i, c));
80
- break;
81
- case 'spawn':
82
- result = await executeSpawn(step, input, ctx, (s, i, c) => execute(s, i, c), {
83
- itemSchemas: baseCtx.itemSchemas,
84
- });
85
- break;
86
- case 'provide':
87
- result = await executeProvide(step, input, ctx, (s, i, c) => execute(s, i, c));
88
- break;
89
- case 'loop':
90
- result = await executeLoop(step, input, ctx, (s, i, c) => execute(s, i, c));
91
- break;
92
- case 'every':
93
- result = await executeEvery(step, input, ctx, (s, i, c) => execute(s, i, c));
94
- break;
95
- default: {
96
- const _exhaustive = step;
97
- throw new NoeticErrorImpl({
98
- kind: 'step_failed',
99
- stepId: 'unknown',
100
- cause: new Error('Unknown step kind'),
101
- retriesExhausted: false,
102
- });
162
+ try {
163
+ switch (step.kind) {
164
+ case 'run':
165
+ result = await dispatchViaAdapter(step, input, ctx, () => executeRun(step, input, ctx));
166
+ break;
167
+ case 'llm':
168
+ result = await executeLLM(step, input, ctx, baseCtx.layers);
169
+ break;
170
+ case 'tool':
171
+ result = await executeTool(step, input, ctx, baseCtx.harness);
172
+ break;
173
+ case 'branch':
174
+ result = await executeBranch(step, input, ctx, (s, i, c) => execute(s, i, c));
175
+ break;
176
+ case 'fork':
177
+ result = await executeFork(step, input, ctx, (s, i, c) => execute(s, i, c));
178
+ break;
179
+ case 'spawn':
180
+ result = await dispatchViaAdapter(step, input, ctx, () => executeSpawn(step, input, ctx, (s, i, c) => execute(s, i, c), buildSpawnOpts(ctx)));
181
+ break;
182
+ case 'provide':
183
+ result = await executeProvide(step, input, ctx, (s, i, c) => execute(s, i, c));
184
+ break;
185
+ case 'loop':
186
+ result = await executeLoop(step, input, ctx, (s, i, c) => execute(s, i, c));
187
+ break;
188
+ case 'every':
189
+ result = await executeEvery(step, input, ctx, (s, i, c) => execute(s, i, c));
190
+ break;
191
+ default: {
192
+ const _exhaustive = step;
193
+ void _exhaustive;
194
+ throw new NoeticErrorImpl({
195
+ kind: 'step_failed',
196
+ stepId: 'unknown',
197
+ cause: new Error('Unknown step kind'),
198
+ retriesExhausted: false,
199
+ });
200
+ }
201
+ }
202
+ }
203
+ finally {
204
+ if (impl && step.id.length > 0) {
205
+ impl.leaveStep(step.id);
103
206
  }
104
207
  }
105
208
  // Emit step_completed framework event (respects step.emit option)
@@ -1,9 +1 @@
1
- import type { LLMResponse } from '../types/common';
2
- import type { Context } from '../types/context';
3
- import type { InputMessageItem, Item } from '../types/items';
4
- export declare function createMessage(text: string, role: 'user' | 'developer'): InputMessageItem;
5
- export declare function extractAssistantText(items: ReadonlyArray<Item>): string;
6
- /** Naive token estimate: ~4 chars per token */
7
- export declare function estimateTokens(text: string): number;
8
- /** Accumulate token usage and cost from an LLM response onto the context. */
9
- export declare function trackUsage(ctx: Context, response: LLMResponse): void;
1
+ export { trackUsage } from '../util/message-helpers.js';
@@ -1,42 +1 @@
1
- import { isAssistantMessage, isMutableContext, isOutputText } from './typeguards';
2
- export function createMessage(text, role) {
3
- return {
4
- id: crypto.randomUUID(),
5
- status: 'completed',
6
- type: 'message',
7
- role,
8
- content: [
9
- {
10
- type: 'input_text',
11
- text,
12
- },
13
- ],
14
- };
15
- }
16
- export function extractAssistantText(items) {
17
- const lastMsg = items.findLast(isAssistantMessage);
18
- if (!lastMsg) {
19
- return '';
20
- }
21
- return (lastMsg.content
22
- ?.filter(isOutputText)
23
- ?.map((c) => c.text)
24
- ?.join('') ?? '');
25
- }
26
- /** Naive token estimate: ~4 chars per token */
27
- export function estimateTokens(text) {
28
- return Math.ceil(text.length / 4);
29
- }
30
- /** Accumulate token usage and cost from an LLM response onto the context. */
31
- export function trackUsage(ctx, response) {
32
- if (!isMutableContext(ctx)) {
33
- return;
34
- }
35
- ctx.tokens.input += response.usage.inputTokens;
36
- ctx.tokens.output += response.usage.outputTokens;
37
- ctx.tokens.total += response.usage.inputTokens + response.usage.outputTokens;
38
- ctx.tokens.cached = (ctx.tokens.cached ?? 0) + (response.usage.cachedTokens ?? 0);
39
- if (response.cost) {
40
- ctx.cost = ctx.cost + response.cost;
41
- }
42
- }
1
+ export { trackUsage } from '../util/message-helpers.js';
@@ -0,0 +1,61 @@
1
+ import type { Context } from '../types/context.js';
2
+ import type { DetachedHandle } from '../types/detached.js';
3
+ import type { ContextMemory } from '../types/memory.js';
4
+ import type { Step } from '../types/step.js';
5
+ import type { SubprocessAdapter } from '../types/subprocess-adapter.js';
6
+ /**
7
+ * Resolve the subprocess adapter used for a given step dispatch.
8
+ *
9
+ * Precedence: `detachedSpawn-overrides.subprocess ?? step.subprocess ??
10
+ * harness.subprocess`. Only `StepRun` and `StepSpawn` currently carry a
11
+ * `subprocess` field — other variants always fall through to the harness
12
+ * default.
13
+ *
14
+ * @internal
15
+ */
16
+ export declare function resolveStepAdapter<TMemory, I, O>(step: Step<TMemory, I, O>, callOverride: SubprocessAdapter | undefined, fallback: SubprocessAdapter): SubprocessAdapter;
17
+ /**
18
+ * Minimum harness surface the dispatcher needs. `AgentHarness` satisfies
19
+ * this shape — kept structural so tests can pass a stub.
20
+ *
21
+ * @internal
22
+ */
23
+ export interface StepDispatchHandle {
24
+ readonly subprocess: SubprocessAdapter;
25
+ createContext(opts?: {
26
+ parent?: Context;
27
+ threadId?: string;
28
+ resourceId?: string;
29
+ cwdInit?: string;
30
+ }): Context;
31
+ }
32
+ export interface DetachedSpawnOverrides {
33
+ /** Override the child's thread id. Default inherits from `parentCtx.threadId`. */
34
+ threadId?: string;
35
+ /** Override the child's resource id. Default inherits from `parentCtx.resourceId`. */
36
+ resourceId?: string;
37
+ /** Override the child's initial cwd. Used by worktree isolation to root the child at the worktree path. */
38
+ cwdInit?: string;
39
+ /**
40
+ * Per-call subprocess adapter override. Takes precedence over both
41
+ * `step.subprocess` and `harness.subprocess`. Pass a custom adapter to
42
+ * dispatch this specific spawn through a different runtime (e.g. a
43
+ * local OS subprocess or a test double that records the request).
44
+ */
45
+ subprocess?: SubprocessAdapter;
46
+ }
47
+ /**
48
+ * Route a step through the configured subprocess adapter and return a
49
+ * `DetachedHandle` that polls until the adapter reports a terminal
50
+ * status. The adapter's `spawn()` may be async (the default in-memory
51
+ * adapter schedules the step body on the microtask queue, and
52
+ * out-of-process adapters may await an OS spawn).
53
+ *
54
+ * `executeNoAdapter` is used as the local executor so the adapter we just
55
+ * routed through does not re-route the same step. The handler still uses
56
+ * `execute()` for nested children, so per-step overrides on descendants
57
+ * keep working.
58
+ *
59
+ * @internal
60
+ */
61
+ export declare function dispatchStepThroughAdapter<I, O>(h: StepDispatchHandle, s: Step<ContextMemory, I, O>, input: I, parentCtx: Context, overrides?: DetachedSpawnOverrides): DetachedHandle<O>;
@@ -0,0 +1,67 @@
1
+ import { DetachedHandleImpl } from '../runtime/detached-handle.js';
2
+ import { executeNoAdapter } from './execute.js';
3
+ //#region Adapter resolution
4
+ /**
5
+ * Resolve the subprocess adapter used for a given step dispatch.
6
+ *
7
+ * Precedence: `detachedSpawn-overrides.subprocess ?? step.subprocess ??
8
+ * harness.subprocess`. Only `StepRun` and `StepSpawn` currently carry a
9
+ * `subprocess` field — other variants always fall through to the harness
10
+ * default.
11
+ *
12
+ * @internal
13
+ */
14
+ export function resolveStepAdapter(step, callOverride, fallback) {
15
+ if (callOverride) {
16
+ return callOverride;
17
+ }
18
+ if ((step.kind === 'run' || step.kind === 'spawn') && step.subprocess) {
19
+ return step.subprocess;
20
+ }
21
+ return fallback;
22
+ }
23
+ //#endregion
24
+ //#region Dispatcher
25
+ /**
26
+ * Route a step through the configured subprocess adapter and return a
27
+ * `DetachedHandle` that polls until the adapter reports a terminal
28
+ * status. The adapter's `spawn()` may be async (the default in-memory
29
+ * adapter schedules the step body on the microtask queue, and
30
+ * out-of-process adapters may await an OS spawn).
31
+ *
32
+ * `executeNoAdapter` is used as the local executor so the adapter we just
33
+ * routed through does not re-route the same step. The handler still uses
34
+ * `execute()` for nested children, so per-step overrides on descendants
35
+ * keep working.
36
+ *
37
+ * @internal
38
+ */
39
+ export function dispatchStepThroughAdapter(h, s, input, parentCtx, overrides) {
40
+ const childCtx = h.createContext({
41
+ parent: parentCtx,
42
+ threadId: overrides?.threadId ?? parentCtx.threadId,
43
+ resourceId: overrides?.resourceId ?? parentCtx.resourceId,
44
+ cwdInit: overrides?.cwdInit,
45
+ });
46
+ const adapter = resolveStepAdapter(s, overrides?.subprocess, h.subprocess);
47
+ const request = {
48
+ kind: 'step',
49
+ stepId: s.id,
50
+ serializedInput: input,
51
+ executionId: childCtx.id,
52
+ overrides: {
53
+ threadId: overrides?.threadId ?? parentCtx.threadId,
54
+ resourceId: overrides?.resourceId ?? parentCtx.resourceId,
55
+ cwdInit: overrides?.cwdInit,
56
+ },
57
+ _localExecutor: () => executeNoAdapter(s, input, childCtx),
58
+ };
59
+ const spawnPromise = adapter.spawn(request);
60
+ return new DetachedHandleImpl({
61
+ id: childCtx.id,
62
+ stepId: s.id,
63
+ adapter,
64
+ spawnPromise,
65
+ });
66
+ }
67
+ //#endregion
@@ -1,25 +1,18 @@
1
- import type { ChannelStore } from '../runtime/channel-store';
2
- import { ContextImpl } from '../runtime/context-impl';
3
- import type { Context } from '../types/context';
4
- import type { FunctionCallItem, Item, MessageItem } from '../types/items';
5
- import type { ContextMemory } from '../types/memory';
6
- import type { MutableContext } from '../types/mutable-context';
1
+ import type { ChannelStore } from '../runtime/channel-store.js';
2
+ import { ContextImpl } from '../runtime/context-impl.js';
3
+ import type { Context } from '../types/context.js';
4
+ import type { FunctionCallItem, Item } from '../types/items.js';
5
+ import type { ContextMemory } from '../types/memory.js';
6
+ import type { MutableContext } from '../types/mutable-context.js';
7
+ export { isAssistantMessage, isOutputText, isUserMessage } from '../util/message-helpers.js';
7
8
  export declare function isMutableContext(ctx: Context<ContextMemory>): ctx is MutableContext;
8
9
  export declare function isContextImpl(ctx: Context<ContextMemory>): ctx is ContextImpl;
9
10
  /**
10
11
  * Co-located with `isContextImpl` so the import of `ContextImpl` flows through
11
12
  * the same module other interpreters pull it from, sidestepping a circular
12
- * TDZ when this helper is used by `execute-every.ts`.
13
+ * TDZ when this helper is used by `executeEvery` in `execute-control.ts`.
13
14
  */
14
15
  export declare function getContextChannelStore<TMemory>(ctx: Context<TMemory>): ChannelStore | undefined;
15
- export declare function isAssistantMessage(item: unknown): item is MessageItem;
16
- export declare function isUserMessage(item: unknown): item is MessageItem;
17
16
  export declare function isFunctionCall(item: Item): item is FunctionCallItem;
18
- export declare function isOutputText(part: {
19
- type: string;
20
- }): part is {
21
- type: 'output_text';
22
- text: string;
23
- };
24
17
  declare function assertNever(_value: never): never;
25
18
  export { assertNever };
@@ -1,4 +1,6 @@
1
- import { ContextImpl } from '../runtime/context-impl';
1
+ import { ContextImpl } from '../runtime/context-impl.js';
2
+ // Re-exports for backward compatibility; the pure variants live in util/.
3
+ export { isAssistantMessage, isOutputText, isUserMessage } from '../util/message-helpers.js';
2
4
  export function isMutableContext(ctx) {
3
5
  // Check if the context has writable mutable fields (ContextImpl or compatible mock)
4
6
  if (ctx instanceof ContextImpl) {
@@ -14,7 +16,7 @@ export function isContextImpl(ctx) {
14
16
  /**
15
17
  * Co-located with `isContextImpl` so the import of `ContextImpl` flows through
16
18
  * the same module other interpreters pull it from, sidestepping a circular
17
- * TDZ when this helper is used by `execute-every.ts`.
19
+ * TDZ when this helper is used by `executeEvery` in `execute-control.ts`.
18
20
  */
19
21
  export function getContextChannelStore(ctx) {
20
22
  if (ctx instanceof ContextImpl) {
@@ -22,28 +24,9 @@ export function getContextChannelStore(ctx) {
22
24
  }
23
25
  return undefined;
24
26
  }
25
- export function isAssistantMessage(item) {
26
- return (typeof item === 'object' &&
27
- item !== null &&
28
- 'type' in item &&
29
- item.type === 'message' &&
30
- 'role' in item &&
31
- item.role === 'assistant');
32
- }
33
- export function isUserMessage(item) {
34
- return (typeof item === 'object' &&
35
- item !== null &&
36
- 'type' in item &&
37
- item.type === 'message' &&
38
- 'role' in item &&
39
- item.role === 'user');
40
- }
41
27
  export function isFunctionCall(item) {
42
28
  return item.type === 'function_call' && 'callId' in item && 'name' in item;
43
29
  }
44
- export function isOutputText(part) {
45
- return part.type === 'output_text';
46
- }
47
30
  function assertNever(_value) {
48
31
  throw new Error('Exhaustive check failed');
49
32
  }
@@ -1,5 +1,5 @@
1
- import type { Context } from '../types/context';
2
- import type { MemoryLayer } from '../types/memory';
1
+ import type { Context } from '../types/context.js';
2
+ import type { MemoryLayer } from '../types/memory.js';
3
3
  export interface BudgetAllocation {
4
4
  layerId: string;
5
5
  allocated: number;
@@ -1,4 +1,4 @@
1
- import { NoeticErrorImpl } from '../errors/noetic-error';
1
+ import { NoeticErrorImpl } from '../errors/noetic-error.js';
2
2
  function extractMin(config) {
3
3
  if (config && typeof config === 'object' && 'min' in config) {
4
4
  return config.min;
@@ -1,5 +1,5 @@
1
- import type { Context } from '../types/context';
2
- import type { ExecutionContext } from '../types/memory';
1
+ import type { Context } from '../types/context.js';
2
+ import type { ExecutionContext } from '../types/memory.js';
3
3
  /**
4
4
  * Optional overrides for `contextToExecCtx`. Used by `executeSpawn` to build
5
5
  * parent and child execution contexts that reuse the same tokenize / trace /
@@ -1,4 +1,4 @@
1
- import { estimateTokens } from '../interpreter/message-helpers';
1
+ import { estimateTokens } from '../util/message-helpers.js';
2
2
  /**
3
3
  * Maps a full `Context` to the lightweight `ExecutionContext` consumed by memory layer hooks.
4
4
  * Centralised here so the mapping stays consistent across agent-harness, layer-api, and the
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Zod schema for plan-mode flow JSON.
3
+ *
4
+ * A "flow" is a JSON-serialisable subset of noetic's `Step` shape that the model
5
+ * may emit during plan mode. The runtime expands each node into a concrete `Step`
6
+ * via the existing builders (`step.llm`, `fork`, `spawn`) at execute time.
7
+ *
8
+ * Tool references inside `llm` nodes are name strings. They get resolved against
9
+ * the harness's live tool registry when the flow runs.
10
+ */
11
+ import { z } from 'zod';
12
+ interface NodeBase {
13
+ id: string;
14
+ subPlanRef?: string;
15
+ }
16
+ export interface LlmFlowNode extends NodeBase {
17
+ kind: 'llm';
18
+ model?: string;
19
+ instructions: string;
20
+ tools?: string[];
21
+ }
22
+ export interface SubagentFlowNode extends NodeBase {
23
+ kind: 'subagent';
24
+ preset: string;
25
+ prompt: string;
26
+ }
27
+ export interface ForkFlowNode extends NodeBase {
28
+ kind: 'fork';
29
+ mode: 'all' | 'race' | 'settle';
30
+ paths: FlowNode[];
31
+ }
32
+ export interface SpawnFlowNode extends NodeBase {
33
+ kind: 'spawn';
34
+ child: FlowNode;
35
+ }
36
+ export interface SequenceFlowNode extends NodeBase {
37
+ kind: 'sequence';
38
+ steps: FlowNode[];
39
+ }
40
+ export type FlowNode = LlmFlowNode | SubagentFlowNode | ForkFlowNode | SpawnFlowNode | SequenceFlowNode;
41
+ /** Top-level flow document: a single root node. */
42
+ export declare const FlowSchema: z.ZodType<FlowNode, unknown, z.core.$ZodTypeInternals<FlowNode, unknown>>;
43
+ /** Validates a candidate flow document. Throws `ZodError` on invalid input. */
44
+ export declare function validateFlow(input: unknown): FlowNode;
45
+ /** Walks a flow tree depth-first, yielding each node. */
46
+ export declare function walkFlow(root: FlowNode): Iterable<FlowNode>;
47
+ /**
48
+ * Returns the maximum depth of a flow tree. A leaf (llm/subagent) is depth 0.
49
+ * Structural nodes (sequence/fork/spawn) contribute +1 over their deepest child.
50
+ */
51
+ export declare function flowDepth(node: FlowNode): number;
52
+ export {};
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Zod schema for plan-mode flow JSON.
3
+ *
4
+ * A "flow" is a JSON-serialisable subset of noetic's `Step` shape that the model
5
+ * may emit during plan mode. The runtime expands each node into a concrete `Step`
6
+ * via the existing builders (`step.llm`, `fork`, `spawn`) at execute time.
7
+ *
8
+ * Tool references inside `llm` nodes are name strings. They get resolved against
9
+ * the harness's live tool registry when the flow runs.
10
+ */
11
+ import { z } from 'zod';
12
+ //#endregion
13
+ //#region Schema
14
+ const FlowNodeRef = z.lazy(() => FlowNodeSchema);
15
+ const SHARED_FIELDS = {
16
+ id: z.string().min(1),
17
+ subPlanRef: z.string().optional(),
18
+ };
19
+ const LlmNodeSchema = z.object({
20
+ kind: z.literal('llm'),
21
+ ...SHARED_FIELDS,
22
+ model: z.string().optional(),
23
+ instructions: z.string(),
24
+ tools: z.array(z.string()).optional(),
25
+ });
26
+ const SubagentNodeSchema = z.object({
27
+ kind: z.literal('subagent'),
28
+ ...SHARED_FIELDS,
29
+ preset: z.string().min(1),
30
+ prompt: z.string(),
31
+ });
32
+ const ForkNodeSchema = z.object({
33
+ kind: z.literal('fork'),
34
+ ...SHARED_FIELDS,
35
+ mode: z.enum([
36
+ 'all',
37
+ 'race',
38
+ 'settle',
39
+ ]),
40
+ paths: z.array(FlowNodeRef).min(1),
41
+ });
42
+ const SpawnNodeSchema = z.object({
43
+ kind: z.literal('spawn'),
44
+ ...SHARED_FIELDS,
45
+ child: FlowNodeRef,
46
+ });
47
+ const SequenceNodeSchema = z.object({
48
+ kind: z.literal('sequence'),
49
+ ...SHARED_FIELDS,
50
+ steps: z.array(FlowNodeRef).min(1),
51
+ });
52
+ const FlowNodeSchema = z.discriminatedUnion('kind', [
53
+ LlmNodeSchema,
54
+ SubagentNodeSchema,
55
+ ForkNodeSchema,
56
+ SpawnNodeSchema,
57
+ SequenceNodeSchema,
58
+ ]);
59
+ /** Top-level flow document: a single root node. */
60
+ export const FlowSchema = FlowNodeSchema;
61
+ //#endregion
62
+ //#region Public API
63
+ /** Validates a candidate flow document. Throws `ZodError` on invalid input. */
64
+ export function validateFlow(input) {
65
+ return FlowSchema.parse(input);
66
+ }
67
+ /** Walks a flow tree depth-first, yielding each node. */
68
+ export function* walkFlow(root) {
69
+ yield root;
70
+ if (root.kind === 'fork') {
71
+ for (const path of root.paths) {
72
+ yield* walkFlow(path);
73
+ }
74
+ return;
75
+ }
76
+ if (root.kind === 'sequence') {
77
+ for (const step of root.steps) {
78
+ yield* walkFlow(step);
79
+ }
80
+ return;
81
+ }
82
+ if (root.kind === 'spawn') {
83
+ yield* walkFlow(root.child);
84
+ }
85
+ }
86
+ /**
87
+ * Returns the maximum depth of a flow tree. A leaf (llm/subagent) is depth 0.
88
+ * Structural nodes (sequence/fork/spawn) contribute +1 over their deepest child.
89
+ */
90
+ export function flowDepth(node) {
91
+ if (node.kind === 'sequence') {
92
+ return 1 + Math.max(0, ...node.steps.map(flowDepth));
93
+ }
94
+ if (node.kind === 'fork') {
95
+ return 1 + Math.max(0, ...node.paths.map(flowDepth));
96
+ }
97
+ if (node.kind === 'spawn') {
98
+ return 1 + flowDepth(node.child);
99
+ }
100
+ return 0;
101
+ }
102
+ //#endregion