@noetic-tools/core 5.8.0 → 5.9.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/dist/adapters/openrouter.d.ts +1 -0
- package/dist/adapters/openrouter.js +1 -0
- package/dist/builders/provide-builder.d.ts +1 -0
- package/dist/builders/provide-builder.js +1 -0
- package/dist/builders/spawn-builder.d.ts +0 -1
- package/dist/builders/spawn-builder.js +0 -1
- package/dist/builders/step-builders.d.ts +0 -1
- package/dist/builders/step-builders.js +0 -1
- package/dist/builders/tool-builder.d.ts +2 -0
- package/dist/builders/tool-builder.js +2 -0
- package/dist/builders/workflow-hydrator.js +15 -13
- package/dist/builders/workflow-replay-key.d.ts +15 -0
- package/dist/builders/workflow-replay-key.js +46 -0
- package/dist/harness/agent-harness.d.ts +25 -6
- package/dist/harness/agent-harness.js +72 -26
- package/dist/harness/deps/interpreter.d.ts +1 -0
- package/dist/harness/deps/interpreter.js +1 -0
- package/dist/harness/model-call.d.ts +3 -0
- package/dist/harness/model-call.js +9 -5
- package/dist/interpreter/action-deps.d.ts +1 -1
- package/dist/interpreter/action-deps.js +1 -1
- package/dist/interpreter/collect-tools.js +19 -54
- package/dist/interpreter/execute-action.js +10 -2
- package/dist/interpreter/execute-control.js +3 -1
- package/dist/interpreter/execute-decide.js +9 -27
- package/dist/interpreter/execute.js +30 -36
- package/dist/interpreter/model-error.d.ts +9 -0
- package/dist/interpreter/model-error.js +32 -0
- package/dist/interpreter/step-walk.d.ts +11 -0
- package/dist/interpreter/step-walk.js +41 -0
- package/dist/observability/genai-attributes.d.ts +7 -0
- package/dist/observability/genai-attributes.js +8 -0
- package/dist/runtime/context-impl.d.ts +29 -31
- package/dist/runtime/context-impl.js +65 -45
- package/dist/runtime/durable/harness-checkpoints.d.ts +31 -18
- package/dist/runtime/durable/harness-checkpoints.js +70 -54
- package/dist/runtime/durable/step-ledger.d.ts +1 -1
- package/dist/runtime/durable/step-ledger.js +1 -1
- package/dist/schemas/workflow.d.ts +1 -1
- package/package.json +3 -3
- package/schema/noetic-workflow.schema.json +375 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { frameworkCast, isServerToolSpec } from '@noetic-tools/types';
|
|
2
|
+
import { stepChildren } from './step-walk.js';
|
|
2
3
|
/**
|
|
3
4
|
* Recursively walks a step tree and collects all Tool instances
|
|
4
5
|
* declared on LLM steps. Used to build the unified tool set
|
|
@@ -26,59 +27,23 @@ export function deduplicateTools(tools) {
|
|
|
26
27
|
];
|
|
27
28
|
}
|
|
28
29
|
function walkStep(step, out) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
case 'decide':
|
|
48
|
-
// An ACP agent runs its own tools inside the external agent process; it
|
|
49
|
-
// contributes nothing to the host unified tool set.
|
|
50
|
-
case 'acp-agent':
|
|
51
|
-
return;
|
|
52
|
-
case 'conditional':
|
|
53
|
-
if (step._optimizable) {
|
|
54
|
-
for (const child of step._optimizable) {
|
|
55
|
-
walkStep(child, out);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return;
|
|
59
|
-
case 'inParallel':
|
|
60
|
-
if (step._optimizable) {
|
|
61
|
-
for (const child of step._optimizable) {
|
|
62
|
-
walkStep(child, out);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return;
|
|
66
|
-
case 'withContext':
|
|
67
|
-
walkStep(step.child, out);
|
|
68
|
-
return;
|
|
69
|
-
case 'spawn':
|
|
70
|
-
walkStep(step.child, out);
|
|
71
|
-
return;
|
|
72
|
-
case 'loop':
|
|
73
|
-
for (const child of step.steps) {
|
|
74
|
-
walkStep(child, out);
|
|
75
|
-
}
|
|
76
|
-
return;
|
|
77
|
-
case 'schedule':
|
|
78
|
-
walkStep(step.step, out);
|
|
79
|
-
return;
|
|
80
|
-
default:
|
|
81
|
-
step;
|
|
82
|
-
return;
|
|
30
|
+
if (step.kind !== 'callModel') {
|
|
31
|
+
for (const child of stepChildren(step)) {
|
|
32
|
+
walkStep(child, out);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Skip function-form tools: they're resolved per execution from `ctx`
|
|
37
|
+
// and cannot contribute to the pre-computed unified set. Consumers that
|
|
38
|
+
// need those tools in the pool should register them via
|
|
39
|
+
// `AgentHarness.tools` instead. Inline server-tool specs (web search/
|
|
40
|
+
// fetch) are not client `Tool`s — they never enter the unified pool.
|
|
41
|
+
if (!Array.isArray(step.tools)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
for (const entry of step.tools) {
|
|
45
|
+
if (!isServerToolSpec(entry)) {
|
|
46
|
+
out.push(entry);
|
|
47
|
+
}
|
|
83
48
|
}
|
|
84
49
|
}
|
|
@@ -10,6 +10,7 @@ import { cloneWithGuard } from './clone-guard.js';
|
|
|
10
10
|
import { collectAllTools, deduplicateTools } from './collect-tools.js';
|
|
11
11
|
import { prepareBandedView, stampAnchoringAttributes } from './context-assembly.js';
|
|
12
12
|
import { trackUsage } from './message-helpers.js';
|
|
13
|
+
import { wrapModelError } from './model-error.js';
|
|
13
14
|
import { parseStructuredOutput } from './structured-output.js';
|
|
14
15
|
import { getContextChannelStore, isContextImpl, isFunctionCall, isMutableContext, } from './typeguards.js';
|
|
15
16
|
//#region run
|
|
@@ -548,7 +549,9 @@ export async function executeCallModel(step, input, ctx, layers) {
|
|
|
548
549
|
serverToolSpecs,
|
|
549
550
|
assembledItems,
|
|
550
551
|
});
|
|
551
|
-
const response = await baseCtx.harness.callModel(request)
|
|
552
|
+
const response = await baseCtx.harness.callModel(request).catch((e) => {
|
|
553
|
+
throw wrapModelError(e, step.id, baseCtx);
|
|
554
|
+
});
|
|
552
555
|
// Track tokens/cost for EVERY model call, including responses steering
|
|
553
556
|
// subsequently rejects (Deny throw / Guide retry below) — the spend is
|
|
554
557
|
// real, and until.maxCost / HarnessResponse.usage must see it (spec 07:
|
|
@@ -738,8 +741,13 @@ export async function executeSpawn(step, input, ctx, executeStep, opts) {
|
|
|
738
741
|
}
|
|
739
742
|
return returnLayers({
|
|
740
743
|
layers,
|
|
744
|
+
stepId: step.id,
|
|
741
745
|
parentCtx: parentExecutionCtx,
|
|
742
|
-
|
|
746
|
+
// `childExecutionCtx` predates the spawn span; trace onto that span.
|
|
747
|
+
childCtx: {
|
|
748
|
+
...childExecutionCtx,
|
|
749
|
+
trace: span,
|
|
750
|
+
},
|
|
743
751
|
childLog: childCtx.itemLog,
|
|
744
752
|
result: childOutput,
|
|
745
753
|
store: layerStore,
|
|
@@ -86,7 +86,7 @@ function tracePaths(ctx, executeStep, childContexts) {
|
|
|
86
86
|
},
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
|
-
function createForkLayerBridge({ ctx, childContexts, opts, }) {
|
|
89
|
+
function createForkLayerBridge({ stepId, ctx, childContexts, opts, }) {
|
|
90
90
|
const layers = ctx.layers;
|
|
91
91
|
const layerStore = opts?.layerStore;
|
|
92
92
|
if (!layers || layers.length === 0 || !layerStore) {
|
|
@@ -113,6 +113,7 @@ function createForkLayerBridge({ ctx, childContexts, opts, }) {
|
|
|
113
113
|
settle(index, result) {
|
|
114
114
|
return serialize(() => returnLayers({
|
|
115
115
|
layers: layers ?? [],
|
|
116
|
+
stepId,
|
|
116
117
|
parentCtx: parentExecCtx,
|
|
117
118
|
childCtx: childExecCtxs[index],
|
|
118
119
|
childLog: childContexts[index].itemLog,
|
|
@@ -173,6 +174,7 @@ export async function executeInParallel(step, input, ctx, rawExecuteStep, opts)
|
|
|
173
174
|
const childContexts = createChildContexts(baseCtx, paths, step.id);
|
|
174
175
|
const concurrency = step.concurrency ?? paths.length;
|
|
175
176
|
const traced = tracePaths(baseCtx, withForkLayers(rawExecuteStep, childContexts, createForkLayerBridge({
|
|
177
|
+
stepId: step.id,
|
|
176
178
|
ctx: baseCtx,
|
|
177
179
|
childContexts,
|
|
178
180
|
opts,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { choiceValues, DecideOutputError, frameworkCast, hashString,
|
|
1
|
+
import { choiceValues, DecideOutputError, frameworkCast, hashString, NoeticConfigError, NoeticErrorImpl, scoreLevels, } from '@noetic-tools/types';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { GenAI, NoeticAttr } from '../observability/genai-attributes.js';
|
|
4
|
-
import {
|
|
5
|
-
import { allocateBudgets, assembleView, DECISION_PROJECTION } from './action-deps.js';
|
|
4
|
+
import { allocateBudgets, assembleView, DECISION_PROJECTION, laneBase } from './action-deps.js';
|
|
6
5
|
import { resolveLazy } from './execute-action.js';
|
|
7
6
|
import { trackUsage } from './message-helpers.js';
|
|
7
|
+
import { wrapModelError } from './model-error.js';
|
|
8
8
|
import { isContextImpl, isMutableContext } from './typeguards.js';
|
|
9
9
|
//#region Public API
|
|
10
10
|
/**
|
|
@@ -110,10 +110,12 @@ export async function executeDecide(step, input, ctx, layers) {
|
|
|
110
110
|
*/
|
|
111
111
|
const ANSWERED = new WeakMap();
|
|
112
112
|
function answered(ctx) {
|
|
113
|
-
|
|
113
|
+
// Every step runs on its own lane of the context; the answers belong to the context.
|
|
114
|
+
const key = laneBase(ctx);
|
|
115
|
+
let map = ANSWERED.get(key);
|
|
114
116
|
if (!map) {
|
|
115
117
|
map = new Map();
|
|
116
|
-
ANSWERED.set(
|
|
118
|
+
ANSWERED.set(key, map);
|
|
117
119
|
}
|
|
118
120
|
return map;
|
|
119
121
|
}
|
|
@@ -150,16 +152,7 @@ async function askHarness(step, ctx, request) {
|
|
|
150
152
|
return await ctx.harness.decide(request);
|
|
151
153
|
}
|
|
152
154
|
catch (e) {
|
|
153
|
-
if (
|
|
154
|
-
throw e;
|
|
155
|
-
}
|
|
156
|
-
if (ctx.aborted) {
|
|
157
|
-
throw new NoeticErrorImpl({
|
|
158
|
-
kind: 'cancelled',
|
|
159
|
-
reason: ctx.abortReason ?? 'context aborted',
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
if (e instanceof DecideOutputError) {
|
|
155
|
+
if (e instanceof DecideOutputError && !ctx.aborted) {
|
|
163
156
|
if (e.usage) {
|
|
164
157
|
trackUsage(ctx, {
|
|
165
158
|
items: [],
|
|
@@ -175,18 +168,7 @@ async function askHarness(step, ctx, request) {
|
|
|
175
168
|
zodError: e.zodError,
|
|
176
169
|
});
|
|
177
170
|
}
|
|
178
|
-
|
|
179
|
-
throw new NoeticErrorImpl({
|
|
180
|
-
kind: 'model_rate_limit',
|
|
181
|
-
stepId: step.id,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
throw new NoeticErrorImpl({
|
|
185
|
-
kind: 'step_failed',
|
|
186
|
-
stepId: step.id,
|
|
187
|
-
cause: e instanceof Error ? e : new Error(String(e)),
|
|
188
|
-
retriesExhausted: false,
|
|
189
|
-
});
|
|
171
|
+
throw wrapModelError(e, step.id, ctx);
|
|
190
172
|
}
|
|
191
173
|
}
|
|
192
174
|
//#endregion
|
|
@@ -142,14 +142,12 @@ export function resolveLedgerKey(step, input, ctx) {
|
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
144
|
* Resume path: when a previous run recorded this exact step at `ledgerKey`,
|
|
145
|
-
* replay its output instead of re-running
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* `step_replayed`. Returns `{ hit: false }` when there is nothing to replay so
|
|
149
|
-
* the caller dispatches the step normally.
|
|
145
|
+
* replay its output instead of re-running, and emit `step_replayed`. Returns
|
|
146
|
+
* `{ hit: false }` when there is nothing to replay so the caller dispatches the
|
|
147
|
+
* step normally.
|
|
150
148
|
*/
|
|
151
149
|
function replayFromLedger(opts) {
|
|
152
|
-
const { ledger, ledgerKey,
|
|
150
|
+
const { ledger, ledgerKey, step, broadcaster, agentName } = opts;
|
|
153
151
|
if (!ledger || ledgerKey === undefined || ledger.isEmpty) {
|
|
154
152
|
return {
|
|
155
153
|
hit: false,
|
|
@@ -164,9 +162,6 @@ function replayFromLedger(opts) {
|
|
|
164
162
|
hit: false,
|
|
165
163
|
};
|
|
166
164
|
}
|
|
167
|
-
if (impl && step.id.length > 0) {
|
|
168
|
-
impl.leaveStep(step.id);
|
|
169
|
-
}
|
|
170
165
|
emitFrameworkEvent({
|
|
171
166
|
broadcaster,
|
|
172
167
|
agentName,
|
|
@@ -249,56 +244,57 @@ export async function execute(step, input, ctx) {
|
|
|
249
244
|
data: startedData,
|
|
250
245
|
});
|
|
251
246
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
247
|
+
/* Register the step as in flight, so a checkpoint records it in the frontier, and
|
|
248
|
+
* run it on its own lane: a view of the context that carries this dispatch's path.
|
|
249
|
+
* Steps this step dispatches hang under that path, so concurrent dispatches on one
|
|
250
|
+
* context cannot interleave each other's paths. Steps with empty ids stay on the
|
|
251
|
+
* caller's lane — they are not reattachable by id and add no path segment. */
|
|
255
252
|
const impl = asContextImpl(baseCtx);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
253
|
+
const frame = {
|
|
254
|
+
stepId: step.id,
|
|
255
|
+
input,
|
|
256
|
+
};
|
|
257
|
+
const lane = impl && step.id.length > 0 ? impl.enterStep(frame) : null;
|
|
258
|
+
const stepCtx = lane ? frameworkCast(lane) : ctx;
|
|
262
259
|
/* Resume: a previous run that completed this exact step replays its recorded
|
|
263
260
|
* output instead of re-running it. `ledgerKey`/`ledger` are captured before
|
|
264
|
-
* dispatch and reused by the record below
|
|
265
|
-
|
|
266
|
-
const ledgerKey = resolveLedgerKey(step, input, ctx);
|
|
261
|
+
* dispatch and reused by the record below. */
|
|
262
|
+
const ledgerKey = resolveLedgerKey(step, input, stepCtx);
|
|
267
263
|
const ledger = impl?.ledger;
|
|
268
264
|
const replay = replayFromLedger({
|
|
269
265
|
ledger,
|
|
270
266
|
ledgerKey,
|
|
271
|
-
impl,
|
|
272
267
|
step,
|
|
273
268
|
broadcaster,
|
|
274
269
|
agentName,
|
|
275
270
|
});
|
|
276
271
|
if (replay.hit) {
|
|
272
|
+
lane?.leaveStep(frame);
|
|
277
273
|
return replay.output;
|
|
278
274
|
}
|
|
279
275
|
let result;
|
|
280
276
|
try {
|
|
281
277
|
switch (step.kind) {
|
|
282
278
|
case 'runCode':
|
|
283
|
-
result = await dispatchViaAdapter(step, input,
|
|
279
|
+
result = await dispatchViaAdapter(step, input, stepCtx, () => executeRunCode(step, input, stepCtx));
|
|
284
280
|
break;
|
|
285
281
|
case 'callModel':
|
|
286
|
-
result = await executeCallModel(step, input,
|
|
282
|
+
result = await executeCallModel(step, input, stepCtx, baseCtx.layers);
|
|
287
283
|
break;
|
|
288
284
|
case 'acp-agent':
|
|
289
|
-
result = await executeAcpAgent(step, input,
|
|
285
|
+
result = await executeAcpAgent(step, input, stepCtx, baseCtx.layers);
|
|
290
286
|
break;
|
|
291
287
|
case 'invokeTool':
|
|
292
|
-
result = await executeInvokeTool(step, input,
|
|
288
|
+
result = await executeInvokeTool(step, input, stepCtx, baseCtx.harness);
|
|
293
289
|
break;
|
|
294
290
|
case 'decide':
|
|
295
|
-
result = await executeDecide(step, input,
|
|
291
|
+
result = await executeDecide(step, input, stepCtx, baseCtx.layers);
|
|
296
292
|
break;
|
|
297
293
|
case 'conditional':
|
|
298
|
-
result = await executeConditional(step, input,
|
|
294
|
+
result = await executeConditional(step, input, stepCtx, (s, i, c) => execute(s, i, c));
|
|
299
295
|
break;
|
|
300
296
|
case 'inParallel':
|
|
301
|
-
result = await executeInParallel(step, input,
|
|
297
|
+
result = await executeInParallel(step, input, stepCtx, (s, i, c) => execute(s, i, c), {
|
|
302
298
|
// Fork paths are child executions: the layer store lets their
|
|
303
299
|
// `onSpawn`/`onReturn` boundary run (see `createForkLayerBridge`).
|
|
304
300
|
layerStore: harnessWithLayerStore(baseCtx).layerStateStore,
|
|
@@ -306,16 +302,16 @@ export async function execute(step, input, ctx) {
|
|
|
306
302
|
});
|
|
307
303
|
break;
|
|
308
304
|
case 'spawn':
|
|
309
|
-
result = await dispatchViaAdapter(step, input,
|
|
305
|
+
result = await dispatchViaAdapter(step, input, stepCtx, () => executeSpawn(step, input, stepCtx, (s, i, c) => execute(s, i, c), buildSpawnOpts(stepCtx)));
|
|
310
306
|
break;
|
|
311
307
|
case 'withContext':
|
|
312
|
-
result = await executeWithContext(step, input,
|
|
308
|
+
result = await executeWithContext(step, input, stepCtx, (s, i, c) => execute(s, i, c));
|
|
313
309
|
break;
|
|
314
310
|
case 'loop':
|
|
315
|
-
result = await executeLoop(step, input,
|
|
311
|
+
result = await executeLoop(step, input, stepCtx, (s, i, c) => execute(s, i, c));
|
|
316
312
|
break;
|
|
317
313
|
case 'schedule':
|
|
318
|
-
result = await executeSchedule(step, input,
|
|
314
|
+
result = await executeSchedule(step, input, stepCtx, (s, i, c) => execute(s, i, c));
|
|
319
315
|
break;
|
|
320
316
|
default: {
|
|
321
317
|
const _exhaustive = step;
|
|
@@ -330,9 +326,7 @@ export async function execute(step, input, ctx) {
|
|
|
330
326
|
}
|
|
331
327
|
}
|
|
332
328
|
finally {
|
|
333
|
-
|
|
334
|
-
impl.leaveStep(step.id);
|
|
335
|
-
}
|
|
329
|
+
lane?.leaveStep(frame);
|
|
336
330
|
}
|
|
337
331
|
// Emit step_completed framework event (respects step.emit option)
|
|
338
332
|
const completedData = {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Context } from './action-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Give an error from a model provider call the step's id and a Noetic kind.
|
|
4
|
+
*
|
|
5
|
+
* Noetic errors pass through untouched. A failure on an aborted context is
|
|
6
|
+
* `cancelled` (the abort is what broke the call), a 429 is `model_rate_limit`,
|
|
7
|
+
* and anything else is `step_failed` with the provider error as its cause.
|
|
8
|
+
*/
|
|
9
|
+
export declare function wrapModelError(e: unknown, stepId: string, ctx: Pick<Context, 'aborted' | 'abortReason'>): Error;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { isNoeticError, NoeticConfigError, NoeticErrorImpl } from '@noetic-tools/types';
|
|
2
|
+
import { statusOf } from '../util/status-code.js';
|
|
3
|
+
/**
|
|
4
|
+
* Give an error from a model provider call the step's id and a Noetic kind.
|
|
5
|
+
*
|
|
6
|
+
* Noetic errors pass through untouched. A failure on an aborted context is
|
|
7
|
+
* `cancelled` (the abort is what broke the call), a 429 is `model_rate_limit`,
|
|
8
|
+
* and anything else is `step_failed` with the provider error as its cause.
|
|
9
|
+
*/
|
|
10
|
+
export function wrapModelError(e, stepId, ctx) {
|
|
11
|
+
if (isNoeticError(e) || e instanceof NoeticConfigError) {
|
|
12
|
+
return e;
|
|
13
|
+
}
|
|
14
|
+
if (ctx.aborted) {
|
|
15
|
+
return new NoeticErrorImpl({
|
|
16
|
+
kind: 'cancelled',
|
|
17
|
+
reason: ctx.abortReason ?? 'context aborted',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
if (statusOf(e) === 429) {
|
|
21
|
+
return new NoeticErrorImpl({
|
|
22
|
+
kind: 'model_rate_limit',
|
|
23
|
+
stepId,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return new NoeticErrorImpl({
|
|
27
|
+
kind: 'step_failed',
|
|
28
|
+
stepId,
|
|
29
|
+
cause: e instanceof Error ? e : new Error(String(e)),
|
|
30
|
+
retriesExhausted: false,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Step } from '@noetic-tools/types';
|
|
2
|
+
/**
|
|
3
|
+
* Whether `step` or any step nested in it satisfies `test`.
|
|
4
|
+
*
|
|
5
|
+
* Only statically declared children are visited: a `conditional` or
|
|
6
|
+
* `inParallel` built from functions exposes its children through
|
|
7
|
+
* `_optimizable` only when the builder could enumerate them.
|
|
8
|
+
*/
|
|
9
|
+
export declare function someStep(step: Step, test: (step: Step) => boolean): boolean;
|
|
10
|
+
/** The steps nested directly inside `step`, as far as they are statically known. */
|
|
11
|
+
export declare function stepChildren(step: Step): ReadonlyArray<Step>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether `step` or any step nested in it satisfies `test`.
|
|
3
|
+
*
|
|
4
|
+
* Only statically declared children are visited: a `conditional` or
|
|
5
|
+
* `inParallel` built from functions exposes its children through
|
|
6
|
+
* `_optimizable` only when the builder could enumerate them.
|
|
7
|
+
*/
|
|
8
|
+
export function someStep(step, test) {
|
|
9
|
+
if (test(step)) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return stepChildren(step).some((child) => someStep(child, test));
|
|
13
|
+
}
|
|
14
|
+
/** The steps nested directly inside `step`, as far as they are statically known. */
|
|
15
|
+
export function stepChildren(step) {
|
|
16
|
+
switch (step.kind) {
|
|
17
|
+
case 'callModel':
|
|
18
|
+
case 'runCode':
|
|
19
|
+
case 'invokeTool':
|
|
20
|
+
case 'decide':
|
|
21
|
+
case 'acp-agent':
|
|
22
|
+
return [];
|
|
23
|
+
case 'conditional':
|
|
24
|
+
case 'inParallel':
|
|
25
|
+
return step._optimizable ?? [];
|
|
26
|
+
case 'withContext':
|
|
27
|
+
case 'spawn':
|
|
28
|
+
return [
|
|
29
|
+
step.child,
|
|
30
|
+
];
|
|
31
|
+
case 'loop':
|
|
32
|
+
return step.steps;
|
|
33
|
+
case 'schedule':
|
|
34
|
+
return [
|
|
35
|
+
step.step,
|
|
36
|
+
];
|
|
37
|
+
default:
|
|
38
|
+
step;
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -72,4 +72,11 @@ export declare const NoeticEvent: {
|
|
|
72
72
|
readonly LOOP_VERDICT: "loop.verdict";
|
|
73
73
|
/** A channel dropped data or is waiting with no timeout: `{ kind, channelName, message, capacity?, operation? }`. */
|
|
74
74
|
readonly CHANNEL_WARNING: "channel.warning";
|
|
75
|
+
/**
|
|
76
|
+
* A layer's `onReturn` hook failed, so a spawn or inParallel path returned
|
|
77
|
+
* its result unchanged. Added to the child's span: `{ stepId, layerId,
|
|
78
|
+
* message }`. Defined in `@noetic-tools/context`, whose `returnLayers`
|
|
79
|
+
* emits it and cannot import core.
|
|
80
|
+
*/
|
|
81
|
+
readonly SPAWN_SUMMARY_FAILED: "spawn.summary_failed";
|
|
75
82
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SPAWN_SUMMARY_FAILED_EVENT } from '@noetic-tools/context';
|
|
1
2
|
// GenAI Semantic Convention attributes
|
|
2
3
|
export const GenAI = {
|
|
3
4
|
SYSTEM: 'gen_ai.system',
|
|
@@ -77,4 +78,11 @@ export const NoeticEvent = {
|
|
|
77
78
|
LOOP_VERDICT: 'loop.verdict',
|
|
78
79
|
/** A channel dropped data or is waiting with no timeout: `{ kind, channelName, message, capacity?, operation? }`. */
|
|
79
80
|
CHANNEL_WARNING: 'channel.warning',
|
|
81
|
+
/**
|
|
82
|
+
* A layer's `onReturn` hook failed, so a spawn or inParallel path returned
|
|
83
|
+
* its result unchanged. Added to the child's span: `{ stepId, layerId,
|
|
84
|
+
* message }`. Defined in `@noetic-tools/context`, whose `returnLayers`
|
|
85
|
+
* emits it and cannot import core.
|
|
86
|
+
*/
|
|
87
|
+
SPAWN_SUMMARY_FAILED: SPAWN_SUMMARY_FAILED_EVENT,
|
|
80
88
|
};
|
|
@@ -69,30 +69,24 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
69
69
|
private readonly _children;
|
|
70
70
|
private _contextData?;
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* a restart can identify which step the context was paused inside.
|
|
72
|
+
* Steps in flight on this context, in dispatch order. `enterStep` adds a frame and
|
|
73
|
+
* `leaveStep` removes that same frame, so concurrent dispatches can settle in any
|
|
74
|
+
* order without disturbing each other. The checkpoint writer serialises this set as
|
|
75
|
+
* the execution frontier.
|
|
77
76
|
* @internal
|
|
78
77
|
*/
|
|
79
|
-
private readonly
|
|
78
|
+
private readonly _inFlight;
|
|
80
79
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
80
|
+
* Execution path of the step whose lane this is. On the context itself it is the
|
|
81
|
+
* prefix its steps hang under: empty on a root context, the parent's path plus a
|
|
82
|
+
* discriminator on an inParallel/spawn child, so a path stays unique across the whole
|
|
83
|
+
* step tree. A lane (see `enterStep`) reads its own step's path here instead.
|
|
83
84
|
* @internal
|
|
84
85
|
*/
|
|
85
|
-
private readonly
|
|
86
|
-
/**
|
|
87
|
-
* Path this context's steps hang under. Empty on a root context; an inParallel/spawn child
|
|
88
|
-
* inherits its parent's current path plus a discriminator, so a path key stays unique
|
|
89
|
-
* across the whole step tree rather than restarting per child context.
|
|
90
|
-
* @internal
|
|
91
|
-
*/
|
|
92
|
-
private readonly _pathPrefix;
|
|
86
|
+
private readonly _path;
|
|
93
87
|
/**
|
|
94
88
|
* Dispatch counts per `<parent path>/<step id>`, so a loop body re-entered on each
|
|
95
|
-
* iteration gets `#0`, `#1`, … instead of colliding on one key.
|
|
89
|
+
* iteration gets `#0`, `#1`, … instead of colliding on one key. Shared by every lane.
|
|
96
90
|
* @internal
|
|
97
91
|
*/
|
|
98
92
|
private readonly _occurrences;
|
|
@@ -179,28 +173,26 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
179
173
|
get abortSignal(): AbortSignal;
|
|
180
174
|
/**
|
|
181
175
|
* @internal
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
176
|
+
* Register `frame` as in flight and return the lane its step runs on: a view of
|
|
177
|
+
* this context that shares everything except its execution path. `execute()` hands
|
|
178
|
+
* the lane to the step, so steps the step dispatches hang under its path no matter
|
|
179
|
+
* how other dispatches on this context interleave. The occurrence ordinal is taken
|
|
180
|
+
* here, at dispatch, never at settle.
|
|
185
181
|
*/
|
|
186
|
-
enterStep(frame: FrontierFrame):
|
|
182
|
+
enterStep(frame: FrontierFrame): ContextImpl;
|
|
187
183
|
/**
|
|
188
184
|
* @internal
|
|
189
|
-
* The execution path of the step
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* that produced them.
|
|
185
|
+
* The execution path of the step this lane belongs to — the ledger key identifying
|
|
186
|
+
* this exact dispatch. Stable across a replay given the same control flow, which is
|
|
187
|
+
* what lets a resumed run line recorded outputs up with the steps that produced them.
|
|
193
188
|
*/
|
|
194
189
|
currentPath(): string;
|
|
195
190
|
/**
|
|
196
191
|
* @internal
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* is used as a consistency check — if it does not match the top frame
|
|
200
|
-
* the pop is still performed, but a best-effort warning is surfaced to
|
|
201
|
-
* stderr rather than swallowed silently.
|
|
192
|
+
* Drop `frame` from the in-flight set. Called when its step resolves, success or
|
|
193
|
+
* failure.
|
|
202
194
|
*/
|
|
203
|
-
leaveStep(
|
|
195
|
+
leaveStep(frame: FrontierFrame): void;
|
|
204
196
|
/**
|
|
205
197
|
* @internal
|
|
206
198
|
* Return a defensive copy of the current frontier. Consumed by the
|
|
@@ -208,3 +200,9 @@ export declare class ContextImpl implements Context<ContextData> {
|
|
|
208
200
|
*/
|
|
209
201
|
serialiseFrontier(): FrontierFrame[];
|
|
210
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* @internal
|
|
205
|
+
* The context a lane views, or `ctx` itself when it is not a lane. For per-execution
|
|
206
|
+
* caches keyed by context identity, which every lane of one context must share.
|
|
207
|
+
*/
|
|
208
|
+
export declare function laneBase(ctx: Context<ContextData>): Context<ContextData>;
|