@noetic-tools/core 5.3.0 → 5.4.0
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.
- package/README.md +8 -6
- package/dist/adapters/in-memory-subprocess/step-completion.js +1 -1
- package/dist/adapters/openrouter.d.ts +12 -1
- package/dist/adapters/openrouter.js +79 -5
- package/dist/builders/control-flow-builders.d.ts +13 -13
- package/dist/builders/control-flow-builders.js +11 -11
- package/dist/{patterns → builders}/dynamic-workflow.d.ts +1 -1
- package/dist/{patterns → builders}/dynamic-workflow.js +18 -12
- package/dist/builders/every.d.ts +14 -14
- package/dist/builders/every.js +16 -16
- package/dist/builders/layer-provides-builders.d.ts +1 -1
- package/dist/builders/layer-provides-builders.js +1 -1
- package/dist/builders/provide-builder.d.ts +5 -12
- package/dist/builders/provide-builder.js +9 -10
- package/dist/builders/spawn-builder.d.ts +0 -2
- package/dist/builders/spawn-builder.js +2 -3
- package/dist/builders/step-builders.d.ts +66 -55
- package/dist/builders/step-builders.js +118 -111
- package/dist/builders/tool-builder.d.ts +18 -22
- package/dist/builders/tool-builder.js +6 -4
- package/dist/builders/workflow-hydrator.d.ts +3 -3
- package/dist/builders/workflow-hydrator.js +59 -63
- package/dist/builders/workflow-step.d.ts +13 -25
- package/dist/builders/workflow-step.js +13 -18
- package/dist/conditions/conditions.d.ts +2 -2
- package/dist/conditions/conditions.js +1 -1
- package/dist/harness/agent-harness.d.ts +54 -40
- package/dist/harness/agent-harness.js +44 -33
- package/dist/harness/deps/types.d.ts +1 -1
- package/dist/harness/model-call.d.ts +1 -1
- package/dist/harness/model-call.js +23 -18
- package/dist/index.d.ts +21 -33
- package/dist/index.js +10 -26
- package/dist/interpreter/action-types.d.ts +1 -1
- package/dist/interpreter/collect-tools.js +7 -7
- package/dist/interpreter/execute-action.d.ts +5 -5
- package/dist/interpreter/execute-action.js +284 -218
- package/dist/interpreter/execute-control.d.ts +9 -9
- package/dist/interpreter/execute-control.js +27 -27
- package/dist/interpreter/execute-sub-harness.d.ts +1 -1
- package/dist/interpreter/execute-sub-harness.js +7 -26
- package/dist/interpreter/execute.d.ts +1 -1
- package/dist/interpreter/execute.js +26 -25
- package/dist/interpreter/step-dispatcher.d.ts +1 -1
- package/dist/interpreter/step-dispatcher.js +3 -3
- package/dist/interpreter/structured-output.d.ts +14 -0
- package/dist/interpreter/structured-output.js +40 -0
- package/dist/interpreter/typeguards.d.ts +1 -1
- package/dist/interpreter/typeguards.js +1 -1
- package/dist/portable/builders.d.ts +3 -3
- package/dist/portable/builders.js +6 -5
- package/dist/portable/context-layers.d.ts +1 -1
- package/dist/portable/context-layers.js +1 -1
- package/dist/runtime/broadcaster-utils.d.ts +1 -1
- package/dist/runtime/broadcaster-utils.js +1 -1
- package/dist/runtime/context-impl.d.ts +8 -10
- package/dist/runtime/context-impl.js +8 -12
- package/dist/runtime/durable/step-ledger.d.ts +2 -2
- package/dist/runtime/durable/step-ledger.js +3 -3
- package/dist/runtime/session-runner.d.ts +12 -1
- package/dist/runtime/session-runner.js +33 -1
- package/dist/runtime/tool-context.js +0 -2
- package/dist/schemas/workflow.d.ts +3 -3
- package/dist/schemas/workflow.js +2 -2
- package/dist/types/checkpoint.d.ts +11 -2
- package/dist/types/checkpoint.js +10 -1
- package/dist/{runtime → util}/detached-handle.js +1 -1
- package/package.json +6 -4
- package/schema/noetic-workflow.schema.json +24 -11
- package/dist/builders/context-option.d.ts +0 -23
- package/dist/builders/context-option.js +0 -22
- package/dist/deprecated.d.ts +0 -30
- package/dist/deprecated.js +0 -28
- package/dist/patterns/interview.d.ts +0 -53
- package/dist/patterns/interview.js +0 -113
- package/dist/patterns/plans.d.ts +0 -44
- package/dist/patterns/plans.js +0 -125
- package/dist/patterns/ralph-wiggum.d.ts +0 -19
- package/dist/patterns/ralph-wiggum.js +0 -37
- package/dist/patterns/react.d.ts +0 -19
- package/dist/patterns/react.js +0 -41
- /package/dist/{runtime → util}/detached-handle.d.ts +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Control-flow step handlers:
|
|
2
|
+
* Control-flow step handlers: conditional, inParallel, loop, schedule.
|
|
3
3
|
*/
|
|
4
4
|
import { createMessage, frameworkCast, isNoeticError, NoeticErrorImpl } from '@noetic-tools/types';
|
|
5
5
|
import { ContextImpl } from '../runtime/context-impl.js';
|
|
@@ -7,18 +7,18 @@ import { snapshotCwdState } from '../runtime/cwd-helpers.js';
|
|
|
7
7
|
import { contextToExecCtx, returnLayers, spawnLayers } from './action-deps.js';
|
|
8
8
|
import { cloneWithGuard } from './clone-guard.js';
|
|
9
9
|
import { getContextChannelStore, isContextImpl, isMutableContext } from './typeguards.js';
|
|
10
|
-
//#region
|
|
11
|
-
export async function
|
|
10
|
+
//#region conditional
|
|
11
|
+
export async function executeConditional(step, input, ctx, executeStep) {
|
|
12
12
|
const selected = await step.route(input, ctx);
|
|
13
13
|
if (selected === null) {
|
|
14
|
-
// Requires I assignable to O for null route — when no
|
|
14
|
+
// Requires I assignable to O for null route — when no route is selected,
|
|
15
15
|
// the input passes through. Callers must ensure I is compatible with O.
|
|
16
16
|
return frameworkCast(input);
|
|
17
17
|
}
|
|
18
18
|
return executeStep(selected, input, ctx);
|
|
19
19
|
}
|
|
20
20
|
//#endregion
|
|
21
|
-
//#region
|
|
21
|
+
//#region inParallel
|
|
22
22
|
function createChildContexts(ctx, count, stepId) {
|
|
23
23
|
const threadId = isContextImpl(ctx) ? ctx.threadId : crypto.randomUUID();
|
|
24
24
|
const resourceId = isContextImpl(ctx) ? ctx.resourceId : undefined;
|
|
@@ -43,8 +43,8 @@ function createChildContexts(ctx, count, stepId) {
|
|
|
43
43
|
resourceId,
|
|
44
44
|
channelStore,
|
|
45
45
|
cwdState: snapshotCwdState(ctx),
|
|
46
|
-
// Layers and the harness tool pool cross the
|
|
47
|
-
// them
|
|
46
|
+
// Layers and the harness tool pool cross the inParallel boundary: without
|
|
47
|
+
// them a `callModel` step inside a path would run with no context
|
|
48
48
|
// projection and no layer tools, and a nested `spawn` would have no
|
|
49
49
|
// parent layers to inherit. Per-path layer STATE is still isolated —
|
|
50
50
|
// see `createForkLayerBridge`.
|
|
@@ -93,7 +93,7 @@ function createForkLayerBridge({ ctx, childContexts, opts, }) {
|
|
|
93
93
|
}
|
|
94
94
|
/**
|
|
95
95
|
* Wraps the interpreter's `executeStep` so every forked path runs inside the
|
|
96
|
-
* layer child boundary. Contexts that are not one of this
|
|
96
|
+
* layer child boundary. Contexts that are not one of this inParallel's children
|
|
97
97
|
* (nested steps within a path) pass straight through.
|
|
98
98
|
*/
|
|
99
99
|
function withForkLayers(executeStep, childContexts, bridge) {
|
|
@@ -119,7 +119,7 @@ function withForkLayers(executeStep, childContexts, bridge) {
|
|
|
119
119
|
}
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
export async function
|
|
122
|
+
export async function executeInParallel(step, input, ctx, rawExecuteStep, opts) {
|
|
123
123
|
const baseCtx = frameworkCast(ctx);
|
|
124
124
|
const paths = step.paths(input, ctx);
|
|
125
125
|
if (paths.length === 0) {
|
|
@@ -156,7 +156,7 @@ export async function executeFork(step, input, ctx, rawExecuteStep, opts) {
|
|
|
156
156
|
throw new NoeticErrorImpl({
|
|
157
157
|
kind: 'step_failed',
|
|
158
158
|
stepId: 'unknown',
|
|
159
|
-
cause: new Error('Unknown
|
|
159
|
+
cause: new Error('Unknown inParallel mode'),
|
|
160
160
|
retriesExhausted: false,
|
|
161
161
|
});
|
|
162
162
|
}
|
|
@@ -242,7 +242,7 @@ async function executeAll(step, paths, _input, ctx, childContexts, executeStep,
|
|
|
242
242
|
if (firstFailureSeen) {
|
|
243
243
|
throw new NoeticErrorImpl({
|
|
244
244
|
kind: 'cancelled',
|
|
245
|
-
reason: `
|
|
245
|
+
reason: `inParallel '${step.id}' sibling failed`,
|
|
246
246
|
});
|
|
247
247
|
}
|
|
248
248
|
try {
|
|
@@ -254,7 +254,7 @@ async function executeAll(step, paths, _input, ctx, childContexts, executeStep,
|
|
|
254
254
|
firstFailureSeen = true;
|
|
255
255
|
for (let j = 0; j < childContexts.length; j++) {
|
|
256
256
|
if (j !== i) {
|
|
257
|
-
childContexts[j].abort(`
|
|
257
|
+
childContexts[j].abort(`inParallel '${step.id}' sibling failed`);
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
}
|
|
@@ -263,7 +263,7 @@ async function executeAll(step, paths, _input, ctx, childContexts, executeStep,
|
|
|
263
263
|
});
|
|
264
264
|
const settled = await runWithConcurrency(tasks, concurrency);
|
|
265
265
|
const { succeeded, failed } = classifyResults(settled, paths);
|
|
266
|
-
// If the PARENT context was aborted mid-
|
|
266
|
+
// If the PARENT context was aborted mid-inParallel, the whole inParallel is cancelled —
|
|
267
267
|
// surface 'cancelled', not a partial-failure shape (spec 09, item 3).
|
|
268
268
|
if (ctx.aborted) {
|
|
269
269
|
throw new NoeticErrorImpl({
|
|
@@ -344,7 +344,7 @@ async function executeRace(step, paths, input, ctx, childContexts, executeStep,
|
|
|
344
344
|
async function executeSettle(step, paths, input, ctx, childContexts, executeStep, concurrency) {
|
|
345
345
|
const tasks = paths.map((path, i) => () => executeStep(path, input, frameworkCast(childContexts[i])));
|
|
346
346
|
const settled = await runWithConcurrency(tasks, concurrency);
|
|
347
|
-
// Parent aborted mid-
|
|
347
|
+
// Parent aborted mid-inParallel: the inParallel is cancelled, merge never runs
|
|
348
348
|
// (spec 09, Cancellation item 3 — same rule as mode 'all').
|
|
349
349
|
if (ctx.aborted) {
|
|
350
350
|
throw new NoeticErrorImpl({
|
|
@@ -454,7 +454,7 @@ export async function executeLoop(step, input, ctx, executeStep) {
|
|
|
454
454
|
}
|
|
455
455
|
// Cancellation is not a retriable error (spec 09): it must terminate
|
|
456
456
|
// the loop promptly rather than be consumed by 'retry'/'skip'. Same
|
|
457
|
-
// guard as
|
|
457
|
+
// guard as executeSchedule's 'continue' policy below.
|
|
458
458
|
if (e.noeticError.kind === 'cancelled') {
|
|
459
459
|
throw e;
|
|
460
460
|
}
|
|
@@ -554,7 +554,7 @@ function nextParkMs(ms, jitter) {
|
|
|
554
554
|
return value;
|
|
555
555
|
}
|
|
556
556
|
/**
|
|
557
|
-
* Parks for `ms` ms, returning early if `
|
|
557
|
+
* Parks for `ms` ms, returning early if `inbox` receives a message or the
|
|
558
558
|
* context is aborted. Always resolves; never throws (cancellation surfaces on
|
|
559
559
|
* the next iteration's abort check).
|
|
560
560
|
*/
|
|
@@ -591,7 +591,7 @@ function park(parkCtx) {
|
|
|
591
591
|
settle();
|
|
592
592
|
}
|
|
593
593
|
}, 5);
|
|
594
|
-
if (!parkCtx.
|
|
594
|
+
if (!parkCtx.inbox) {
|
|
595
595
|
return;
|
|
596
596
|
}
|
|
597
597
|
const { channelStore } = parkCtx;
|
|
@@ -599,13 +599,13 @@ function park(parkCtx) {
|
|
|
599
599
|
// Non-consuming subscription so the body still observes the message that
|
|
600
600
|
// woke us — `recv()` would dequeue queue-mode messages and leave the
|
|
601
601
|
// body's drain loop empty.
|
|
602
|
-
wakeUnsub = channelStore.subscribeWake(parkCtx.
|
|
602
|
+
wakeUnsub = channelStore.subscribeWake(parkCtx.inbox, settle);
|
|
603
603
|
return;
|
|
604
604
|
}
|
|
605
605
|
// Contexts without a ContextImpl/channel store have no body draining the
|
|
606
606
|
// channel anyway; the destructive `recv` is acceptable in that edge case.
|
|
607
607
|
parkCtx.ctx
|
|
608
|
-
.recv(parkCtx.
|
|
608
|
+
.recv(parkCtx.inbox, {
|
|
609
609
|
timeout: Math.max(duration, 1) + 1e2,
|
|
610
610
|
})
|
|
611
611
|
.then(settle)
|
|
@@ -617,7 +617,7 @@ function park(parkCtx) {
|
|
|
617
617
|
function recordIterationError(ctx, error) {
|
|
618
618
|
const message = error instanceof Error ? error.message : String(error);
|
|
619
619
|
const stack = error instanceof Error && error.stack ? error.stack : '';
|
|
620
|
-
ctx.span.addEvent('
|
|
620
|
+
ctx.span.addEvent('schedule.iteration.error', {
|
|
621
621
|
message,
|
|
622
622
|
stack,
|
|
623
623
|
});
|
|
@@ -629,18 +629,18 @@ function throwCancelled(reason) {
|
|
|
629
629
|
});
|
|
630
630
|
}
|
|
631
631
|
/**
|
|
632
|
-
* Executes
|
|
633
|
-
* woken early by `
|
|
632
|
+
* Executes a `schedule` step: runs the body step forever, paced by `ms ± jitter`,
|
|
633
|
+
* woken early by `inbox`. Throws `cancelled` when the context is aborted.
|
|
634
634
|
*
|
|
635
635
|
* On body throw, behavior depends on `onError`:
|
|
636
|
-
* - `'continue'` (default): emit `
|
|
636
|
+
* - `'continue'` (default): emit `schedule.iteration.error` span event and proceed
|
|
637
637
|
* to the park step as if no error occurred.
|
|
638
638
|
* - `'fail'`: re-throw, terminating the operator.
|
|
639
639
|
*
|
|
640
|
-
* The body's output is discarded — `
|
|
640
|
+
* The body's output is discarded — `schedule` runs forever and does not accumulate
|
|
641
641
|
* iteration outputs. Only ever returns by throwing on cancellation or `fail`.
|
|
642
642
|
*/
|
|
643
|
-
export async function
|
|
643
|
+
export async function executeSchedule(step, input, ctx, executeStep) {
|
|
644
644
|
const onError = step.onError ?? 'continue';
|
|
645
645
|
const jitter = step.jitter ?? 0;
|
|
646
646
|
// Resolve once — the channel store reference is stable for the lifetime
|
|
@@ -668,9 +668,9 @@ export async function executeEvery(step, input, ctx, executeStep) {
|
|
|
668
668
|
throwCancelled(ctx.abortReason);
|
|
669
669
|
}
|
|
670
670
|
await park({
|
|
671
|
-
ms: step.
|
|
671
|
+
ms: step.interval,
|
|
672
672
|
jitter,
|
|
673
|
-
|
|
673
|
+
inbox: step.inbox,
|
|
674
674
|
ctx,
|
|
675
675
|
channelStore,
|
|
676
676
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* SubHarness step handler: drives an external coding-agent harness
|
|
3
3
|
* (Claude Code, Codex, opencode, pi) for one agentic turn and folds its output
|
|
4
4
|
* back into the Noetic execution context — the harness analogue of
|
|
5
|
-
* `
|
|
5
|
+
* `executeCallModel`.
|
|
6
6
|
*/
|
|
7
7
|
import type { Context, StepSubHarness } from '@noetic-tools/types';
|
|
8
8
|
export declare function executeSubHarness<TContext, I, O>(step: StepSubHarness<TContext, I, O>, input: I, ctx: Context<TContext>): Promise<O>;
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* SubHarness step handler: drives an external coding-agent harness
|
|
3
3
|
* (Claude Code, Codex, opencode, pi) for one agentic turn and folds its output
|
|
4
4
|
* back into the Noetic execution context — the harness analogue of
|
|
5
|
-
* `
|
|
5
|
+
* `executeCallModel`.
|
|
6
6
|
*/
|
|
7
7
|
import { createMessage, extractAssistantText, frameworkCast, NoeticConfigError, NoeticErrorImpl, } from '@noetic-tools/types';
|
|
8
|
-
import { ZodError } from 'zod';
|
|
9
8
|
import { resolveLazy } from './execute-action.js';
|
|
10
9
|
import { trackUsage } from './message-helpers.js';
|
|
10
|
+
import { parseStructuredOutput } from './structured-output.js';
|
|
11
11
|
import { SubHarnessEventBridge } from './sub-harness-events.js';
|
|
12
12
|
import { isContextImpl, isFunctionCall, isMutableContext } from './typeguards.js';
|
|
13
13
|
//#endregion
|
|
@@ -211,30 +211,11 @@ export async function executeSubHarness(step, input, ctx) {
|
|
|
211
211
|
await finalizeSession(resolution, step.session, baseCtx);
|
|
212
212
|
const lastText = result.text.length > 0 ? result.text : extractAssistantText(result.items);
|
|
213
213
|
if (step.output) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (e instanceof SyntaxError || e instanceof ZodError) {
|
|
220
|
-
throw new NoeticErrorImpl({
|
|
221
|
-
kind: 'llm_parse_error',
|
|
222
|
-
stepId: step.id,
|
|
223
|
-
raw: lastText,
|
|
224
|
-
schema: step.output,
|
|
225
|
-
zodError: e instanceof ZodError
|
|
226
|
-
? e
|
|
227
|
-
: new ZodError([
|
|
228
|
-
{
|
|
229
|
-
code: 'custom',
|
|
230
|
-
message: `Invalid JSON: ${e.message}`,
|
|
231
|
-
path: [],
|
|
232
|
-
},
|
|
233
|
-
]),
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
throw e;
|
|
237
|
-
}
|
|
214
|
+
return parseStructuredOutput({
|
|
215
|
+
schema: step.output,
|
|
216
|
+
rawText: lastText,
|
|
217
|
+
stepId: step.id,
|
|
218
|
+
});
|
|
238
219
|
}
|
|
239
220
|
return frameworkCast(lastText);
|
|
240
221
|
}
|
|
@@ -4,7 +4,7 @@ import type { Context, Step } from '@noetic-tools/types';
|
|
|
4
4
|
* @internal
|
|
5
5
|
* Dispatch a step at the per-kind handler level without adapter routing.
|
|
6
6
|
*
|
|
7
|
-
* Used as the top-level `_localExecutor` for `
|
|
7
|
+
* Used as the top-level `_localExecutor` for `StepRunCode` / `StepSpawn`
|
|
8
8
|
* dispatches: the outer adapter call already recorded the request, so
|
|
9
9
|
* running the handler directly avoids a re-entrant adapter round-trip
|
|
10
10
|
* inside the same logical step. Nested step dispatches (e.g. `executeSpawn`
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { frameworkCast, NoeticErrorImpl } from '@noetic-tools/types';
|
|
2
2
|
import { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils.js';
|
|
3
3
|
import { ContextImpl } from '../runtime/context-impl.js';
|
|
4
|
-
import { DetachedHandleImpl } from '../
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { DetachedHandleImpl } from '../util/detached-handle.js';
|
|
5
|
+
import { executeCallModel, executeInvokeTool, executeRunCode, executeSpawn, executeWithContext, } from './execute-action.js';
|
|
6
|
+
import { executeConditional, executeInParallel, executeLoop, executeSchedule, } from './execute-control.js';
|
|
7
7
|
import { executeSubHarness } from './execute-sub-harness.js';
|
|
8
8
|
import { isMutableContext } from './typeguards.js';
|
|
9
9
|
//#region Constants
|
|
@@ -31,11 +31,11 @@ function buildSpawnOpts(ctx) {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Resolve the per-step framework-event emit option. Both `
|
|
34
|
+
* Resolve the per-step framework-event emit option. Both `callModel` and the harness
|
|
35
35
|
* step kinds carry an `emit` field; every other kind defaults to enabled.
|
|
36
36
|
*/
|
|
37
37
|
function resolveStepEmit(step) {
|
|
38
|
-
if (step.kind === '
|
|
38
|
+
if (step.kind === 'callModel' ||
|
|
39
39
|
step.kind === 'claude-code' ||
|
|
40
40
|
step.kind === 'codex' ||
|
|
41
41
|
step.kind === 'opencode' ||
|
|
@@ -50,7 +50,7 @@ function resolveStepEmit(step) {
|
|
|
50
50
|
* `detachedSpawn` overrides layer above this in the harness itself.
|
|
51
51
|
*/
|
|
52
52
|
function resolveStepAdapter(step, ctx) {
|
|
53
|
-
if ((step.kind === '
|
|
53
|
+
if ((step.kind === 'runCode' || step.kind === 'spawn') && step.subprocess) {
|
|
54
54
|
return step.subprocess;
|
|
55
55
|
}
|
|
56
56
|
return ctx.harness.subprocess;
|
|
@@ -90,7 +90,7 @@ async function dispatchViaAdapter(step, input, ctx, executor) {
|
|
|
90
90
|
* @internal
|
|
91
91
|
* Dispatch a step at the per-kind handler level without adapter routing.
|
|
92
92
|
*
|
|
93
|
-
* Used as the top-level `_localExecutor` for `
|
|
93
|
+
* Used as the top-level `_localExecutor` for `StepRunCode` / `StepSpawn`
|
|
94
94
|
* dispatches: the outer adapter call already recorded the request, so
|
|
95
95
|
* running the handler directly avoids a re-entrant adapter round-trip
|
|
96
96
|
* inside the same logical step. Nested step dispatches (e.g. `executeSpawn`
|
|
@@ -100,13 +100,14 @@ async function dispatchViaAdapter(step, input, ctx, executor) {
|
|
|
100
100
|
*/
|
|
101
101
|
export async function executeNoAdapter(step, input, ctx) {
|
|
102
102
|
switch (step.kind) {
|
|
103
|
-
case '
|
|
104
|
-
return
|
|
103
|
+
case 'runCode':
|
|
104
|
+
return executeRunCode(step, input, ctx);
|
|
105
105
|
case 'spawn':
|
|
106
106
|
return executeSpawn(step, input, ctx, (s, i, c) => execute(s, i, c), buildSpawnOpts(ctx));
|
|
107
107
|
default:
|
|
108
|
-
// Kinds that don't currently route through the adapter (
|
|
109
|
-
//
|
|
108
|
+
// Kinds that don't currently route through the adapter (callModel,
|
|
109
|
+
// invokeTool, conditional, inParallel, withContext, loop, schedule).
|
|
110
|
+
// Delegate back to `execute()`
|
|
110
111
|
// so they exercise the normal dispatch table, framework-event emits,
|
|
111
112
|
// abort checks, and depth guard.
|
|
112
113
|
return execute(step, input, ctx);
|
|
@@ -251,11 +252,11 @@ export async function execute(step, input, ctx) {
|
|
|
251
252
|
let result;
|
|
252
253
|
try {
|
|
253
254
|
switch (step.kind) {
|
|
254
|
-
case '
|
|
255
|
-
result = await dispatchViaAdapter(step, input, ctx, () =>
|
|
255
|
+
case 'runCode':
|
|
256
|
+
result = await dispatchViaAdapter(step, input, ctx, () => executeRunCode(step, input, ctx));
|
|
256
257
|
break;
|
|
257
|
-
case '
|
|
258
|
-
result = await
|
|
258
|
+
case 'callModel':
|
|
259
|
+
result = await executeCallModel(step, input, ctx, baseCtx.layers);
|
|
259
260
|
break;
|
|
260
261
|
case 'claude-code':
|
|
261
262
|
case 'codex':
|
|
@@ -263,14 +264,14 @@ export async function execute(step, input, ctx) {
|
|
|
263
264
|
case 'pi':
|
|
264
265
|
result = await executeSubHarness(step, input, ctx);
|
|
265
266
|
break;
|
|
266
|
-
case '
|
|
267
|
-
result = await
|
|
267
|
+
case 'invokeTool':
|
|
268
|
+
result = await executeInvokeTool(step, input, ctx, baseCtx.harness);
|
|
268
269
|
break;
|
|
269
|
-
case '
|
|
270
|
-
result = await
|
|
270
|
+
case 'conditional':
|
|
271
|
+
result = await executeConditional(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
271
272
|
break;
|
|
272
|
-
case '
|
|
273
|
-
result = await
|
|
273
|
+
case 'inParallel':
|
|
274
|
+
result = await executeInParallel(step, input, ctx, (s, i, c) => execute(s, i, c), {
|
|
274
275
|
// Fork paths are child executions: the layer store lets their
|
|
275
276
|
// `onSpawn`/`onReturn` boundary run (see `createForkLayerBridge`).
|
|
276
277
|
layerStore: harnessWithLayerStore(baseCtx).layerStateStore,
|
|
@@ -280,14 +281,14 @@ export async function execute(step, input, ctx) {
|
|
|
280
281
|
case 'spawn':
|
|
281
282
|
result = await dispatchViaAdapter(step, input, ctx, () => executeSpawn(step, input, ctx, (s, i, c) => execute(s, i, c), buildSpawnOpts(ctx)));
|
|
282
283
|
break;
|
|
283
|
-
case '
|
|
284
|
-
result = await
|
|
284
|
+
case 'withContext':
|
|
285
|
+
result = await executeWithContext(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
285
286
|
break;
|
|
286
287
|
case 'loop':
|
|
287
288
|
result = await executeLoop(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
288
289
|
break;
|
|
289
|
-
case '
|
|
290
|
-
result = await
|
|
290
|
+
case 'schedule':
|
|
291
|
+
result = await executeSchedule(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
291
292
|
break;
|
|
292
293
|
default: {
|
|
293
294
|
const _exhaustive = step;
|
|
@@ -4,7 +4,7 @@ import type { Context, DetachedHandle, Step, SubprocessAdapter } from '@noetic-t
|
|
|
4
4
|
* Resolve the subprocess adapter used for a given step dispatch.
|
|
5
5
|
*
|
|
6
6
|
* Precedence: `detachedSpawn-overrides.subprocess ?? step.subprocess ??
|
|
7
|
-
* harness.subprocess`. Only `
|
|
7
|
+
* harness.subprocess`. Only `StepRunCode` and `StepSpawn` currently carry a
|
|
8
8
|
* `subprocess` field — other variants always fall through to the harness
|
|
9
9
|
* default.
|
|
10
10
|
*
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { DetachedHandleImpl } from '../
|
|
1
|
+
import { DetachedHandleImpl } from '../util/detached-handle.js';
|
|
2
2
|
import { executeNoAdapter } from './execute.js';
|
|
3
3
|
//#region Adapter resolution
|
|
4
4
|
/**
|
|
5
5
|
* Resolve the subprocess adapter used for a given step dispatch.
|
|
6
6
|
*
|
|
7
7
|
* Precedence: `detachedSpawn-overrides.subprocess ?? step.subprocess ??
|
|
8
|
-
* harness.subprocess`. Only `
|
|
8
|
+
* harness.subprocess`. Only `StepRunCode` and `StepSpawn` currently carry a
|
|
9
9
|
* `subprocess` field — other variants always fall through to the harness
|
|
10
10
|
* default.
|
|
11
11
|
*
|
|
@@ -15,7 +15,7 @@ export function resolveStepAdapter(step, callOverride, fallback) {
|
|
|
15
15
|
if (callOverride) {
|
|
16
16
|
return callOverride;
|
|
17
17
|
}
|
|
18
|
-
if ((step.kind === '
|
|
18
|
+
if ((step.kind === 'runCode' || step.kind === 'spawn') && step.subprocess) {
|
|
19
19
|
return step.subprocess;
|
|
20
20
|
}
|
|
21
21
|
return fallback;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '@noetic-tools/types';
|
|
2
|
+
/**
|
|
3
|
+
* JSON-parse + validate assistant text against a Standard Schema, raising
|
|
4
|
+
* `model_parse_error`. Shared by the LLM step and sub-harness step handlers.
|
|
5
|
+
*
|
|
6
|
+
* Zod schemas keep the original `safeParse` error; other vendors' Standard
|
|
7
|
+
* Schema issues are adapted into a synthetic `ZodError` of `custom` issues so
|
|
8
|
+
* `model_parse_error.zodError` stays a single error surface.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseStructuredOutput<O>(params: {
|
|
11
|
+
schema: StandardSchemaV1;
|
|
12
|
+
rawText: string;
|
|
13
|
+
stepId: string;
|
|
14
|
+
}): Promise<O>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { frameworkCast, NoeticErrorImpl, standardIssuesToZodError, validateSchema, } from '@noetic-tools/types';
|
|
2
|
+
/**
|
|
3
|
+
* JSON-parse + validate assistant text against a Standard Schema, raising
|
|
4
|
+
* `model_parse_error`. Shared by the LLM step and sub-harness step handlers.
|
|
5
|
+
*
|
|
6
|
+
* Zod schemas keep the original `safeParse` error; other vendors' Standard
|
|
7
|
+
* Schema issues are adapted into a synthetic `ZodError` of `custom` issues so
|
|
8
|
+
* `model_parse_error.zodError` stays a single error surface.
|
|
9
|
+
*/
|
|
10
|
+
export async function parseStructuredOutput(params) {
|
|
11
|
+
const { schema, rawText, stepId } = params;
|
|
12
|
+
let parsed;
|
|
13
|
+
try {
|
|
14
|
+
parsed = JSON.parse(rawText);
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
throw new NoeticErrorImpl({
|
|
18
|
+
kind: 'model_parse_error',
|
|
19
|
+
stepId,
|
|
20
|
+
raw: rawText,
|
|
21
|
+
schema,
|
|
22
|
+
zodError: standardIssuesToZodError([
|
|
23
|
+
{
|
|
24
|
+
message: `Invalid JSON: ${e instanceof Error ? e.message : String(e)}`,
|
|
25
|
+
},
|
|
26
|
+
]),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const result = await validateSchema(schema, parsed);
|
|
30
|
+
if (!result.success) {
|
|
31
|
+
throw new NoeticErrorImpl({
|
|
32
|
+
kind: 'model_parse_error',
|
|
33
|
+
stepId,
|
|
34
|
+
raw: rawText,
|
|
35
|
+
schema,
|
|
36
|
+
zodError: result.zodError,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return frameworkCast(result.value);
|
|
40
|
+
}
|
|
@@ -9,7 +9,7 @@ export declare function isContextImpl(ctx: Context<ContextData>): ctx is Context
|
|
|
9
9
|
/**
|
|
10
10
|
* Co-located with `isContextImpl` so the import of `ContextImpl` flows through
|
|
11
11
|
* the same module other interpreters pull it from, sidestepping a circular
|
|
12
|
-
* TDZ when this helper is used by `
|
|
12
|
+
* TDZ when this helper is used by `executeSchedule` in `execute-control.ts`.
|
|
13
13
|
*/
|
|
14
14
|
export declare function getContextChannelStore<TContext>(ctx: Context<TContext>): ChannelStore | undefined;
|
|
15
15
|
export declare function isFunctionCall(item: Item): item is FunctionCallItem;
|
|
@@ -16,7 +16,7 @@ export function isContextImpl(ctx) {
|
|
|
16
16
|
/**
|
|
17
17
|
* Co-located with `isContextImpl` so the import of `ContextImpl` flows through
|
|
18
18
|
* the same module other interpreters pull it from, sidestepping a circular
|
|
19
|
-
* TDZ when this helper is used by `
|
|
19
|
+
* TDZ when this helper is used by `executeSchedule` in `execute-control.ts`.
|
|
20
20
|
*/
|
|
21
21
|
export function getContextChannelStore(ctx) {
|
|
22
22
|
if (ctx instanceof ContextImpl) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** @public */
|
|
2
|
-
export {
|
|
2
|
+
export { conditional, inParallel } from '../builders/control-flow-builders.js';
|
|
3
3
|
/** @public */
|
|
4
|
-
export { layerData,
|
|
4
|
+
export { layerData, layerFunction } from '../builders/layer-provides-builders.js';
|
|
5
5
|
/** @public */
|
|
6
6
|
export { loop } from '../builders/loop-builder.js';
|
|
7
7
|
/** @public */
|
|
8
8
|
export { spawn } from '../builders/spawn-builder.js';
|
|
9
9
|
/** @public */
|
|
10
|
-
export { step } from '../builders/step-builders.js';
|
|
10
|
+
export { callModel, invokeTool, runCode, step } from '../builders/step-builders.js';
|
|
11
11
|
/** @public */
|
|
12
12
|
export { tool, toolWithGenerator } from '../builders/tool-builder.js';
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/** @public */
|
|
2
|
-
export {
|
|
2
|
+
export { conditional, inParallel } from '../builders/control-flow-builders.js';
|
|
3
3
|
/** @public */
|
|
4
|
-
export { layerData,
|
|
4
|
+
export { layerData, layerFunction } from '../builders/layer-provides-builders.js';
|
|
5
5
|
/** @public */
|
|
6
6
|
export { loop } from '../builders/loop-builder.js';
|
|
7
7
|
/** @public */
|
|
8
8
|
export { spawn } from '../builders/spawn-builder.js';
|
|
9
|
-
// The base
|
|
10
|
-
//
|
|
9
|
+
// The base builders plus the sub-harness `step` namespace, without
|
|
10
|
+
// `workflow` — the portable surface stays free of the hydrator so
|
|
11
|
+
// restricted runtimes don't pull it in.
|
|
11
12
|
/** @public */
|
|
12
|
-
export { step } from '../builders/step-builders.js';
|
|
13
|
+
export { callModel, invokeTool, runCode, step } from '../builders/step-builders.js';
|
|
13
14
|
/** @public */
|
|
14
15
|
export { tool, toolWithGenerator } from '../builders/tool-builder.js';
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
/** @public */
|
|
6
6
|
/** @public */
|
|
7
7
|
/** @public */
|
|
8
|
-
export {
|
|
8
|
+
export { history, observations, plan, scratchpad, taskState, temporal, toolCalls, } from '@noetic-tools/context';
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
/** @public */
|
|
6
6
|
/** @public */
|
|
7
7
|
/** @public */
|
|
8
|
-
export {
|
|
8
|
+
export { history, observations, plan, scratchpad, taskState, temporal, toolCalls, } from '@noetic-tools/context';
|
|
@@ -7,7 +7,7 @@ export type EmitOption = boolean | ((eventType: string, data: Record<string, unk
|
|
|
7
7
|
*
|
|
8
8
|
* When `emit` is `undefined` (the default for non-LLM steps where the
|
|
9
9
|
* field does not exist on the Step type), events are always emitted.
|
|
10
|
-
* Only
|
|
10
|
+
* Only StepCallModel exposes `emit` for opt-out or filtering.
|
|
11
11
|
*
|
|
12
12
|
* @internal
|
|
13
13
|
*/
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* When `emit` is `undefined` (the default for non-LLM steps where the
|
|
7
7
|
* field does not exist on the Step type), events are always emitted.
|
|
8
|
-
* Only
|
|
8
|
+
* Only StepCallModel exposes `emit` for opt-out or filtering.
|
|
9
9
|
*
|
|
10
10
|
* @internal
|
|
11
11
|
*/
|
|
@@ -31,8 +31,8 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
31
31
|
private readonly _createdAt;
|
|
32
32
|
/**
|
|
33
33
|
* Shared channel store for cross-context communication. Inherited by
|
|
34
|
-
* `
|
|
35
|
-
* to peers — see `
|
|
34
|
+
* `inParallel` / `spawn` children so channels published by a sibling are visible
|
|
35
|
+
* to peers — see `executeInParallel` and `executeSpawn` in `interpreter/execute-action.ts`
|
|
36
36
|
* and `interpreter/execute-control.ts`.
|
|
37
37
|
* @internal
|
|
38
38
|
*/
|
|
@@ -46,14 +46,14 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
46
46
|
* Per-context abort fan-out. `abort()` fires it so operations blocked on
|
|
47
47
|
* this context (channel `recv` waiters, parked back-pressure senders)
|
|
48
48
|
* reject promptly with `{ kind: 'cancelled' }` instead of hanging until
|
|
49
|
-
* their timeout (spec 09, Cancellation item 2). Each
|
|
49
|
+
* their timeout (spec 09, Cancellation item 2). Each inParallel/spawn child
|
|
50
50
|
* constructs its own ContextImpl and therefore its own controller —
|
|
51
51
|
* aborting a child never rejects the parent's waiters, while aborting a
|
|
52
52
|
* parent DOES cascade down to every live child (see `_children`).
|
|
53
53
|
*/
|
|
54
54
|
private readonly _abortController;
|
|
55
55
|
/**
|
|
56
|
-
* Live child contexts (
|
|
56
|
+
* Live child contexts (inParallel paths, spawn children) registered for the abort
|
|
57
57
|
* cascade. A child adds itself at construction and removes itself via
|
|
58
58
|
* `detachFromParent()` when its execution settles, so a long-lived parent
|
|
59
59
|
* driving a loop of spawns does not accumulate finished children.
|
|
@@ -77,7 +77,7 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
77
77
|
*/
|
|
78
78
|
private readonly _segments;
|
|
79
79
|
/**
|
|
80
|
-
* Path this context's steps hang under. Empty on a root context;
|
|
80
|
+
* Path this context's steps hang under. Empty on a root context; an inParallel/spawn child
|
|
81
81
|
* inherits its parent's current path plus a discriminator, so a path key stays unique
|
|
82
82
|
* across the whole step tree rather than restarting per child context.
|
|
83
83
|
* @internal
|
|
@@ -91,7 +91,7 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
91
91
|
private readonly _occurrences;
|
|
92
92
|
/**
|
|
93
93
|
* Completion ledger for the execution this context belongs to. Shared by reference
|
|
94
|
-
* with
|
|
94
|
+
* with inParallel/spawn children — one execution, one ledger.
|
|
95
95
|
* @internal
|
|
96
96
|
*/
|
|
97
97
|
readonly ledger?: StepLedger;
|
|
@@ -105,7 +105,7 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
105
105
|
span?: Span;
|
|
106
106
|
channelStore?: ChannelStore;
|
|
107
107
|
checkpointFn?: (ctx: Context) => Promise<void>;
|
|
108
|
-
/** Path this context's steps hang under (
|
|
108
|
+
/** Path this context's steps hang under (inParallel/spawn children inherit one). */
|
|
109
109
|
pathPrefix?: string;
|
|
110
110
|
/** The execution's completion ledger, shared with children by reference. */
|
|
111
111
|
ledger?: StepLedger;
|
|
@@ -134,7 +134,7 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
134
134
|
/**
|
|
135
135
|
* @internal
|
|
136
136
|
* Leave the parent's abort cascade. Called by the interpreter when a spawn
|
|
137
|
-
* child or
|
|
137
|
+
* child or inParallel path settles — a finished child has nothing left to cancel,
|
|
138
138
|
* and holding it would leak for the remaining life of the parent.
|
|
139
139
|
*/
|
|
140
140
|
detachFromParent(): void;
|
|
@@ -150,8 +150,6 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
150
150
|
get shell(): AgentHarnessContract['shell'];
|
|
151
151
|
get subprocess(): AgentHarnessContract['subprocess'];
|
|
152
152
|
get context(): ContextData;
|
|
153
|
-
/** @deprecated Renamed to `context`. Returns the same object. */
|
|
154
|
-
get memory(): ContextData;
|
|
155
153
|
recv<T>(ch: Channel<T>, opts?: {
|
|
156
154
|
timeout?: number;
|
|
157
155
|
}): Promise<T>;
|