@noetic-tools/core 2.2.1 → 2.3.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/builders/step-builders.d.ts +7 -7
- package/dist/builders/workflow-hydrator.js +40 -16
- package/dist/interpreter/execute-control.d.ts +7 -1
- package/dist/interpreter/execute-control.js +81 -1
- package/dist/interpreter/execute.js +6 -1
- package/dist/observability/genai-attributes.d.ts +15 -15
- package/dist/patterns/dynamic-workflow.d.ts +7 -1
- package/dist/patterns/dynamic-workflow.js +1 -0
- package/dist/schemas/workflow.d.ts +16 -9
- package/dist/schemas/workflow.js +1 -0
- package/package.json +3 -3
- package/schema/noetic-workflow.schema.json +8 -0
|
@@ -66,7 +66,7 @@ export declare const step: {
|
|
|
66
66
|
* @throws `NoeticConfigError` with code `MISSING_EXECUTE_FUNCTION` if `execute` is not provided.
|
|
67
67
|
* @throws `NoeticConfigError` with code `DUPLICATE_STEP_ID` if another step with the same id is already registered with a different body.
|
|
68
68
|
*/
|
|
69
|
-
run<TMemory =
|
|
69
|
+
run<TMemory = ContextMemory, I = unknown, O = unknown>(opts: StepRunOpts<TMemory, I, O>): StepRun<TMemory, I, O>;
|
|
70
70
|
/**
|
|
71
71
|
* Creates an LLM model call step with optional tools and structured output.
|
|
72
72
|
*
|
|
@@ -82,7 +82,7 @@ export declare const step: {
|
|
|
82
82
|
* @throws `NoeticConfigError` with code `EMPTY_STEP_ID` if `id` is empty.
|
|
83
83
|
* @throws `NoeticConfigError` with code `MISSING_MODEL` if an eager `model` string is empty. Function-form models are validated at step execution.
|
|
84
84
|
*/
|
|
85
|
-
llm<TMemory =
|
|
85
|
+
llm<TMemory = ContextMemory, I = unknown, O = unknown>(opts: StepLLMOpts<TMemory, O>): StepLLM<TMemory, I, O>;
|
|
86
86
|
/**
|
|
87
87
|
* Creates a tool execution step that invokes a typed tool definition.
|
|
88
88
|
*
|
|
@@ -95,7 +95,7 @@ export declare const step: {
|
|
|
95
95
|
* @throws `NoeticConfigError` with code `EMPTY_STEP_ID` if `id` is empty.
|
|
96
96
|
* @throws `NoeticConfigError` with code `MISSING_TOOL` if `tool` is not provided.
|
|
97
97
|
*/
|
|
98
|
-
tool<TMemory =
|
|
98
|
+
tool<TMemory = ContextMemory, I = unknown, O = unknown>(opts: StepToolOpts<I, O>): StepTool<TMemory, I, O>;
|
|
99
99
|
/**
|
|
100
100
|
* Creates a step that delegates a turn to the Claude Code harness.
|
|
101
101
|
*
|
|
@@ -108,27 +108,27 @@ export declare const step: {
|
|
|
108
108
|
* @returns A `StepSubHarness` of kind `claude-code`, auto-registered in the step registry.
|
|
109
109
|
* @throws `NoeticConfigError` `EMPTY_STEP_ID` / `MISSING_SUB_HARNESS` / `SUB_HARNESS_KIND_MISMATCH`.
|
|
110
110
|
*/
|
|
111
|
-
claudeCode<TMemory =
|
|
111
|
+
claudeCode<TMemory = ContextMemory, I = unknown, O = unknown>(opts: StepSubHarnessOpts<TMemory, O>): StepSubHarness<TMemory, I, O>;
|
|
112
112
|
/**
|
|
113
113
|
* Creates a step that delegates a turn to the Codex harness.
|
|
114
114
|
* @public
|
|
115
115
|
* @param opts - See {@link step.claudeCode}; `opts.harness` is a `codex(...)` adapter.
|
|
116
116
|
* @returns A `StepSubHarness` of kind `codex`.
|
|
117
117
|
*/
|
|
118
|
-
codex<TMemory =
|
|
118
|
+
codex<TMemory = ContextMemory, I = unknown, O = unknown>(opts: StepSubHarnessOpts<TMemory, O>): StepSubHarness<TMemory, I, O>;
|
|
119
119
|
/**
|
|
120
120
|
* Creates a step that delegates a turn to the opencode harness.
|
|
121
121
|
* @public
|
|
122
122
|
* @param opts - See {@link step.claudeCode}; `opts.harness` is an `opencode(...)` adapter.
|
|
123
123
|
* @returns A `StepSubHarness` of kind `opencode`.
|
|
124
124
|
*/
|
|
125
|
-
opencode<TMemory =
|
|
125
|
+
opencode<TMemory = ContextMemory, I = unknown, O = unknown>(opts: StepSubHarnessOpts<TMemory, O>): StepSubHarness<TMemory, I, O>;
|
|
126
126
|
/**
|
|
127
127
|
* Creates a step that delegates a turn to the pi harness.
|
|
128
128
|
* @public
|
|
129
129
|
* @param opts - See {@link step.claudeCode}; `opts.harness` is a `pi(...)` adapter.
|
|
130
130
|
* @returns A `StepSubHarness` of kind `pi`.
|
|
131
131
|
*/
|
|
132
|
-
pi<TMemory =
|
|
132
|
+
pi<TMemory = ContextMemory, I = unknown, O = unknown>(opts: StepSubHarnessOpts<TMemory, O>): StepSubHarness<TMemory, I, O>;
|
|
133
133
|
};
|
|
134
134
|
export {};
|
|
@@ -271,39 +271,63 @@ function hydrateForkNode(node, ctx) {
|
|
|
271
271
|
_optimizable: optimizable,
|
|
272
272
|
});
|
|
273
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Resolves layer names declared on a `provide` / `spawn` node against the
|
|
276
|
+
* registry on `HydrationContext.layers`. Returns `[]` when no registry was
|
|
277
|
+
* supplied — the host runs with its harness-default layers in that case.
|
|
278
|
+
*/
|
|
279
|
+
function resolveNamedLayers({ names, nodeKind, nodeId, ctx, }) {
|
|
280
|
+
if (!ctx.layers) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
return names.map((name) => {
|
|
284
|
+
const layer = ctx.layers?.get(name);
|
|
285
|
+
if (!layer) {
|
|
286
|
+
throw new NoeticConfigError({
|
|
287
|
+
code: 'UNKNOWN_LAYER_REFERENCE',
|
|
288
|
+
message: `Memory layer '${name}' referenced in ${nodeKind} node '${nodeId}' is not registered.`,
|
|
289
|
+
hint: `Available layers: ${[
|
|
290
|
+
...(ctx.layers?.keys() ?? []),
|
|
291
|
+
].join(', ') || '(none)'}. Pass named layers via HydrationContext.layers.`,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return layer;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
274
297
|
function hydrateSpawnNode(node, ctx) {
|
|
275
298
|
if (node.kind !== 'spawn') {
|
|
276
299
|
return frameworkCast(undefined);
|
|
277
300
|
}
|
|
301
|
+
// `memory` is left undefined when the node names no layers so the child
|
|
302
|
+
// inherits the parent's layers (spec 04). An explicit list replaces them.
|
|
303
|
+
const resolvedLayers = node.layers
|
|
304
|
+
? resolveNamedLayers({
|
|
305
|
+
names: node.layers,
|
|
306
|
+
nodeKind: 'spawn',
|
|
307
|
+
nodeId: node.id,
|
|
308
|
+
ctx,
|
|
309
|
+
})
|
|
310
|
+
: undefined;
|
|
278
311
|
return spawn({
|
|
279
312
|
id: node.id,
|
|
280
313
|
child: hydrateNode(node.child, ctx),
|
|
281
314
|
timeout: node.timeout,
|
|
315
|
+
memory: resolvedLayers,
|
|
282
316
|
});
|
|
283
317
|
}
|
|
284
318
|
function hydrateProvideNode(node, ctx) {
|
|
285
319
|
if (node.kind !== 'provide') {
|
|
286
320
|
return frameworkCast(undefined);
|
|
287
321
|
}
|
|
288
|
-
const resolvedLayers = ctx.layers
|
|
289
|
-
? node.layers.map((name) => {
|
|
290
|
-
const layer = ctx.layers?.get(name);
|
|
291
|
-
if (!layer) {
|
|
292
|
-
throw new NoeticConfigError({
|
|
293
|
-
code: 'UNKNOWN_LAYER_REFERENCE',
|
|
294
|
-
message: `Memory layer '${name}' referenced in provide node '${node.id}' is not registered.`,
|
|
295
|
-
hint: `Available layers: ${[
|
|
296
|
-
...(ctx.layers?.keys() ?? []),
|
|
297
|
-
].join(', ') || '(none)'}. Pass named layers via HydrationContext.layers.`,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
return layer;
|
|
301
|
-
})
|
|
302
|
-
: [];
|
|
303
322
|
return provide({
|
|
304
323
|
id: node.id,
|
|
305
324
|
child: hydrateNode(node.child, ctx),
|
|
306
|
-
memory:
|
|
325
|
+
memory: resolveNamedLayers({
|
|
326
|
+
names: node.layers,
|
|
327
|
+
nodeKind: 'provide',
|
|
328
|
+
nodeId: node.id,
|
|
329
|
+
ctx,
|
|
330
|
+
}),
|
|
307
331
|
});
|
|
308
332
|
}
|
|
309
333
|
function hydrateLoopNode(node, ctx) {
|
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
* Control-flow step handlers: branch, fork, loop, every.
|
|
3
3
|
*/
|
|
4
4
|
import type { Context, ExecuteStepFn, StepBranch, StepEvery, StepFork, StepLoop } from '@noetic-tools/types';
|
|
5
|
+
import type { ItemSchemaRegistry, LayerStateStore } from './action-deps.js';
|
|
5
6
|
export declare function executeBranch<TMemory, I, O>(step: StepBranch<TMemory, I, O>, input: I, ctx: Context<TMemory>, executeStep: ExecuteStepFn): Promise<O>;
|
|
6
|
-
|
|
7
|
+
/** Layer-boundary wiring supplied by the interpreter (see `buildSpawnOpts`). */
|
|
8
|
+
export interface ExecuteForkOpts {
|
|
9
|
+
layerStore?: LayerStateStore;
|
|
10
|
+
itemSchemas?: ItemSchemaRegistry;
|
|
11
|
+
}
|
|
12
|
+
export declare function executeFork<TMemory, I, O>(step: StepFork<TMemory, I, O>, input: I, ctx: Context<TMemory>, rawExecuteStep: ExecuteStepFn, opts?: ExecuteForkOpts): Promise<O>;
|
|
7
13
|
export declare function executeLoop<TMemory, I, O>(step: StepLoop<TMemory, I, O>, input: I, ctx: Context<TMemory>, executeStep: ExecuteStepFn): Promise<O>;
|
|
8
14
|
/**
|
|
9
15
|
* Executes an `every` step: runs the body step forever, paced by `ms ± jitter`,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { createMessage, frameworkCast, isNoeticError, NoeticErrorImpl } from '@noetic-tools/types';
|
|
5
5
|
import { ContextImpl } from '../runtime/context-impl.js';
|
|
6
6
|
import { snapshotCwdState } from '../runtime/cwd-helpers.js';
|
|
7
|
+
import { contextToExecCtx, returnLayers, spawnLayers } from './action-deps.js';
|
|
7
8
|
import { cloneWithGuard } from './clone-guard.js';
|
|
8
9
|
import { getContextChannelStore, isContextImpl, isMutableContext } from './typeguards.js';
|
|
9
10
|
//#region branch
|
|
@@ -35,9 +36,83 @@ function createChildContexts(ctx, count, stepId) {
|
|
|
35
36
|
resourceId,
|
|
36
37
|
channelStore,
|
|
37
38
|
cwdState: snapshotCwdState(ctx),
|
|
39
|
+
// Layers and the harness tool pool cross the fork boundary: without
|
|
40
|
+
// them an `llm` step inside a path would run with no memory
|
|
41
|
+
// projection and no layer tools, and a nested `spawn` would have no
|
|
42
|
+
// parent layers to inherit. Per-path layer STATE is still isolated —
|
|
43
|
+
// see `createForkLayerBridge`.
|
|
44
|
+
layers: ctx.layers,
|
|
45
|
+
unifiedTools: ctx.unifiedTools,
|
|
38
46
|
}));
|
|
39
47
|
}
|
|
40
|
-
|
|
48
|
+
function createForkLayerBridge({ ctx, childContexts, opts, }) {
|
|
49
|
+
const layers = ctx.layers;
|
|
50
|
+
const layerStore = opts?.layerStore;
|
|
51
|
+
if (!layers || layers.length === 0 || !layerStore) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const parentExecCtx = contextToExecCtx(ctx);
|
|
55
|
+
const childExecCtxs = childContexts.map((child) => contextToExecCtx(child));
|
|
56
|
+
let tail = Promise.resolve();
|
|
57
|
+
function serialize(fn) {
|
|
58
|
+
const run = tail.then(fn, fn);
|
|
59
|
+
tail = run.then(() => undefined, () => undefined);
|
|
60
|
+
return run;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
async seed(index) {
|
|
64
|
+
await spawnLayers({
|
|
65
|
+
layers: layers ?? [],
|
|
66
|
+
parentCtx: parentExecCtx,
|
|
67
|
+
childCtx: childExecCtxs[index],
|
|
68
|
+
store: layerStore,
|
|
69
|
+
itemSchemas: opts?.itemSchemas,
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
settle(index, result) {
|
|
73
|
+
return serialize(() => returnLayers({
|
|
74
|
+
layers: layers ?? [],
|
|
75
|
+
parentCtx: parentExecCtx,
|
|
76
|
+
childCtx: childExecCtxs[index],
|
|
77
|
+
childLog: childContexts[index].itemLog,
|
|
78
|
+
result,
|
|
79
|
+
store: layerStore,
|
|
80
|
+
}));
|
|
81
|
+
},
|
|
82
|
+
cleanup(index) {
|
|
83
|
+
layerStore.cleanup(childExecCtxs[index].executionId);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Wraps the interpreter's `executeStep` so every forked path runs inside the
|
|
89
|
+
* layer child boundary. Contexts that are not one of this fork's children
|
|
90
|
+
* (nested steps within a path) pass straight through.
|
|
91
|
+
*/
|
|
92
|
+
function withForkLayers(executeStep, childContexts, bridge) {
|
|
93
|
+
if (!bridge) {
|
|
94
|
+
return executeStep;
|
|
95
|
+
}
|
|
96
|
+
const indexByContext = new Map(childContexts.map((child, i) => [
|
|
97
|
+
child,
|
|
98
|
+
i,
|
|
99
|
+
]));
|
|
100
|
+
return async (step, input, childCtx) => {
|
|
101
|
+
const index = indexByContext.get(frameworkCast(childCtx));
|
|
102
|
+
if (index === undefined) {
|
|
103
|
+
return executeStep(step, input, childCtx);
|
|
104
|
+
}
|
|
105
|
+
await bridge.seed(index);
|
|
106
|
+
try {
|
|
107
|
+
const output = await executeStep(step, input, childCtx);
|
|
108
|
+
return await bridge.settle(index, output);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
bridge.cleanup(index);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export async function executeFork(step, input, ctx, rawExecuteStep, opts) {
|
|
41
116
|
const baseCtx = frameworkCast(ctx);
|
|
42
117
|
const paths = step.paths(input, ctx);
|
|
43
118
|
if (paths.length === 0) {
|
|
@@ -56,6 +131,11 @@ export async function executeFork(step, input, ctx, executeStep) {
|
|
|
56
131
|
}
|
|
57
132
|
const childContexts = createChildContexts(baseCtx, paths.length, step.id);
|
|
58
133
|
const concurrency = step.concurrency ?? paths.length;
|
|
134
|
+
const executeStep = withForkLayers(rawExecuteStep, childContexts, createForkLayerBridge({
|
|
135
|
+
ctx: baseCtx,
|
|
136
|
+
childContexts,
|
|
137
|
+
opts,
|
|
138
|
+
}));
|
|
59
139
|
switch (step.mode) {
|
|
60
140
|
case 'all':
|
|
61
141
|
return executeAll(step, paths, input, ctx, childContexts, executeStep, concurrency);
|
|
@@ -194,7 +194,12 @@ export async function execute(step, input, ctx) {
|
|
|
194
194
|
result = await executeBranch(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
195
195
|
break;
|
|
196
196
|
case 'fork':
|
|
197
|
-
result = await executeFork(step, input, ctx, (s, i, c) => execute(s, i, c)
|
|
197
|
+
result = await executeFork(step, input, ctx, (s, i, c) => execute(s, i, c), {
|
|
198
|
+
// Fork paths are child executions: the layer store lets their
|
|
199
|
+
// `onSpawn`/`onReturn` boundary run (see `createForkLayerBridge`).
|
|
200
|
+
layerStore: harnessWithLayerStore(baseCtx).layerStateStore,
|
|
201
|
+
itemSchemas: baseCtx.itemSchemas,
|
|
202
|
+
});
|
|
198
203
|
break;
|
|
199
204
|
case 'spawn':
|
|
200
205
|
result = await dispatchViaAdapter(step, input, ctx, () => executeSpawn(step, input, ctx, (s, i, c) => execute(s, i, c), buildSpawnOpts(ctx)));
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
export declare const GenAI: {
|
|
2
|
-
readonly SYSTEM:
|
|
3
|
-
readonly REQUEST_MODEL:
|
|
4
|
-
readonly USAGE_INPUT_TOKENS:
|
|
5
|
-
readonly USAGE_OUTPUT_TOKENS:
|
|
6
|
-
readonly COST:
|
|
2
|
+
readonly SYSTEM: 'gen_ai.system';
|
|
3
|
+
readonly REQUEST_MODEL: 'gen_ai.request.model';
|
|
4
|
+
readonly USAGE_INPUT_TOKENS: 'gen_ai.usage.input_tokens';
|
|
5
|
+
readonly USAGE_OUTPUT_TOKENS: 'gen_ai.usage.output_tokens';
|
|
6
|
+
readonly COST: 'gen_ai.cost';
|
|
7
7
|
};
|
|
8
8
|
export declare const ToolAttr: {
|
|
9
|
-
readonly NAME:
|
|
10
|
-
readonly NEEDS_APPROVAL:
|
|
9
|
+
readonly NAME: 'tool.name';
|
|
10
|
+
readonly NEEDS_APPROVAL: 'tool.needs_approval';
|
|
11
11
|
};
|
|
12
12
|
export declare const NoeticAttr: {
|
|
13
13
|
/** Full JSON-serialised `WorkflowDocument` (the DAG, lossless). */
|
|
14
|
-
readonly WORKFLOW_DOCUMENT:
|
|
14
|
+
readonly WORKFLOW_DOCUMENT: 'noetic.workflow.document';
|
|
15
15
|
/** Workflow document schema version. */
|
|
16
|
-
readonly WORKFLOW_VERSION:
|
|
16
|
+
readonly WORKFLOW_VERSION: 'noetic.workflow.version';
|
|
17
17
|
/** Count of declared nodes in the workflow tree. */
|
|
18
|
-
readonly WORKFLOW_NODE_COUNT:
|
|
18
|
+
readonly WORKFLOW_NODE_COUNT: 'noetic.workflow.node_count';
|
|
19
19
|
/** JSON array of `{ id, kind }` for every declared node (flattened graph). */
|
|
20
|
-
readonly WORKFLOW_NODES:
|
|
20
|
+
readonly WORKFLOW_NODES: 'noetic.workflow.nodes';
|
|
21
21
|
/** JSON array of `{ from, to }` parent→child edges between declared nodes. */
|
|
22
|
-
readonly WORKFLOW_EDGES:
|
|
22
|
+
readonly WORKFLOW_EDGES: 'noetic.workflow.edges';
|
|
23
23
|
/** Id of the declared workflow node an `llm.call`/`tool.call` span belongs to. */
|
|
24
|
-
readonly NODE_ID:
|
|
24
|
+
readonly NODE_ID: 'noetic.node.id';
|
|
25
25
|
/**
|
|
26
26
|
* Conversation/session this run belongs to (the run's `ctx.threadId`). Stamped
|
|
27
27
|
* on the root `workflow.run` span so every turn of a multi-turn session shares
|
|
28
28
|
* one id, letting a consumer group per-run traces back into their session.
|
|
29
29
|
*/
|
|
30
|
-
readonly SESSION_ID:
|
|
30
|
+
readonly SESSION_ID: 'noetic.session.id';
|
|
31
31
|
/** Resource the session is scoped to (the run's `ctx.resourceId`), when set. */
|
|
32
|
-
readonly RESOURCE_ID:
|
|
32
|
+
readonly RESOURCE_ID: 'noetic.resource.id';
|
|
33
33
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Pattern that lets an LLM generate a workflow as JSON during execution,
|
|
3
3
|
* then hydrates and runs it within the same harness session.
|
|
4
4
|
*/
|
|
5
|
-
import type { ContextMemory } from '@noetic-tools/memory';
|
|
5
|
+
import type { ContextMemory, MemoryLayer } from '@noetic-tools/memory';
|
|
6
6
|
import type { AgentHarnessContract, Context, Step, Tool } from '@noetic-tools/types';
|
|
7
7
|
/** @public Options for creating a dynamic workflow step. */
|
|
8
8
|
export interface DynamicWorkflowOpts {
|
|
@@ -34,6 +34,12 @@ export interface ParseAndRunWorkflowOpts {
|
|
|
34
34
|
tools: Tool[];
|
|
35
35
|
input?: string;
|
|
36
36
|
maxDepth?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Memory layers the document's `provide` / `spawn` nodes may reference by name.
|
|
39
|
+
* Without a registry those nodes resolve to NO layers rather than failing, so a
|
|
40
|
+
* host that runs layer-bearing workflows must pass its layers here.
|
|
41
|
+
*/
|
|
42
|
+
layers?: ReadonlyMap<string, MemoryLayer>;
|
|
37
43
|
}
|
|
38
44
|
/**
|
|
39
45
|
* Parses raw JSON into a `WorkflowDocument`, hydrates it into a live step
|
|
@@ -142,6 +142,7 @@ export async function parseAndRunWorkflow(opts) {
|
|
|
142
142
|
const hydrationCtx = {
|
|
143
143
|
tools: buildToolMap(opts.tools),
|
|
144
144
|
executeStep,
|
|
145
|
+
layers: opts.layers,
|
|
145
146
|
};
|
|
146
147
|
const hydrated = hydrateWorkflow(parseResult.doc, hydrationCtx);
|
|
147
148
|
const runSpan = beginWorkflowRunSpan(opts.harness, opts.ctx, parseResult.doc);
|
|
@@ -44,17 +44,17 @@ export type UntilPredicate = {
|
|
|
44
44
|
export declare const UntilPredicateSchema: z.ZodType<UntilPredicate>;
|
|
45
45
|
/** @public ESM literal enum for merge strategies. */
|
|
46
46
|
export declare const MergeStrategy: {
|
|
47
|
-
readonly Last:
|
|
48
|
-
readonly First:
|
|
49
|
-
readonly Concat:
|
|
47
|
+
readonly Last: 'last';
|
|
48
|
+
readonly First: 'first';
|
|
49
|
+
readonly Concat: 'concat';
|
|
50
50
|
};
|
|
51
51
|
/** @public Named merge strategy for fork nodes. */
|
|
52
52
|
export type MergeStrategy = (typeof MergeStrategy)[keyof typeof MergeStrategy];
|
|
53
53
|
/** @public Zod schema for the named merge strategy used by `fork` nodes. */
|
|
54
54
|
export declare const MergeStrategySchema: z.ZodEnum<{
|
|
55
55
|
concat: "concat";
|
|
56
|
-
last: "last";
|
|
57
56
|
first: "first";
|
|
57
|
+
last: "last";
|
|
58
58
|
}>;
|
|
59
59
|
declare const ModelParamsSchema: z.ZodObject<{
|
|
60
60
|
temperature: z.ZodOptional<z.ZodNumber>;
|
|
@@ -84,9 +84,9 @@ declare const LlmToolEntrySchema: z.ZodObject<{
|
|
|
84
84
|
declare const RetryPolicySchema: z.ZodObject<{
|
|
85
85
|
maxAttempts: z.ZodNumber;
|
|
86
86
|
backoff: z.ZodEnum<{
|
|
87
|
+
exponential: "exponential";
|
|
87
88
|
fixed: "fixed";
|
|
88
89
|
linear: "linear";
|
|
89
|
-
exponential: "exponential";
|
|
90
90
|
}>;
|
|
91
91
|
initialDelay: z.ZodNumber;
|
|
92
92
|
maxDelay: z.ZodOptional<z.ZodNumber>;
|
|
@@ -94,10 +94,10 @@ declare const RetryPolicySchema: z.ZodObject<{
|
|
|
94
94
|
declare const HarnessSettingsSchema: z.ZodObject<{
|
|
95
95
|
model: z.ZodOptional<z.ZodString>;
|
|
96
96
|
permissionMode: z.ZodOptional<z.ZodEnum<{
|
|
97
|
-
default: "default";
|
|
98
|
-
plan: "plan";
|
|
99
97
|
acceptEdits: "acceptEdits";
|
|
100
98
|
bypassPermissions: "bypassPermissions";
|
|
99
|
+
default: "default";
|
|
100
|
+
plan: "plan";
|
|
101
101
|
}>>;
|
|
102
102
|
maxTurns: z.ZodOptional<z.ZodNumber>;
|
|
103
103
|
allowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -106,9 +106,9 @@ declare const HarnessSettingsSchema: z.ZodObject<{
|
|
|
106
106
|
declare const HarnessSessionPolicySchema: z.ZodObject<{
|
|
107
107
|
reuse: z.ZodOptional<z.ZodString>;
|
|
108
108
|
onComplete: z.ZodOptional<z.ZodEnum<{
|
|
109
|
-
stop: "stop";
|
|
110
|
-
detach: "detach";
|
|
111
109
|
destroy: "destroy";
|
|
110
|
+
detach: "detach";
|
|
111
|
+
stop: "stop";
|
|
112
112
|
}>>;
|
|
113
113
|
}, z.core.$strip>;
|
|
114
114
|
interface WorkflowNodeBase {
|
|
@@ -200,6 +200,13 @@ export interface SpawnWorkflowNode extends WorkflowNodeBase {
|
|
|
200
200
|
kind: 'spawn';
|
|
201
201
|
child: WorkflowNode;
|
|
202
202
|
timeout?: number;
|
|
203
|
+
/**
|
|
204
|
+
* Memory layers the child runs with, by registered name (resolved from
|
|
205
|
+
* `HydrationContext.layers`, like `provide`). Omit to inherit the parent's
|
|
206
|
+
* layers — the default spawn behaviour. Naming layers here REPLACES the
|
|
207
|
+
* inherited set for the child, so list every layer the child needs.
|
|
208
|
+
*/
|
|
209
|
+
layers?: string[];
|
|
203
210
|
}
|
|
204
211
|
export interface ProvideWorkflowNode extends WorkflowNodeBase {
|
|
205
212
|
kind: 'provide';
|
package/dist/schemas/workflow.js
CHANGED
|
@@ -210,6 +210,7 @@ const SpawnNodeSchema = z.object({
|
|
|
210
210
|
...SHARED_FIELDS,
|
|
211
211
|
child: WorkflowNodeRef,
|
|
212
212
|
timeout: z.number().positive().optional(),
|
|
213
|
+
layers: z.array(z.string().min(1)).min(1).optional(),
|
|
213
214
|
});
|
|
214
215
|
const ProvideNodeSchema = z.object({
|
|
215
216
|
kind: z.literal('provide'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noetic-tools/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Step primitives, interpreter, runtime, and memory layer contract for Noetic agents.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@noetic-tools/memory": "^1.0
|
|
70
|
-
"@noetic-tools/types": "^1.
|
|
69
|
+
"@noetic-tools/memory": "^1.1.0",
|
|
70
|
+
"@noetic-tools/types": "^1.4.0",
|
|
71
71
|
"@openrouter/agent": "^0.6.0",
|
|
72
72
|
"zod": "^4.0.0"
|
|
73
73
|
},
|