@noetic-tools/core 5.3.0 → 5.4.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 (94) hide show
  1. package/README.md +8 -6
  2. package/dist/adapters/in-memory-subprocess/step-completion.js +1 -1
  3. package/dist/adapters/openrouter.d.ts +12 -1
  4. package/dist/adapters/openrouter.js +79 -5
  5. package/dist/builders/acp-agent-tool.d.ts +67 -0
  6. package/dist/builders/acp-agent-tool.js +87 -0
  7. package/dist/builders/control-flow-builders.d.ts +13 -13
  8. package/dist/builders/control-flow-builders.js +11 -11
  9. package/dist/{patterns → builders}/dynamic-workflow.d.ts +1 -1
  10. package/dist/{patterns → builders}/dynamic-workflow.js +20 -14
  11. package/dist/builders/every.d.ts +14 -14
  12. package/dist/builders/every.js +16 -16
  13. package/dist/builders/layer-provides-builders.d.ts +1 -1
  14. package/dist/builders/layer-provides-builders.js +1 -1
  15. package/dist/builders/provide-builder.d.ts +5 -12
  16. package/dist/builders/provide-builder.js +9 -10
  17. package/dist/builders/spawn-builder.d.ts +0 -2
  18. package/dist/builders/spawn-builder.js +2 -3
  19. package/dist/builders/step-builders.d.ts +103 -92
  20. package/dist/builders/step-builders.js +146 -163
  21. package/dist/builders/tool-builder.d.ts +18 -22
  22. package/dist/builders/tool-builder.js +6 -4
  23. package/dist/builders/workflow-hydrator.d.ts +11 -7
  24. package/dist/builders/workflow-hydrator.js +75 -87
  25. package/dist/builders/workflow-step.d.ts +15 -27
  26. package/dist/builders/workflow-step.js +14 -19
  27. package/dist/conditions/conditions.d.ts +2 -2
  28. package/dist/conditions/conditions.js +1 -1
  29. package/dist/harness/agent-harness.d.ts +89 -46
  30. package/dist/harness/agent-harness.js +93 -39
  31. package/dist/harness/deps/types.d.ts +1 -1
  32. package/dist/harness/model-call.d.ts +1 -1
  33. package/dist/harness/model-call.js +23 -18
  34. package/dist/index.d.ts +27 -35
  35. package/dist/index.js +13 -29
  36. package/dist/interpreter/acp-events.d.ts +67 -0
  37. package/dist/interpreter/acp-events.js +256 -0
  38. package/dist/interpreter/acp-history.d.ts +22 -0
  39. package/dist/interpreter/acp-history.js +58 -0
  40. package/dist/interpreter/action-types.d.ts +1 -1
  41. package/dist/interpreter/collect-tools.js +10 -13
  42. package/dist/interpreter/execute-acp-agent.d.ts +10 -0
  43. package/dist/interpreter/execute-acp-agent.js +505 -0
  44. package/dist/interpreter/execute-action.d.ts +5 -5
  45. package/dist/interpreter/execute-action.js +284 -218
  46. package/dist/interpreter/execute-control.d.ts +9 -9
  47. package/dist/interpreter/execute-control.js +27 -27
  48. package/dist/interpreter/execute.d.ts +1 -1
  49. package/dist/interpreter/execute.js +29 -35
  50. package/dist/interpreter/step-dispatcher.d.ts +1 -1
  51. package/dist/interpreter/step-dispatcher.js +3 -3
  52. package/dist/interpreter/structured-output.d.ts +14 -0
  53. package/dist/interpreter/structured-output.js +40 -0
  54. package/dist/interpreter/typeguards.d.ts +1 -1
  55. package/dist/interpreter/typeguards.js +1 -1
  56. package/dist/portable/builders.d.ts +5 -3
  57. package/dist/portable/builders.js +8 -5
  58. package/dist/portable/context-layers.d.ts +1 -1
  59. package/dist/portable/context-layers.js +1 -1
  60. package/dist/runtime/acp-session-store.d.ts +75 -0
  61. package/dist/runtime/acp-session-store.js +0 -0
  62. package/dist/runtime/broadcaster-utils.d.ts +1 -1
  63. package/dist/runtime/broadcaster-utils.js +1 -1
  64. package/dist/runtime/context-impl.d.ts +8 -10
  65. package/dist/runtime/context-impl.js +8 -12
  66. package/dist/runtime/durable/step-ledger.d.ts +2 -2
  67. package/dist/runtime/durable/step-ledger.js +3 -3
  68. package/dist/runtime/session-runner.d.ts +12 -1
  69. package/dist/runtime/session-runner.js +33 -1
  70. package/dist/runtime/tool-context.js +0 -2
  71. package/dist/schemas/workflow.d.ts +3 -3
  72. package/dist/schemas/workflow.js +2 -2
  73. package/dist/types/checkpoint.d.ts +11 -2
  74. package/dist/types/checkpoint.js +10 -1
  75. package/dist/{runtime → util}/detached-handle.js +1 -1
  76. package/package.json +6 -4
  77. package/schema/noetic-workflow.schema.json +239 -260
  78. package/dist/builders/context-option.d.ts +0 -23
  79. package/dist/builders/context-option.js +0 -22
  80. package/dist/deprecated.d.ts +0 -30
  81. package/dist/deprecated.js +0 -28
  82. package/dist/interpreter/execute-sub-harness.d.ts +0 -8
  83. package/dist/interpreter/execute-sub-harness.js +0 -241
  84. package/dist/interpreter/sub-harness-events.d.ts +0 -48
  85. package/dist/interpreter/sub-harness-events.js +0 -208
  86. package/dist/patterns/interview.d.ts +0 -53
  87. package/dist/patterns/interview.js +0 -113
  88. package/dist/patterns/plans.d.ts +0 -44
  89. package/dist/patterns/plans.js +0 -125
  90. package/dist/patterns/ralph-wiggum.d.ts +0 -19
  91. package/dist/patterns/ralph-wiggum.js +0 -37
  92. package/dist/patterns/react.d.ts +0 -19
  93. package/dist/patterns/react.js +0 -41
  94. /package/dist/{runtime → util}/detached-handle.d.ts +0 -0
