@noetic-tools/core 5.2.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 +20 -2
- package/dist/adapters/openrouter.js +102 -6
- 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 +58 -40
- package/dist/harness/agent-harness.js +125 -51
- package/dist/harness/deps/context.d.ts +1 -1
- package/dist/harness/deps/context.js +1 -1
- package/dist/harness/deps/interpreter.d.ts +1 -0
- package/dist/harness/deps/interpreter.js +1 -0
- package/dist/harness/deps/types.d.ts +2 -2
- package/dist/harness/model-call.d.ts +1 -1
- package/dist/harness/model-call.js +45 -23
- package/dist/index.d.ts +23 -35
- package/dist/index.js +10 -26
- package/dist/interpreter/action-deps.d.ts +2 -2
- package/dist/interpreter/action-deps.js +1 -1
- package/dist/interpreter/action-types.d.ts +1 -1
- package/dist/interpreter/collect-tools.js +7 -7
- package/dist/interpreter/context-assembly.d.ts +50 -0
- package/dist/interpreter/context-assembly.js +547 -0
- package/dist/interpreter/execute-action.d.ts +5 -5
- package/dist/interpreter/execute-action.js +337 -208
- 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/observability/genai-attributes.d.ts +20 -0
- package/dist/observability/genai-attributes.js +20 -0
- 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,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Control-flow step handlers:
|
|
2
|
+
* Control-flow step handlers: conditional, inParallel, loop, schedule.
|
|
3
3
|
*/
|
|
4
|
-
import type { Context, ExecuteStepFn,
|
|
4
|
+
import type { Context, ExecuteStepFn, StepConditional, StepInParallel, StepLoop, StepSchedule } from '@noetic-tools/types';
|
|
5
5
|
import type { ItemSchemaRegistry, LayerStateStore } from './action-deps.js';
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function executeConditional<TContext, I, O>(step: StepConditional<TContext, I, O>, input: I, ctx: Context<TContext>, executeStep: ExecuteStepFn): Promise<O>;
|
|
7
7
|
/** Layer-boundary wiring supplied by the interpreter (see `buildSpawnOpts`). */
|
|
8
8
|
export interface ExecuteForkOpts {
|
|
9
9
|
layerStore?: LayerStateStore;
|
|
10
10
|
itemSchemas?: ItemSchemaRegistry;
|
|
11
11
|
}
|
|
12
|
-
export declare function
|
|
12
|
+
export declare function executeInParallel<TContext, I, O>(step: StepInParallel<TContext, I, O>, input: I, ctx: Context<TContext>, rawExecuteStep: ExecuteStepFn, opts?: ExecuteForkOpts): Promise<O>;
|
|
13
13
|
export declare function executeLoop<TContext, I, O>(step: StepLoop<TContext, I, O>, input: I, ctx: Context<TContext>, executeStep: ExecuteStepFn): Promise<O>;
|
|
14
14
|
/**
|
|
15
|
-
* Executes
|
|
16
|
-
* woken early by `
|
|
15
|
+
* Executes a `schedule` step: runs the body step forever, paced by `ms ± jitter`,
|
|
16
|
+
* woken early by `inbox`. Throws `cancelled` when the context is aborted.
|
|
17
17
|
*
|
|
18
18
|
* On body throw, behavior depends on `onError`:
|
|
19
|
-
* - `'continue'` (default): emit `
|
|
19
|
+
* - `'continue'` (default): emit `schedule.iteration.error` span event and proceed
|
|
20
20
|
* to the park step as if no error occurred.
|
|
21
21
|
* - `'fail'`: re-throw, terminating the operator.
|
|
22
22
|
*
|
|
23
|
-
* The body's output is discarded — `
|
|
23
|
+
* The body's output is discarded — `schedule` runs forever and does not accumulate
|
|
24
24
|
* iteration outputs. Only ever returns by throwing on cancellation or `fail`.
|
|
25
25
|
*/
|
|
26
|
-
export declare function
|
|
26
|
+
export declare function executeSchedule<TContext, I, O>(step: StepSchedule<TContext, I, O>, input: I, ctx: Context<TContext>, executeStep: ExecuteStepFn): Promise<O>;
|
|
@@ -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) {
|
|
@@ -3,6 +3,10 @@ export declare const GenAI: {
|
|
|
3
3
|
readonly REQUEST_MODEL: 'gen_ai.request.model';
|
|
4
4
|
readonly USAGE_INPUT_TOKENS: 'gen_ai.usage.input_tokens';
|
|
5
5
|
readonly USAGE_OUTPUT_TOKENS: 'gen_ai.usage.output_tokens';
|
|
6
|
+
/** Prompt tokens served from the provider's cache. Absent if unreported. */
|
|
7
|
+
readonly USAGE_CACHED_INPUT_TOKENS: 'gen_ai.usage.cached_input_tokens';
|
|
8
|
+
/** Prompt tokens written into the provider's cache. Absent if unreported. */
|
|
9
|
+
readonly USAGE_CACHE_WRITE_TOKENS: 'gen_ai.usage.cache_write_tokens';
|
|
6
10
|
readonly COST: 'gen_ai.cost';
|
|
7
11
|
};
|
|
8
12
|
export declare const ToolAttr: {
|
|
@@ -30,4 +34,20 @@ export declare const NoeticAttr: {
|
|
|
30
34
|
readonly SESSION_ID: 'noetic.session.id';
|
|
31
35
|
/** Resource the session is scoped to (the run's `ctx.resourceId`), when set. */
|
|
32
36
|
readonly RESOURCE_ID: 'noetic.resource.id';
|
|
37
|
+
/** Id of the anchoring epoch the assembled view belongs to. */
|
|
38
|
+
readonly CONTEXT_EPOCH_ID: 'noetic.context.epoch.id';
|
|
39
|
+
/** Assemblies served by the current epoch, including this one. */
|
|
40
|
+
readonly CONTEXT_EPOCH_AGE: 'noetic.context.epoch.age';
|
|
41
|
+
/** Why the epoch re-anchored on this assembly, when it did. */
|
|
42
|
+
readonly CONTEXT_REANCHOR_REASON: 'noetic.context.reanchor_reason';
|
|
43
|
+
/** Tokens in the anchor band (the cache-stable prefix). */
|
|
44
|
+
readonly CONTEXT_ANCHOR_TOKENS: 'noetic.context.anchor_tokens';
|
|
45
|
+
/** Tokens in the live band (rendered after history). */
|
|
46
|
+
readonly CONTEXT_LIVE_TOKENS: 'noetic.context.live_tokens';
|
|
47
|
+
/** Tokens spent superseding stale anchors. */
|
|
48
|
+
readonly CONTEXT_DELTA_TOKENS: 'noetic.context.delta_tokens';
|
|
49
|
+
/** JSON array of `{ id, placement, served }` per contributing layer. */
|
|
50
|
+
readonly CONTEXT_LAYER_PLACEMENTS: 'noetic.context.layer_placements';
|
|
51
|
+
/** JSON array of `{ id, rate, rebillTokens }` per contributing layer. */
|
|
52
|
+
readonly CONTEXT_LAYER_CHURN: 'noetic.context.layer_churn';
|
|
33
53
|
};
|
|
@@ -4,6 +4,10 @@ export const GenAI = {
|
|
|
4
4
|
REQUEST_MODEL: 'gen_ai.request.model',
|
|
5
5
|
USAGE_INPUT_TOKENS: 'gen_ai.usage.input_tokens',
|
|
6
6
|
USAGE_OUTPUT_TOKENS: 'gen_ai.usage.output_tokens',
|
|
7
|
+
/** Prompt tokens served from the provider's cache. Absent if unreported. */
|
|
8
|
+
USAGE_CACHED_INPUT_TOKENS: 'gen_ai.usage.cached_input_tokens',
|
|
9
|
+
/** Prompt tokens written into the provider's cache. Absent if unreported. */
|
|
10
|
+
USAGE_CACHE_WRITE_TOKENS: 'gen_ai.usage.cache_write_tokens',
|
|
7
11
|
COST: 'gen_ai.cost',
|
|
8
12
|
};
|
|
9
13
|
export const ToolAttr = {
|
|
@@ -33,4 +37,20 @@ export const NoeticAttr = {
|
|
|
33
37
|
SESSION_ID: 'noetic.session.id',
|
|
34
38
|
/** Resource the session is scoped to (the run's `ctx.resourceId`), when set. */
|
|
35
39
|
RESOURCE_ID: 'noetic.resource.id',
|
|
40
|
+
/** Id of the anchoring epoch the assembled view belongs to. */
|
|
41
|
+
CONTEXT_EPOCH_ID: 'noetic.context.epoch.id',
|
|
42
|
+
/** Assemblies served by the current epoch, including this one. */
|
|
43
|
+
CONTEXT_EPOCH_AGE: 'noetic.context.epoch.age',
|
|
44
|
+
/** Why the epoch re-anchored on this assembly, when it did. */
|
|
45
|
+
CONTEXT_REANCHOR_REASON: 'noetic.context.reanchor_reason',
|
|
46
|
+
/** Tokens in the anchor band (the cache-stable prefix). */
|
|
47
|
+
CONTEXT_ANCHOR_TOKENS: 'noetic.context.anchor_tokens',
|
|
48
|
+
/** Tokens in the live band (rendered after history). */
|
|
49
|
+
CONTEXT_LIVE_TOKENS: 'noetic.context.live_tokens',
|
|
50
|
+
/** Tokens spent superseding stale anchors. */
|
|
51
|
+
CONTEXT_DELTA_TOKENS: 'noetic.context.delta_tokens',
|
|
52
|
+
/** JSON array of `{ id, placement, served }` per contributing layer. */
|
|
53
|
+
CONTEXT_LAYER_PLACEMENTS: 'noetic.context.layer_placements',
|
|
54
|
+
/** JSON array of `{ id, rate, rebillTokens }` per contributing layer. */
|
|
55
|
+
CONTEXT_LAYER_CHURN: 'noetic.context.layer_churn',
|
|
36
56
|
};
|
|
@@ -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';
|