@@ -1,20 +1,20 @@
1
1
  /**
2
2
  * Converts a validated `WorkflowDocument` into a live `Step` tree.
3
3
  *
4
- * Each node kind maps to the corresponding builder (`step.llm`, `fork`,
4
+ * Each node kind maps to the corresponding builder (`callModel`, `inParallel`,
5
5
  * `loop`, etc.) so hydrated steps are indistinguishable from programmatic
6
6
  * ones — they register in the step registry, carry retry policies, etc.
7
7
  */
8
8
  import { frameworkCast, isServerToolSpec, NoeticConfigError } from '@noetic-tools/types';
9
- import { DetachedHandleImpl } from '../runtime/detached-handle.js';
10
9
  import { all, any } from '../until/combinators.js';
11
10
  import { until } from '../until/predicates.js';
12
- import { branch, fork } from './control-flow-builders.js';
13
- import { every } from './every.js';
11
+ import { DetachedHandleImpl } from '../util/detached-handle.js';
12
+ import { conditional, inParallel } from './control-flow-builders.js';
13
+ import { schedule } from './every.js';
14
14
  import { loop } from './loop-builder.js';
15
- import { provide } from './provide-builder.js';
15
+ import { withContext } from './provide-builder.js';
16
16
  import { spawn } from './spawn-builder.js';
17
- import { step } from './step-builders.js';
17
+ import { callModel, runCode, step } from './step-builders.js';
18
18
  //#endregion
19
19
  //#region Until Predicate Hydration
20
20
  function hydrateUntilPredicate(pred) {
@@ -24,9 +24,11 @@ function hydrateUntilPredicate(pred) {
24
24
  case 'maxCost':
25
25
  return until.maxCost(pred.usd);
26
26
  case 'maxDuration':
27
- return until.maxDuration(pred.ms);
27
+ return until.maxDuration(pred.duration);
28
28
  case 'noToolCalls':
29
29
  return until.noToolCalls();
30
+ case 'never':
31
+ return until.never();
30
32
  case 'outputContains':
31
33
  return until.outputContains(pred.marker);
32
34
  case 'outputEquals':
@@ -43,7 +45,7 @@ function hydrateUntilPredicate(pred) {
43
45
  throw new NoeticConfigError({
44
46
  code: 'UNKNOWN_UNTIL_PREDICATE',
45
47
  message: `Unknown until predicate kind: '${frameworkCast(pred).kind}'.`,
46
- hint: 'Supported kinds: maxSteps, maxCost, maxDuration, noToolCalls, outputContains, outputEquals, converged, any, all.',
48
+ hint: 'Supported kinds: maxSteps, maxCost, maxDuration, noToolCalls, never, outputContains, outputEquals, converged, any, all.',
47
49
  });
48
50
  }
49
51
  }
@@ -58,7 +60,7 @@ function hydrateUntilPredicate(pred) {
58
60
  * kinds combine into one `tools` array; the interpreter partitions them again
59
61
  * at execution.
60
62
  */
61
- function resolveLlmTools(entries, registry) {
63
+ function resolveCallModelTools(entries, registry) {
62
64
  const toolNames = [];
63
65
  const serverSpecs = [];
64
66
  for (const entry of entries ?? []) {
@@ -74,12 +76,12 @@ function resolveLlmTools(entries, registry) {
74
76
  ...serverSpecs,
75
77
  ];
76
78
  }
77
- function hydrateLlmNode(node, ctx) {
78
- if (node.kind !== 'llm') {
79
+ function hydrateCallModelNode(node, ctx) {
80
+ if (node.kind !== 'callModel') {
79
81
  return frameworkCast(undefined);
80
82
  }
81
- const combined = resolveLlmTools(node.tools, ctx.tools);
82
- return step.llm({
83
+ const combined = resolveCallModelTools(node.tools, ctx.tools);
84
+ return callModel({
83
85
  id: node.id,
84
86
  model: node.model ?? 'openai/gpt-4o',
85
87
  instructions: node.instructions,
@@ -89,7 +91,7 @@ function hydrateLlmNode(node, ctx) {
89
91
  });
90
92
  }
91
93
  /**
92
- * Resolve an `llm` node's `output` codec reference to a live `OutputCodec`.
94
+ * Resolve a `callModel` node's `output` codec reference to a live `OutputCodec`.
93
95
  *
94
96
  * Hydrated steps are typed `<string, string>` (the JSON boundary erases output
95
97
  * types), so the resolved codec is cast to the step's erased output type — the
@@ -118,8 +120,8 @@ function resolveOutputCodec(ref, nodeId, ctx) {
118
120
  hint: `Pass output codecs via HydrationContext.uiLibraries, e.g. new Map([['${ref.library}', openUi(myLibrary)]]) from @noetic-tools/openui. Available: ${availableLibraries(ctx)}`,
119
121
  });
120
122
  }
121
- function hydrateToolNode(node, ctx) {
122
- if (node.kind !== 'tool') {
123
+ function hydrateInvokeToolNode(node, ctx) {
124
+ if (node.kind !== 'invokeTool') {
123
125
  return frameworkCast(undefined);
124
126
  }
125
127
  const resolved = ctx.tools.get(node.toolName);
@@ -132,7 +134,7 @@ function hydrateToolNode(node, ctx) {
132
134
  ].join(', ') || '(none)'}`,
133
135
  });
134
136
  }
135
- return frameworkCast(step.run({
137
+ return frameworkCast(runCode({
136
138
  id: node.id,
137
139
  execute: async (_input, execCtx) => {
138
140
  const args = node.args ?? {};
@@ -156,12 +158,6 @@ function hydrateToolNode(node, ctx) {
156
158
  fs: execCtx.fs,
157
159
  shell: execCtx.shell,
158
160
  context: layerState,
159
- // Deprecated alias — the same accessor object, as
160
- // `buildToolExecutionContext` does. `Tool.execute` takes its second
161
- // argument as `unknown`, so nothing here is structurally checked
162
- // against `ToolExecutionContext`; the annotation above is what makes
163
- // a missing field a compile error instead of a runtime `undefined`.
164
- memory: layerState,
165
161
  assembledView: execCtx.itemLog.items,
166
162
  lastStepMeta: execCtx.lastStepMeta,
167
163
  };
@@ -170,13 +166,13 @@ function hydrateToolNode(node, ctx) {
170
166
  },
171
167
  }));
172
168
  }
173
- function hydrateRunNode(node, ctx) {
174
- if (node.kind !== 'run') {
169
+ function hydrateRunCodeNode(node, ctx) {
170
+ if (node.kind !== 'runCode') {
175
171
  return frameworkCast(undefined);
176
172
  }
177
173
  const code = node.execute;
178
174
  const subprocessRef = node.subprocess;
179
- return frameworkCast(step.run({
175
+ return frameworkCast(runCode({
180
176
  id: node.id,
181
177
  retry: node.retry,
182
178
  execute: async (input, execCtx) => {
@@ -195,8 +191,8 @@ function hydrateRunNode(node, ctx) {
195
191
  },
196
192
  }));
197
193
  }
198
- function hydrateBranchNode(node, ctx) {
199
- if (node.kind !== 'branch') {
194
+ function hydrateConditionalNode(node, ctx) {
195
+ if (node.kind !== 'conditional') {
200
196
  return frameworkCast(undefined);
201
197
  }
202
198
  const hydratedRoutes = node.routes.map((r) => ({
@@ -208,7 +204,7 @@ function hydrateBranchNode(node, ctx) {
208
204
  if (defaultTarget) {
209
205
  allTargets.push(defaultTarget);
210
206
  }
211
- return branch({
207
+ return conditional({
212
208
  id: node.id,
213
209
  route: (input) => {
214
210
  const trimmed = input.trim().toLowerCase();
@@ -222,12 +218,12 @@ function hydrateBranchNode(node, ctx) {
222
218
  _optimizable: frameworkCast(allTargets),
223
219
  });
224
220
  }
225
- function hydrateForkNode(node, ctx) {
226
- if (node.kind !== 'fork') {
221
+ function hydrateInParallelNode(node, ctx) {
222
+ if (node.kind !== 'inParallel') {
227
223
  return frameworkCast(undefined);
228
224
  }
229
- // Static fork: paths are known at hydration time and feed the optimizer.
230
- // Dynamic fork (`each`): paths are produced per fork-input at runtime, one
225
+ // Static inParallel: paths are known at hydration time and feed the optimizer.
226
+ // Dynamic inParallel (`each`): paths are produced per inParallel-input at runtime, one
231
227
  // child per array item, so they cannot be pre-computed or optimized.
232
228
  const dynamic = node.each !== undefined;
233
229
  const eachTemplate = node.each;
@@ -247,7 +243,7 @@ function hydrateForkNode(node, ctx) {
247
243
  };
248
244
  const optimizable = dynamic ? undefined : frameworkCast(staticPaths);
249
245
  if (node.mode === 'race') {
250
- return fork({
246
+ return inParallel({
251
247
  id: node.id,
252
248
  mode: 'race',
253
249
  paths: pathsFactory,
@@ -257,7 +253,7 @@ function hydrateForkNode(node, ctx) {
257
253
  }
258
254
  const mergeFn = buildMerge(node.merge ?? 'last');
259
255
  if (node.mode === 'settle') {
260
- return fork({
256
+ return inParallel({
261
257
  id: node.id,
262
258
  mode: 'settle',
263
259
  paths: pathsFactory,
@@ -269,7 +265,7 @@ function hydrateForkNode(node, ctx) {
269
265
  _optimizable: optimizable,
270
266
  });
271
267
  }
272
- return fork({
268
+ return inParallel({
273
269
  id: node.id,
274
270
  mode: 'all',
275
271
  paths: pathsFactory,
@@ -279,7 +275,7 @@ function hydrateForkNode(node, ctx) {
279
275
  });
280
276
  }
281
277
  /**
282
- * Resolves layer names declared on a `provide` / `spawn` node against the
278
+ * Resolves layer names declared on a `withContext` / `spawn` node against the
283
279
  * registry on `HydrationContext.layers`. Returns `[]` when no registry was
284
280
  * supplied — the host runs with its harness-default layers in that case.
285
281
  */
@@ -322,16 +318,16 @@ function hydrateSpawnNode(node, ctx) {
322
318
  context: resolvedLayers,
323
319
  });
324
320
  }
325
- function hydrateProvideNode(node, ctx) {
326
- if (node.kind !== 'provide') {
321
+ function hydrateWithContextNode(node, ctx) {
322
+ if (node.kind !== 'withContext') {
327
323
  return frameworkCast(undefined);
328
324
  }
329
- return provide({
325
+ return withContext({
330
326
  id: node.id,
331
327
  child: hydrateNode(node.child, ctx),
332
328
  context: resolveNamedLayers({
333
329
  names: node.layers,
334
- nodeKind: 'provide',
330
+ nodeKind: 'withContext',
335
331
  nodeId: node.id,
336
332
  ctx,
337
333
  }),
@@ -355,7 +351,7 @@ function hydrateSequenceNode(node, ctx) {
355
351
  return frameworkCast(undefined);
356
352
  }
357
353
  const children = node.steps.map((s) => hydrateNode(s, ctx));
358
- return frameworkCast(step.run({
354
+ return frameworkCast(runCode({
359
355
  id: node.id,
360
356
  execute: async (input, execCtx) => {
361
357
  let current = input;
@@ -367,44 +363,39 @@ function hydrateSequenceNode(node, ctx) {
367
363
  },
368
364
  }));
369
365
  }
370
- function hydrateEveryNode(node, ctx) {
371
- if (node.kind !== 'every') {
366
+ function hydrateScheduleNode(node, ctx) {
367
+ if (node.kind !== 'schedule') {
372
368
  return frameworkCast(undefined);
373
369
  }
374
- return every({
370
+ return schedule({
375
371
  id: node.id,
376
372
  step: hydrateNode(node.step, ctx),
377
- ms: node.ms,
373
+ interval: node.interval,
378
374
  onError: node.onError,
379
375
  });
380
376
  }
381
- const SUB_HARNESS_BUILDERS = {
382
- 'claude-code': (opts) => step.claudeCode(opts),
383
- codex: (opts) => step.codex(opts),
384
- opencode: (opts) => step.opencode(opts),
385
- pi: (opts) => step.pi(opts),
386
- };
387
- function hydrateSubHarnessNode(node, ctx) {
388
- if (node.kind !== 'claude-code' &&
389
- node.kind !== 'codex' &&
390
- node.kind !== 'opencode' &&
391
- node.kind !== 'pi') {
377
+ function hydrateAcpAgentNode(node, ctx) {
378
+ if (node.kind !== 'acp-agent') {
392
379
  return frameworkCast(undefined);
393
380
  }
394
- const harness = ctx.subHarnesses?.get(node.kind);
395
- if (!harness) {
381
+ const agent = ctx.acpAgents?.get(node.agent);
382
+ if (!agent) {
396
383
  throw new NoeticConfigError({
397
- code: 'UNKNOWN_SUB_HARNESS_REFERENCE',
398
- message: `SubHarness '${node.kind}' referenced in workflow node '${node.id}' is not registered.`,
399
- hint: `Pass harness adapters via HydrationContext.subHarnesses, e.g. new Map([['${node.kind}', ${node.kind.replace(/-([a-z])/g, (_, c) => c.toUpperCase())}({ model })]]).`,
384
+ code: 'UNKNOWN_ACP_AGENT_REFERENCE',
385
+ message: `ACP agent '${node.agent}' referenced in workflow node '${node.id}' is not registered.`,
386
+ hint: 'Pass agent adapters via HydrationContext.acpAgents, e.g. createAcpAgentRegistry(claudeCode()) from @noetic-tools/acp.',
400
387
  });
401
388
  }
402
- return SUB_HARNESS_BUILDERS[node.kind]({
389
+ return step.acpAgent({
403
390
  id: node.id,
404
- harness,
391
+ agent,
405
392
  prompt: node.prompt,
406
- instructions: node.instructions,
407
- settings: node.settings,
393
+ cwd: node.cwd,
394
+ mode: node.mode,
395
+ model: node.model,
396
+ mcpServers: node.mcpServers,
397
+ permissions: node.permissions,
398
+ clientCapabilities: node.clientCapabilities,
408
399
  session: node.session,
409
400
  });
410
401
  }
@@ -427,7 +418,7 @@ function hydrateSubflowNode(node, ctx) {
427
418
  }
428
419
  return cached;
429
420
  };
430
- return frameworkCast(step.run({
421
+ return frameworkCast(runCode({
431
422
  id: node.id,
432
423
  execute: async (input, execCtx) => {
433
424
  const child = resolve();
@@ -480,21 +471,18 @@ function resolveSubflowDocument(node, ctx) {
480
471
  //#endregion
481
472
  //#region Handler Registry
482
473
  const NODE_HYDRATORS = {
483
- llm: hydrateLlmNode,
484
- tool: hydrateToolNode,
485
- run: hydrateRunNode,
486
- branch: hydrateBranchNode,
487
- fork: hydrateForkNode,
474
+ callModel: hydrateCallModelNode,
475
+ invokeTool: hydrateInvokeToolNode,
476
+ runCode: hydrateRunCodeNode,
477
+ conditional: hydrateConditionalNode,
478
+ inParallel: hydrateInParallelNode,
488
479
  spawn: hydrateSpawnNode,
489
- provide: hydrateProvideNode,
480
+ withContext: hydrateWithContextNode,
490
481
  loop: hydrateLoopNode,
491
482
  sequence: hydrateSequenceNode,
492
- every: hydrateEveryNode,
483
+ schedule: hydrateScheduleNode,
493
484
  subflow: hydrateSubflowNode,
494
- 'claude-code': hydrateSubHarnessNode,
495
- codex: hydrateSubHarnessNode,
496
- opencode: hydrateSubHarnessNode,
497
- pi: hydrateSubHarnessNode,
485
+ 'acp-agent': hydrateAcpAgentNode,
498
486
  };
499
487
  //#endregion
500
488
  //#region Helpers
@@ -537,7 +525,7 @@ function stringifyResult(value) {
537
525
  //#endregion
538
526
  //#region Dynamic Fork Helpers
539
527
  /**
540
- * Parses the fork input (a JSON string) and locates the array to fan out over.
528
+ * Parses the inParallel input (a JSON string) and locates the array to fan out over.
541
529
  * When `over` is set, reads that property off the parsed object; otherwise the
542
530
  * parsed value itself must be an array.
543
531
  */
@@ -549,15 +537,15 @@ function selectArray(input, over, nodeId) {
549
537
  catch {
550
538
  throw new NoeticConfigError({
551
539
  code: 'INVALID_FORK_INPUT',
552
- message: `Dynamic fork '${nodeId}' could not parse its input as JSON.`,
553
- hint: 'A dynamic fork (with `each`) expects its input to be a JSON array (or a JSON object when `over` is set).',
540
+ message: `Dynamic inParallel '${nodeId}' could not parse its input as JSON.`,
541
+ hint: 'A dynamic inParallel (with `each`) expects its input to be a JSON array (or a JSON object when `over` is set).',
554
542
  });
555
543
  }
556
544
  const candidate = over === undefined ? parsed : frameworkCast(parsed)?.[over];
557
545
  if (!Array.isArray(candidate)) {
558
546
  throw new NoeticConfigError({
559
547
  code: 'INVALID_FORK_INPUT',
560
- message: `Dynamic fork '${nodeId}' did not resolve an array${over ? ` at key '${over}'` : ''}.`,
548
+ message: `Dynamic inParallel '${nodeId}' did not resolve an array${over ? ` at key '${over}'` : ''}.`,
561
549
  hint: over
562
550
  ? `Ensure the input JSON object has an array at '${over}'.`
563
551
  : 'Ensure the input is a JSON array, or set `over` to select an array property.',
@@ -566,15 +554,15 @@ function selectArray(input, over, nodeId) {
566
554
  return candidate;
567
555
  }
568
556
  /**
569
- * Builds one fork path for a single dynamic-fork item. The item is injected as
570
- * the body's input (forks pass the same fork-input to every path), and the
557
+ * Builds one inParallel path for a single dynamic fan-out item. The item is injected as
558
+ * the body's input (inParallel steps pass the same input to every path), and the
571
559
  * template's node ids are suffixed with `-${i}` so each instantiation has
572
560
  * unique ids for tracing and step-registry uniqueness.
573
561
  */
574
562
  function buildPerItemStep(opts) {
575
563
  const { forkId, eachTemplate, item, index, ctx } = opts;
576
564
  const hydratedEach = hydrateNode(suffixNodeIds(eachTemplate, `-${index}`), ctx);
577
- return frameworkCast(step.run({
565
+ return frameworkCast(runCode({
578
566
  id: `${forkId}-item-${index}`,
579
567
  execute: async (_input, execCtx) => {
580
568
  const itemInput = JSON.stringify(item);
@@ -621,7 +609,7 @@ function suffixNodeIds(node, suffix) {
621
609
  //#endregion
622
610
  //#region Run Node Helpers
623
611
  /**
624
- * Resolves the subprocess adapter for a `run` node. When the node names an
612
+ * Resolves the subprocess adapter for a `runCode` node. When the node names an
625
613
  * adapter ref, the host must supply a `resolveSubprocess` resolver; otherwise
626
614
  * the step falls back to the harness adapter on the execution context.
627
615
  */
@@ -641,7 +629,7 @@ function resolveSubprocessAdapter(opts) {
641
629
  return resolved;
642
630
  }
643
631
  /**
644
- * Dispatches a `run` node's code string through a subprocess adapter: ships the
632
+ * Dispatches a `runCode` node's code string through a subprocess adapter: ships the
645
633
  * code plus the JSON-stringified input as a process request (input on stdin),
646
634
  * waits for the handle to settle, and returns the captured stdout as the step
647
635
  * output. A non-zero exit / failed handle surfaces as a thrown error.
@@ -1,31 +1,30 @@
1
1
  /**
2
- * `step.workflow` — runs a `WorkflowDocument` as a single composable step.
2
+ * `workflow` — runs a `WorkflowDocument` as a single composable step.
3
3
  *
4
4
  * Lives beside `step-builders.ts` (rather than inside it) because the hydrator
5
- * imports the base `step` object; adding a hydrating builder there would create
6
- * an import cycle. This module composes the public `step` namespace instead.
5
+ * imports the base builders; adding a hydrating builder there would create an
6
+ * import cycle.
7
7
  */
8
8
  import type { ContextData, ContextLayer } from '@noetic-tools/context';
9
- import type { OutputCodec, StepRun, SubHarness, SubHarnessKind, SubprocessAdapter, Tool, WorkflowDocument } from '@noetic-tools/types';
10
- import * as stepBuilders from './step-builders.js';
11
- /** @public Options for `step.workflow`. */
12
- export interface StepWorkflowOpts {
9
+ import type { AcpAgent, OutputCodec, StepRunCode, SubprocessAdapter, Tool, WorkflowDocument } from '@noetic-tools/types';
10
+ /** @public Options for `workflow`. */
11
+ export interface WorkflowOpts {
13
12
  id: string;
14
13
  /** Inline workflow document. Mutually exclusive with `ref`. */
15
14
  document?: WorkflowDocument;
16
15
  /** Named workflow resolved from `workflows`. Mutually exclusive with `document`. */
17
16
  ref?: string;
18
- /** Client tools the document's `llm`/`tool` nodes may reference by name. Default: none. */
17
+ /** Client tools the document's `callModel`/`invokeTool` nodes may reference by name. Default: none. */
19
18
  tools?: Tool[];
20
- /** Named context layers for `provide`/`spawn` nodes. */
19
+ /** Named context layers for `withContext`/`spawn` nodes. */
21
20
  layers?: ReadonlyMap<string, ContextLayer>;
22
21
  /** Named sub-workflows for `subflow` nodes — and for resolving `ref` itself. */
23
22
  workflows?: ReadonlyMap<string, WorkflowDocument>;
24
- /** SubHarness adapters for `claude-code`/`codex`/`opencode`/`pi` nodes. */
25
- subHarnesses?: ReadonlyMap<SubHarnessKind, SubHarness>;
26
- /** Output codecs for `llm` nodes' `output` codec references. */
23
+ /** ACP agent adapters, keyed by `agentId`, for `acp-agent` nodes. */
24
+ acpAgents?: ReadonlyMap<string, AcpAgent>;
25
+ /** Output codecs for `callModel` nodes' `output` codec references. */
27
26
  uiLibraries?: ReadonlyMap<string, OutputCodec>;
28
- /** Resolver for named subprocess adapters on `run` nodes. */
27
+ /** Resolver for named subprocess adapters on `runCode` nodes. */
29
28
  resolveSubprocess?: (ref: string) => SubprocessAdapter | undefined;
30
29
  /** `'inherit'` (default) runs in the caller's session; `'spawn'` isolates via `spawn()`. */
31
30
  isolation?: 'inherit' | 'spawn';
@@ -40,22 +39,11 @@ export interface StepWorkflowOpts {
40
39
  * workflow fails with `WORKFLOW_CYCLE` instead of recursing forever.
41
40
  *
42
41
  * @public
43
- * @param opts - See `StepWorkflowOpts`.
44
- * @returns A `StepRun` executing the workflow via the harness on the context.
42
+ * @param opts - See `WorkflowOpts`.
43
+ * @returns A `StepRunCode` executing the workflow via the harness on the context.
45
44
  * @throws `NoeticConfigError` with code `EMPTY_STEP_ID` if `id` is empty.
46
45
  * @throws `NoeticConfigError` with code `INVALID_WORKFLOW_SOURCE` unless exactly one of `document`/`ref` is set.
47
46
  * @throws `NoeticConfigError` with code `MISSING_HARNESS_CONTEXT` (at execution) without `ctx.harness`.
48
47
  * @throws `NoeticConfigError` with code `UNKNOWN_WORKFLOW_REFERENCE` (at execution) if `ref` names no registered workflow.
49
48
  */
50
- export declare function stepWorkflow(opts: StepWorkflowOpts): StepRun<ContextData, string, string>;
51
- /** @public The step builder namespace: the base builders plus `workflow`. */
52
- export declare const step: {
53
- readonly run: <TContext = Readonly<Record<string, Record<string, unknown>>>, I = unknown, O = unknown>(opts: stepBuilders.StepRunOpts<TContext, I, O>) => StepRun<TContext, I, O>;
54
- readonly llm: <TContext = Readonly<Record<string, Record<string, unknown>>>, I = unknown, O = unknown>(opts: stepBuilders.StepLLMOpts<TContext, O>) => import("@noetic-tools/types").StepLLM<TContext, I, O>;
55
- readonly tool: <TContext = Readonly<Record<string, Record<string, unknown>>>, I = unknown, O = unknown>(opts: stepBuilders.StepToolOpts<I, O>) => import("@noetic-tools/types").StepTool<TContext, I, O>;
56
- readonly claudeCode: <TContext = Readonly<Record<string, Record<string, unknown>>>, I = unknown, O = unknown>(opts: stepBuilders.StepSubHarnessOpts<TContext, O>) => import("@noetic-tools/types").StepSubHarness<TContext, I, O>;
57
- readonly codex: <TContext = Readonly<Record<string, Record<string, unknown>>>, I = unknown, O = unknown>(opts: stepBuilders.StepSubHarnessOpts<TContext, O>) => import("@noetic-tools/types").StepSubHarness<TContext, I, O>;
58
- readonly opencode: <TContext = Readonly<Record<string, Record<string, unknown>>>, I = unknown, O = unknown>(opts: stepBuilders.StepSubHarnessOpts<TContext, O>) => import("@noetic-tools/types").StepSubHarness<TContext, I, O>;
59
- readonly pi: <TContext = Readonly<Record<string, Record<string, unknown>>>, I = unknown, O = unknown>(opts: stepBuilders.StepSubHarnessOpts<TContext, O>) => import("@noetic-tools/types").StepSubHarness<TContext, I, O>;
60
- readonly workflow: typeof stepWorkflow;
61
- };
49
+ export declare function workflow(opts: WorkflowOpts): StepRunCode<ContextData, string, string>;
@@ -1,13 +1,13 @@
1
1
  /**
2
- * `step.workflow` — runs a `WorkflowDocument` as a single composable step.
2
+ * `workflow` — runs a `WorkflowDocument` as a single composable step.
3
3
  *
4
4
  * Lives beside `step-builders.ts` (rather than inside it) because the hydrator
5
- * imports the base `step` object; adding a hydrating builder there would create
6
- * an import cycle. This module composes the public `step` namespace instead.
5
+ * imports the base builders; adding a hydrating builder there would create an
6
+ * import cycle.
7
7
  */
8
8
  import { frameworkCast, NoeticConfigError } from '@noetic-tools/types';
9
9
  import { spawn } from './spawn-builder.js';
10
- import * as stepBuilders from './step-builders.js';
10
+ import { runCode } from './step-builders.js';
11
11
  import { hydrateWorkflow } from './workflow-hydrator.js';
12
12
  //#endregion
13
13
  //#region Builder
@@ -21,25 +21,25 @@ import { hydrateWorkflow } from './workflow-hydrator.js';
21
21
  * workflow fails with `WORKFLOW_CYCLE` instead of recursing forever.
22
22
  *
23
23
  * @public
24
- * @param opts - See `StepWorkflowOpts`.
25
- * @returns A `StepRun` executing the workflow via the harness on the context.
24
+ * @param opts - See `WorkflowOpts`.
25
+ * @returns A `StepRunCode` executing the workflow via the harness on the context.
26
26
  * @throws `NoeticConfigError` with code `EMPTY_STEP_ID` if `id` is empty.
27
27
  * @throws `NoeticConfigError` with code `INVALID_WORKFLOW_SOURCE` unless exactly one of `document`/`ref` is set.
28
28
  * @throws `NoeticConfigError` with code `MISSING_HARNESS_CONTEXT` (at execution) without `ctx.harness`.
29
29
  * @throws `NoeticConfigError` with code `UNKNOWN_WORKFLOW_REFERENCE` (at execution) if `ref` names no registered workflow.
30
30
  */
31
- export function stepWorkflow(opts) {
31
+ export function workflow(opts) {
32
32
  if (!opts.id || opts.id.trim() === '') {
33
33
  throw new NoeticConfigError({
34
34
  code: 'EMPTY_STEP_ID',
35
- message: 'step.workflow() requires a non-empty id.',
36
- hint: 'Pass a unique string as the id field, e.g. step.workflow({ id: "verify", ... }).',
35
+ message: 'workflow() requires a non-empty id.',
36
+ hint: 'Pass a unique string as the id field, e.g. workflow({ id: "verify", ... }).',
37
37
  });
38
38
  }
39
39
  if ((opts.document === undefined) === (opts.ref === undefined)) {
40
40
  throw new NoeticConfigError({
41
41
  code: 'INVALID_WORKFLOW_SOURCE',
42
- message: `step.workflow('${opts.id}') requires exactly one of 'document' (inline) or 'ref' (named).`,
42
+ message: `workflow('${opts.id}') requires exactly one of 'document' (inline) or 'ref' (named).`,
43
43
  hint: "Pass document: { version: 1, root: ... } or ref: '<name>' with a workflows registry.",
44
44
  });
45
45
  }
@@ -48,14 +48,14 @@ export function stepWorkflow(opts) {
48
48
  // across sessions re-hydrates when it runs under a different harness
49
49
  // instead of routing nested steps through the first one.
50
50
  let cached;
51
- return stepBuilders.step.run({
51
+ return runCode({
52
52
  id: opts.id,
53
53
  execute: async (input, ctx) => {
54
54
  const harness = ctx.harness;
55
55
  if (!harness) {
56
56
  throw new NoeticConfigError({
57
57
  code: 'MISSING_HARNESS_CONTEXT',
58
- message: 'step.workflow requires a harness on the execution context.',
58
+ message: 'workflow requires a harness on the execution context.',
59
59
  hint: 'Execute this step via AgentHarness.run() or ensure ctx.harness is set.',
60
60
  });
61
61
  }
@@ -70,11 +70,6 @@ export function stepWorkflow(opts) {
70
70
  },
71
71
  });
72
72
  }
73
- /** @public The step builder namespace: the base builders plus `workflow`. */
74
- export const step = {
75
- ...stepBuilders.step,
76
- workflow: stepWorkflow,
77
- };
78
73
  //#endregion
79
74
  //#region Helpers
80
75
  function hydrate(opts, executeStep) {
@@ -82,7 +77,7 @@ function hydrate(opts, executeStep) {
82
77
  if (!doc) {
83
78
  throw new NoeticConfigError({
84
79
  code: 'UNKNOWN_WORKFLOW_REFERENCE',
85
- message: `Workflow '${opts.ref}' referenced by step.workflow('${opts.id}') is not registered.`,
80
+ message: `Workflow '${opts.ref}' referenced by workflow('${opts.id}') is not registered.`,
86
81
  hint: `Pass named workflows via the workflows option. Available: ${[
87
82
  ...(opts.workflows?.keys() ?? []),
88
83
  ].join(', ') || '(none)'}.`,
@@ -96,7 +91,7 @@ function hydrate(opts, executeStep) {
96
91
  executeStep,
97
92
  layers: opts.layers,
98
93
  workflows: opts.workflows,
99
- subHarnesses: opts.subHarnesses,
94
+ acpAgents: opts.acpAgents,
100
95
  uiLibraries: opts.uiLibraries,
101
96
  resolveSubprocess: opts.resolveSubprocess,
102
97
  subflowAncestry: opts.ref
@@ -35,7 +35,7 @@ export declare function otherwise<TContext = ContextData, I = unknown, O = unkno
35
35
  *
36
36
  * @public
37
37
  * @param clauses - Ordered `WhenClause` and optional trailing `OtherwiseClause`.
38
- * @returns A route function suitable for `branch({ route })`.
38
+ * @returns A route function suitable for `conditional({ route })`.
39
39
  */
40
40
  export declare function semanticRoute<TContext = ContextData, I = unknown, O = unknown>(...clauses: Clause<TContext, I, O>[]): (input: I, ctx: Context) => Promise<Step<TContext, I, O> | null>;
41
41
  interface SemanticSwitchSimple<TContext, I, O> {
@@ -60,7 +60,7 @@ interface SemanticSwitchAdvanced<TContext, I, O> {
60
60
  *
61
61
  * @public
62
62
  * @param opts - Simple form with `Record<string, Step>` cases, or advanced form with multi-label cases.
63
- * @returns A route function suitable for `branch({ route })`.
63
+ * @returns A route function suitable for `conditional({ route })`.
64
64
  */
65
65
  export declare function semanticSwitch<TContext = ContextData, I = unknown, O = unknown>(opts: SemanticSwitchSimple<TContext, I, O>): (input: I, ctx: Context) => Promise<Step<TContext, I, O> | null>;
66
66
  /** @public */
@@ -85,7 +85,7 @@ function isOtherwise(clause) {
85
85
  *
86
86
  * @public
87
87
  * @param clauses - Ordered `WhenClause` and optional trailing `OtherwiseClause`.
88
- * @returns A route function suitable for `branch({ route })`.
88
+ * @returns A route function suitable for `conditional({ route })`.
89
89
  */
90
90
  export function semanticRoute(...clauses) {
91
91
  return async (input, ctx) => {