@noetic-tools/core 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -11
- package/dist/adapters/in-memory-fs-adapter.d.ts +1 -1
- package/dist/adapters/in-memory-fs-adapter.js +1 -1
- package/dist/adapters/in-memory-shell-adapter.d.ts +1 -1
- package/dist/adapters/in-memory-subprocess/manifest-persistence.d.ts +8 -0
- package/dist/adapters/in-memory-subprocess/manifest-persistence.js +56 -0
- package/dist/adapters/in-memory-subprocess/metadata.d.ts +12 -0
- package/dist/adapters/in-memory-subprocess/metadata.js +58 -0
- package/dist/adapters/in-memory-subprocess/step-completion.d.ts +3 -0
- package/dist/adapters/in-memory-subprocess/step-completion.js +86 -0
- package/dist/adapters/in-memory-subprocess/types.d.ts +88 -0
- package/dist/adapters/in-memory-subprocess/types.js +2 -0
- package/dist/adapters/in-memory-subprocess-adapter.d.ts +12 -4
- package/dist/adapters/in-memory-subprocess-adapter.js +164 -55
- package/dist/adapters/openrouter.d.ts +16 -7
- package/dist/adapters/openrouter.js +68 -7
- package/dist/builders/channel-builder.d.ts +1 -1
- package/dist/builders/control-flow-builders.d.ts +3 -3
- package/dist/builders/control-flow-builders.js +9 -4
- package/dist/builders/every.d.ts +3 -3
- package/dist/builders/every.js +5 -2
- package/dist/builders/layer-provides-builders.d.ts +1 -32
- package/dist/builders/layer-provides-builders.js +1 -33
- package/dist/builders/loop-builder.d.ts +2 -2
- package/dist/builders/loop-builder.js +5 -2
- package/dist/builders/memory-builder.d.ts +1 -1
- package/dist/builders/memory-builder.js +1 -1
- package/dist/builders/provide-builder.d.ts +2 -2
- package/dist/builders/provide-builder.js +5 -2
- package/dist/builders/spawn-builder.d.ts +22 -9
- package/dist/builders/spawn-builder.js +17 -3
- package/dist/builders/step-builders.d.ts +51 -30
- package/dist/builders/step-builders.js +31 -12
- package/dist/builders/tool-builder.d.ts +7 -3
- package/dist/builders/tool-builder.js +3 -1
- package/dist/builders/workflow-hydrator.d.ts +38 -0
- package/dist/builders/workflow-hydrator.js +336 -0
- package/dist/conditions/conditions.d.ts +4 -4
- package/dist/conditions/conditions.js +2 -2
- package/dist/errors/noetic-error.d.ts +1 -1
- package/dist/errors/noetic-error.js +2 -0
- package/dist/{runtime → harness}/agent-harness.d.ts +65 -45
- package/dist/harness/agent-harness.js +576 -0
- package/dist/harness/deps/adapters.d.ts +4 -0
- package/dist/harness/deps/adapters.js +4 -0
- package/dist/harness/deps/interpreter.d.ts +5 -0
- package/dist/harness/deps/interpreter.js +4 -0
- package/dist/harness/deps/memory.d.ts +5 -0
- package/dist/harness/deps/memory.js +4 -0
- package/dist/harness/deps/runtime.d.ts +10 -0
- package/dist/harness/deps/runtime.js +7 -0
- package/dist/harness/deps/types.d.ts +17 -0
- package/dist/harness/deps/types.js +1 -0
- package/dist/harness/model-call.d.ts +50 -0
- package/dist/harness/model-call.js +694 -0
- package/dist/harness/model-schema.d.ts +19 -0
- package/dist/harness/model-schema.js +46 -0
- package/dist/harness/step-bootstrap.d.ts +25 -0
- package/dist/harness/step-bootstrap.js +170 -0
- package/dist/index.d.ts +114 -76
- package/dist/index.js +74 -50
- package/dist/internal-test.d.ts +4 -4
- package/dist/internal-test.js +4 -4
- package/dist/interpreter/action-deps.d.ts +12 -0
- package/dist/interpreter/action-deps.js +10 -0
- package/dist/interpreter/action-types.d.ts +8 -0
- package/dist/interpreter/action-types.js +1 -0
- package/dist/interpreter/collect-tools.d.ts +2 -2
- package/dist/interpreter/collect-tools.js +6 -2
- package/dist/interpreter/execute-action.d.ts +22 -0
- package/dist/interpreter/execute-action.js +527 -0
- package/dist/interpreter/{execute-every.d.ts → execute-control.d.ts} +8 -2
- package/dist/interpreter/execute-control.js +530 -0
- package/dist/interpreter/execute.d.ts +16 -3
- package/dist/interpreter/execute.js +154 -51
- package/dist/interpreter/message-helpers.d.ts +1 -9
- package/dist/interpreter/message-helpers.js +1 -42
- package/dist/interpreter/step-dispatcher.d.ts +61 -0
- package/dist/interpreter/step-dispatcher.js +67 -0
- package/dist/interpreter/typeguards.d.ts +8 -15
- package/dist/interpreter/typeguards.js +4 -21
- package/dist/memory/budget.d.ts +2 -2
- package/dist/memory/budget.js +1 -1
- package/dist/{runtime → memory}/exec-context-factory.d.ts +2 -2
- package/dist/{runtime → memory}/exec-context-factory.js +1 -1
- package/dist/memory/flow-schema.d.ts +52 -0
- package/dist/memory/flow-schema.js +102 -0
- package/dist/memory/function-call-utils.d.ts +1 -1
- package/dist/memory/function-call-utils.js +4 -2
- package/dist/memory/layer-api.d.ts +4 -4
- package/dist/memory/layer-api.js +2 -2
- package/dist/memory/layer-lifecycle.d.ts +6 -6
- package/dist/memory/layer-lifecycle.js +6 -6
- package/dist/memory/layer-provides.d.ts +32 -0
- package/dist/memory/layer-provides.js +33 -0
- package/dist/memory/layer-usage.d.ts +4 -3
- package/dist/memory/layer-usage.js +9 -3
- package/dist/memory/layers/durable-task-state.d.ts +8 -8
- package/dist/memory/layers/durable-task-state.js +2 -2
- package/dist/memory/layers/file-reference.d.ts +1 -1
- package/dist/memory/layers/file-reference.js +303 -247
- package/dist/memory/layers/history-window.d.ts +1 -1
- package/dist/memory/layers/history-window.js +4 -4
- package/dist/memory/layers/observational-memory.d.ts +5 -5
- package/dist/memory/layers/observational-memory.js +2 -3
- package/dist/memory/layers/plan.d.ts +2 -2
- package/dist/memory/layers/plan.js +15 -10
- package/dist/memory/layers/static-content.d.ts +2 -2
- package/dist/memory/layers/static-content.js +1 -1
- package/dist/memory/layers/steering.d.ts +7 -7
- package/dist/memory/layers/steering.js +5 -5
- package/dist/memory/layers/tool-memory-layer.d.ts +2 -2
- package/dist/memory/layers/tool-memory-layer.js +1 -1
- package/dist/memory/layers/working-memory.d.ts +7 -7
- package/dist/memory/layers/working-memory.js +4 -4
- package/dist/memory/projector.d.ts +2 -2
- package/dist/memory/scope.d.ts +1 -1
- package/dist/memory/strip-unresolved.d.ts +1 -1
- package/dist/observability/span-impl.d.ts +1 -1
- package/dist/observability/trace-exporter.d.ts +2 -2
- package/dist/observability/trace-exporter.js +1 -1
- package/dist/patterns/dynamic-workflow.d.ts +55 -0
- package/dist/patterns/dynamic-workflow.js +194 -0
- package/dist/patterns/flow.d.ts +2 -52
- package/dist/patterns/flow.js +1 -103
- package/dist/patterns/interview.d.ts +2 -2
- package/dist/patterns/interview.js +5 -5
- package/dist/patterns/plans.d.ts +6 -6
- package/dist/patterns/plans.js +4 -4
- package/dist/patterns/ralph-wiggum.d.ts +4 -4
- package/dist/patterns/ralph-wiggum.js +5 -5
- package/dist/patterns/react.d.ts +3 -3
- package/dist/patterns/react.js +5 -5
- package/dist/portable/adapters.d.ts +6 -0
- package/dist/portable/adapters.js +6 -0
- package/dist/portable/ask-user.d.ts +4 -0
- package/dist/portable/ask-user.js +2 -0
- package/dist/portable/builders.d.ts +12 -0
- package/dist/portable/builders.js +12 -0
- package/dist/portable/memory-layers.d.ts +12 -0
- package/dist/portable/memory-layers.js +12 -0
- package/dist/portable/runtime.d.ts +4 -0
- package/dist/portable/runtime.js +4 -0
- package/dist/portable/until.d.ts +4 -0
- package/dist/portable/until.js +4 -0
- package/dist/portable.d.ts +8 -26
- package/dist/portable.js +8 -34
- package/dist/runtime/broadcaster-utils.d.ts +7 -3
- package/dist/runtime/broadcaster-utils.js +11 -6
- package/dist/runtime/channel-store.d.ts +1 -1
- package/dist/runtime/channel-store.js +2 -2
- package/dist/runtime/context-deps.d.ts +4 -0
- package/dist/runtime/context-deps.js +2 -0
- package/dist/runtime/context-impl.d.ts +42 -12
- package/dist/runtime/context-impl.js +52 -5
- package/dist/runtime/context-type-deps.d.ts +14 -0
- package/dist/runtime/context-type-deps.js +1 -0
- package/dist/runtime/cwd-helpers.d.ts +1 -1
- package/dist/runtime/detached-handle.d.ts +52 -4
- package/dist/runtime/detached-handle.js +218 -14
- package/dist/runtime/durable/checkpoint-store.d.ts +38 -0
- package/dist/runtime/durable/checkpoint-store.js +101 -0
- package/dist/runtime/durable/detached-signal.d.ts +37 -0
- package/dist/runtime/durable/detached-signal.js +52 -0
- package/dist/runtime/durable/harness-checkpoints.d.ts +51 -0
- package/dist/runtime/durable/harness-checkpoints.js +104 -0
- package/dist/runtime/durable/index.d.ts +10 -0
- package/dist/runtime/durable/index.js +5 -0
- package/dist/runtime/durable/runnable-loop.d.ts +100 -0
- package/dist/runtime/durable/runnable-loop.js +90 -0
- package/dist/runtime/durable/session-seed.d.ts +28 -0
- package/dist/runtime/durable/session-seed.js +27 -0
- package/dist/runtime/durable/stall-nudge.d.ts +82 -0
- package/dist/runtime/durable/stall-nudge.js +87 -0
- package/dist/runtime/event-broadcaster.d.ts +1 -1
- package/dist/runtime/in-memory-storage.d.ts +1 -1
- package/dist/runtime/in-memory-storage.js +1 -1
- package/dist/runtime/item-log-impl.d.ts +3 -3
- package/dist/runtime/item-log-impl.js +1 -1
- package/dist/runtime/message-queue.d.ts +2 -2
- package/dist/runtime/session-runner.d.ts +7 -7
- package/dist/runtime/session-runner.js +3 -3
- package/dist/runtime/session-streams.d.ts +3 -3
- package/dist/runtime/session-streams.js +1 -1
- package/dist/runtime/step-registry.d.ts +42 -0
- package/dist/runtime/step-registry.js +77 -0
- package/dist/runtime/tool-memory.d.ts +3 -3
- package/dist/schemas/item.d.ts +1 -1
- package/dist/schemas/workflow.d.ts +139 -0
- package/dist/schemas/workflow.js +232 -0
- package/dist/types/ask-user-types.d.ts +57 -0
- package/dist/types/ask-user-types.js +74 -0
- package/dist/types/checkpoint.d.ts +98 -0
- package/dist/types/checkpoint.js +81 -0
- package/dist/types/common.d.ts +1 -50
- package/dist/types/context-parts/item-log.d.ts +6 -0
- package/dist/types/context-parts/item-log.js +1 -0
- package/dist/types/context-parts/layer-usage.d.ts +18 -0
- package/dist/types/context-parts/layer-usage.js +1 -0
- package/dist/types/context.d.ts +140 -31
- package/dist/types/error.d.ts +15 -0
- package/dist/types/harness-result.d.ts +2 -2
- package/dist/types/items.d.ts +18 -2
- package/dist/types/memory-context.d.ts +39 -0
- package/dist/types/memory-context.js +1 -0
- package/dist/types/memory.d.ts +38 -68
- package/dist/types/mutable-context.d.ts +3 -3
- package/dist/types/runtime.d.ts +14 -13
- package/dist/types/steering.d.ts +2 -2
- package/dist/types/step-registrar.d.ts +46 -0
- package/dist/types/step-registrar.js +56 -0
- package/dist/types/step.d.ts +34 -9
- package/dist/types/subprocess-adapter.d.ts +119 -3
- package/dist/types/subprocess-adapter.js +2 -0
- package/dist/types/tool-context.d.ts +6 -6
- package/dist/types/tool.d.ts +64 -0
- package/dist/types/tool.js +1 -0
- package/dist/unstable.d.ts +9 -9
- package/dist/unstable.js +7 -7
- package/dist/until/combinators.d.ts +1 -1
- package/dist/until/never.d.ts +1 -1
- package/dist/until/predicates.d.ts +13 -4
- package/dist/until/predicates.js +16 -2
- package/dist/util/message-helpers.d.ts +17 -0
- package/dist/util/message-helpers.js +64 -0
- package/package.json +6 -4
- package/dist/adapters/local-fs-adapter.d.ts +0 -3
- package/dist/adapters/local-fs-adapter.js +0 -43
- package/dist/adapters/local-shell-adapter.d.ts +0 -51
- package/dist/adapters/local-shell-adapter.js +0 -167
- package/dist/adapters/local-subprocess-adapter.d.ts +0 -16
- package/dist/adapters/local-subprocess-adapter.js +0 -187
- package/dist/conditions/index.d.ts +0 -3
- package/dist/conditions/index.js +0 -2
- package/dist/interpreter/execute-branch.d.ts +0 -3
- package/dist/interpreter/execute-branch.js +0 -10
- package/dist/interpreter/execute-every.js +0 -143
- package/dist/interpreter/execute-fork.d.ts +0 -3
- package/dist/interpreter/execute-fork.js +0 -215
- package/dist/interpreter/execute-llm.d.ts +0 -4
- package/dist/interpreter/execute-llm.js +0 -217
- package/dist/interpreter/execute-loop.d.ts +0 -3
- package/dist/interpreter/execute-loop.js +0 -171
- package/dist/interpreter/execute-provide.d.ts +0 -10
- package/dist/interpreter/execute-provide.js +0 -64
- package/dist/interpreter/execute-run.d.ts +0 -3
- package/dist/interpreter/execute-run.js +0 -39
- package/dist/interpreter/execute-spawn.d.ts +0 -11
- package/dist/interpreter/execute-spawn.js +0 -113
- package/dist/interpreter/execute-tool.d.ts +0 -5
- package/dist/interpreter/execute-tool.js +0 -81
- package/dist/runtime/agent-harness.js +0 -1170
- /package/dist/{interpreter → util}/framework-cast.d.ts +0 -0
- /package/dist/{interpreter → util}/framework-cast.js +0 -0
|
@@ -1,17 +1,104 @@
|
|
|
1
|
-
import { NoeticErrorImpl } from '../errors/noetic-error';
|
|
2
|
-
import { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { executeLLM } from './execute-
|
|
7
|
-
import { executeLoop } from './execute-
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import { executeSpawn } from './execute-spawn';
|
|
11
|
-
import { executeTool } from './execute-tool';
|
|
12
|
-
import { frameworkCast } from './framework-cast';
|
|
13
|
-
import { isMutableContext } from './typeguards';
|
|
1
|
+
import { NoeticErrorImpl } from '../errors/noetic-error.js';
|
|
2
|
+
import { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils.js';
|
|
3
|
+
import { ContextImpl } from '../runtime/context-impl.js';
|
|
4
|
+
import { DetachedHandleImpl } from '../runtime/detached-handle.js';
|
|
5
|
+
import { frameworkCast } from '../util/framework-cast.js';
|
|
6
|
+
import { executeLLM, executeProvide, executeRun, executeSpawn, executeTool, } from './execute-action.js';
|
|
7
|
+
import { executeBranch, executeEvery, executeFork, executeLoop } from './execute-control.js';
|
|
8
|
+
import { isMutableContext } from './typeguards.js';
|
|
9
|
+
//#region Constants
|
|
14
10
|
const MAX_DEPTH = 64;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region Helpers
|
|
13
|
+
/**
|
|
14
|
+
* Return the underlying `ContextImpl` when the context is one we produced
|
|
15
|
+
* in-process. Out-of-process descendants reach us through the adapter
|
|
16
|
+
* boundary and are not `ContextImpl` instances, so frontier bookkeeping is
|
|
17
|
+
* a no-op for them.
|
|
18
|
+
*/
|
|
19
|
+
function asContextImpl(ctx) {
|
|
20
|
+
return ctx instanceof ContextImpl ? ctx : null;
|
|
21
|
+
}
|
|
22
|
+
function harnessWithLayerStore(ctx) {
|
|
23
|
+
return frameworkCast(ctx.harness);
|
|
24
|
+
}
|
|
25
|
+
function buildSpawnOpts(ctx) {
|
|
26
|
+
const baseCtx = frameworkCast(ctx);
|
|
27
|
+
return {
|
|
28
|
+
itemSchemas: baseCtx.itemSchemas,
|
|
29
|
+
parentLayers: baseCtx.layers,
|
|
30
|
+
layerStore: harnessWithLayerStore(baseCtx).layerStateStore,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the subprocess adapter that should handle dispatch for this step.
|
|
35
|
+
* Precedence is `step.subprocess ?? ctx.harness.subprocess`. Per-call
|
|
36
|
+
* `detachedSpawn` overrides layer above this in the harness itself.
|
|
37
|
+
*/
|
|
38
|
+
function resolveStepAdapter(step, ctx) {
|
|
39
|
+
if ((step.kind === 'run' || step.kind === 'spawn') && step.subprocess) {
|
|
40
|
+
return step.subprocess;
|
|
41
|
+
}
|
|
42
|
+
return ctx.harness.subprocess;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Dispatch a run/spawn step through the resolved subprocess adapter.
|
|
46
|
+
*
|
|
47
|
+
* The `_localExecutor` closure carries the in-process fallback path — the
|
|
48
|
+
* in-memory adapter invokes it directly, preserving the pre-refactor
|
|
49
|
+
* dispatch semantics including parent-context inheritance, layer lifecycle,
|
|
50
|
+
* and synchronous error propagation. Out-of-process adapters ignore
|
|
51
|
+
* `_localExecutor` and run the step in a child runtime.
|
|
52
|
+
*/
|
|
53
|
+
async function dispatchViaAdapter(step, input, ctx, executor) {
|
|
54
|
+
const adapter = resolveStepAdapter(step, ctx);
|
|
55
|
+
const request = {
|
|
56
|
+
kind: 'step',
|
|
57
|
+
stepId: step.id,
|
|
58
|
+
serializedInput: input,
|
|
59
|
+
executionId: ctx.id,
|
|
60
|
+
overrides: {
|
|
61
|
+
threadId: ctx.threadId,
|
|
62
|
+
resourceId: ctx.resourceId,
|
|
63
|
+
},
|
|
64
|
+
_localExecutor: () => executor(),
|
|
65
|
+
};
|
|
66
|
+
const spawnPromise = adapter.spawn(request);
|
|
67
|
+
const handle = new DetachedHandleImpl({
|
|
68
|
+
id: ctx.id,
|
|
69
|
+
stepId: step.id,
|
|
70
|
+
adapter,
|
|
71
|
+
spawnPromise,
|
|
72
|
+
});
|
|
73
|
+
return handle.await();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @internal
|
|
77
|
+
* Dispatch a step at the per-kind handler level without adapter routing.
|
|
78
|
+
*
|
|
79
|
+
* Used as the top-level `_localExecutor` for `StepRun` / `StepSpawn`
|
|
80
|
+
* dispatches: the outer adapter call already recorded the request, so
|
|
81
|
+
* running the handler directly avoids a re-entrant adapter round-trip
|
|
82
|
+
* inside the same logical step. Nested step dispatches (e.g. `executeSpawn`
|
|
83
|
+
* descending into `step.child`) still go through `execute()` and route
|
|
84
|
+
* through the adapter as normal, so per-step overrides on descendants
|
|
85
|
+
* continue to work.
|
|
86
|
+
*/
|
|
87
|
+
export async function executeNoAdapter(step, input, ctx) {
|
|
88
|
+
switch (step.kind) {
|
|
89
|
+
case 'run':
|
|
90
|
+
return executeRun(step, input, ctx);
|
|
91
|
+
case 'spawn':
|
|
92
|
+
return executeSpawn(step, input, ctx, (s, i, c) => execute(s, i, c), buildSpawnOpts(ctx));
|
|
93
|
+
default:
|
|
94
|
+
// Kinds that don't currently route through the adapter (llm, tool,
|
|
95
|
+
// branch, fork, provide, loop, every). Delegate back to `execute()`
|
|
96
|
+
// so they exercise the normal dispatch table, framework-event emits,
|
|
97
|
+
// abort checks, and depth guard.
|
|
98
|
+
return execute(step, input, ctx);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
15
102
|
/**
|
|
16
103
|
* Executes a step within the interpreter, dispatching to the appropriate handler by step kind.
|
|
17
104
|
*
|
|
@@ -61,45 +148,61 @@ export async function execute(step, input, ctx) {
|
|
|
61
148
|
data: startedData,
|
|
62
149
|
});
|
|
63
150
|
}
|
|
151
|
+
// Push onto the durable frontier so checkpoints can record exactly which
|
|
152
|
+
// step was in flight when the snapshot fired. Steps with empty ids are
|
|
153
|
+
// skipped — they are not reattachable by id and would pollute the stack.
|
|
154
|
+
const impl = asContextImpl(baseCtx);
|
|
155
|
+
if (impl && step.id.length > 0) {
|
|
156
|
+
impl.enterStep({
|
|
157
|
+
stepId: step.id,
|
|
158
|
+
input,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
64
161
|
let result;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
162
|
+
try {
|
|
163
|
+
switch (step.kind) {
|
|
164
|
+
case 'run':
|
|
165
|
+
result = await dispatchViaAdapter(step, input, ctx, () => executeRun(step, input, ctx));
|
|
166
|
+
break;
|
|
167
|
+
case 'llm':
|
|
168
|
+
result = await executeLLM(step, input, ctx, baseCtx.layers);
|
|
169
|
+
break;
|
|
170
|
+
case 'tool':
|
|
171
|
+
result = await executeTool(step, input, ctx, baseCtx.harness);
|
|
172
|
+
break;
|
|
173
|
+
case 'branch':
|
|
174
|
+
result = await executeBranch(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
175
|
+
break;
|
|
176
|
+
case 'fork':
|
|
177
|
+
result = await executeFork(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
178
|
+
break;
|
|
179
|
+
case 'spawn':
|
|
180
|
+
result = await dispatchViaAdapter(step, input, ctx, () => executeSpawn(step, input, ctx, (s, i, c) => execute(s, i, c), buildSpawnOpts(ctx)));
|
|
181
|
+
break;
|
|
182
|
+
case 'provide':
|
|
183
|
+
result = await executeProvide(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
184
|
+
break;
|
|
185
|
+
case 'loop':
|
|
186
|
+
result = await executeLoop(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
187
|
+
break;
|
|
188
|
+
case 'every':
|
|
189
|
+
result = await executeEvery(step, input, ctx, (s, i, c) => execute(s, i, c));
|
|
190
|
+
break;
|
|
191
|
+
default: {
|
|
192
|
+
const _exhaustive = step;
|
|
193
|
+
void _exhaustive;
|
|
194
|
+
throw new NoeticErrorImpl({
|
|
195
|
+
kind: 'step_failed',
|
|
196
|
+
stepId: 'unknown',
|
|
197
|
+
cause: new Error('Unknown step kind'),
|
|
198
|
+
retriesExhausted: false,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
if (impl && step.id.length > 0) {
|
|
205
|
+
impl.leaveStep(step.id);
|
|
103
206
|
}
|
|
104
207
|
}
|
|
105
208
|
// Emit step_completed framework event (respects step.emit option)
|
|
@@ -1,9 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Context } from '../types/context';
|
|
3
|
-
import type { InputMessageItem, Item } from '../types/items';
|
|
4
|
-
export declare function createMessage(text: string, role: 'user' | 'developer'): InputMessageItem;
|
|
5
|
-
export declare function extractAssistantText(items: ReadonlyArray<Item>): string;
|
|
6
|
-
/** Naive token estimate: ~4 chars per token */
|
|
7
|
-
export declare function estimateTokens(text: string): number;
|
|
8
|
-
/** Accumulate token usage and cost from an LLM response onto the context. */
|
|
9
|
-
export declare function trackUsage(ctx: Context, response: LLMResponse): void;
|
|
1
|
+
export { trackUsage } from '../util/message-helpers.js';
|
|
@@ -1,42 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export function createMessage(text, role) {
|
|
3
|
-
return {
|
|
4
|
-
id: crypto.randomUUID(),
|
|
5
|
-
status: 'completed',
|
|
6
|
-
type: 'message',
|
|
7
|
-
role,
|
|
8
|
-
content: [
|
|
9
|
-
{
|
|
10
|
-
type: 'input_text',
|
|
11
|
-
text,
|
|
12
|
-
},
|
|
13
|
-
],
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
export function extractAssistantText(items) {
|
|
17
|
-
const lastMsg = items.findLast(isAssistantMessage);
|
|
18
|
-
if (!lastMsg) {
|
|
19
|
-
return '';
|
|
20
|
-
}
|
|
21
|
-
return (lastMsg.content
|
|
22
|
-
?.filter(isOutputText)
|
|
23
|
-
?.map((c) => c.text)
|
|
24
|
-
?.join('') ?? '');
|
|
25
|
-
}
|
|
26
|
-
/** Naive token estimate: ~4 chars per token */
|
|
27
|
-
export function estimateTokens(text) {
|
|
28
|
-
return Math.ceil(text.length / 4);
|
|
29
|
-
}
|
|
30
|
-
/** Accumulate token usage and cost from an LLM response onto the context. */
|
|
31
|
-
export function trackUsage(ctx, response) {
|
|
32
|
-
if (!isMutableContext(ctx)) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
ctx.tokens.input += response.usage.inputTokens;
|
|
36
|
-
ctx.tokens.output += response.usage.outputTokens;
|
|
37
|
-
ctx.tokens.total += response.usage.inputTokens + response.usage.outputTokens;
|
|
38
|
-
ctx.tokens.cached = (ctx.tokens.cached ?? 0) + (response.usage.cachedTokens ?? 0);
|
|
39
|
-
if (response.cost) {
|
|
40
|
-
ctx.cost = ctx.cost + response.cost;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
export { trackUsage } from '../util/message-helpers.js';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Context } from '../types/context.js';
|
|
2
|
+
import type { DetachedHandle } from '../types/detached.js';
|
|
3
|
+
import type { ContextMemory } from '../types/memory.js';
|
|
4
|
+
import type { Step } from '../types/step.js';
|
|
5
|
+
import type { SubprocessAdapter } from '../types/subprocess-adapter.js';
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the subprocess adapter used for a given step dispatch.
|
|
8
|
+
*
|
|
9
|
+
* Precedence: `detachedSpawn-overrides.subprocess ?? step.subprocess ??
|
|
10
|
+
* harness.subprocess`. Only `StepRun` and `StepSpawn` currently carry a
|
|
11
|
+
* `subprocess` field — other variants always fall through to the harness
|
|
12
|
+
* default.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveStepAdapter<TMemory, I, O>(step: Step<TMemory, I, O>, callOverride: SubprocessAdapter | undefined, fallback: SubprocessAdapter): SubprocessAdapter;
|
|
17
|
+
/**
|
|
18
|
+
* Minimum harness surface the dispatcher needs. `AgentHarness` satisfies
|
|
19
|
+
* this shape — kept structural so tests can pass a stub.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export interface StepDispatchHandle {
|
|
24
|
+
readonly subprocess: SubprocessAdapter;
|
|
25
|
+
createContext(opts?: {
|
|
26
|
+
parent?: Context;
|
|
27
|
+
threadId?: string;
|
|
28
|
+
resourceId?: string;
|
|
29
|
+
cwdInit?: string;
|
|
30
|
+
}): Context;
|
|
31
|
+
}
|
|
32
|
+
export interface DetachedSpawnOverrides {
|
|
33
|
+
/** Override the child's thread id. Default inherits from `parentCtx.threadId`. */
|
|
34
|
+
threadId?: string;
|
|
35
|
+
/** Override the child's resource id. Default inherits from `parentCtx.resourceId`. */
|
|
36
|
+
resourceId?: string;
|
|
37
|
+
/** Override the child's initial cwd. Used by worktree isolation to root the child at the worktree path. */
|
|
38
|
+
cwdInit?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Per-call subprocess adapter override. Takes precedence over both
|
|
41
|
+
* `step.subprocess` and `harness.subprocess`. Pass a custom adapter to
|
|
42
|
+
* dispatch this specific spawn through a different runtime (e.g. a
|
|
43
|
+
* local OS subprocess or a test double that records the request).
|
|
44
|
+
*/
|
|
45
|
+
subprocess?: SubprocessAdapter;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Route a step through the configured subprocess adapter and return a
|
|
49
|
+
* `DetachedHandle` that polls until the adapter reports a terminal
|
|
50
|
+
* status. The adapter's `spawn()` may be async (the default in-memory
|
|
51
|
+
* adapter schedules the step body on the microtask queue, and
|
|
52
|
+
* out-of-process adapters may await an OS spawn).
|
|
53
|
+
*
|
|
54
|
+
* `executeNoAdapter` is used as the local executor so the adapter we just
|
|
55
|
+
* routed through does not re-route the same step. The handler still uses
|
|
56
|
+
* `execute()` for nested children, so per-step overrides on descendants
|
|
57
|
+
* keep working.
|
|
58
|
+
*
|
|
59
|
+
* @internal
|
|
60
|
+
*/
|
|
61
|
+
export declare function dispatchStepThroughAdapter<I, O>(h: StepDispatchHandle, s: Step<ContextMemory, I, O>, input: I, parentCtx: Context, overrides?: DetachedSpawnOverrides): DetachedHandle<O>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { DetachedHandleImpl } from '../runtime/detached-handle.js';
|
|
2
|
+
import { executeNoAdapter } from './execute.js';
|
|
3
|
+
//#region Adapter resolution
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the subprocess adapter used for a given step dispatch.
|
|
6
|
+
*
|
|
7
|
+
* Precedence: `detachedSpawn-overrides.subprocess ?? step.subprocess ??
|
|
8
|
+
* harness.subprocess`. Only `StepRun` and `StepSpawn` currently carry a
|
|
9
|
+
* `subprocess` field — other variants always fall through to the harness
|
|
10
|
+
* default.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export function resolveStepAdapter(step, callOverride, fallback) {
|
|
15
|
+
if (callOverride) {
|
|
16
|
+
return callOverride;
|
|
17
|
+
}
|
|
18
|
+
if ((step.kind === 'run' || step.kind === 'spawn') && step.subprocess) {
|
|
19
|
+
return step.subprocess;
|
|
20
|
+
}
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region Dispatcher
|
|
25
|
+
/**
|
|
26
|
+
* Route a step through the configured subprocess adapter and return a
|
|
27
|
+
* `DetachedHandle` that polls until the adapter reports a terminal
|
|
28
|
+
* status. The adapter's `spawn()` may be async (the default in-memory
|
|
29
|
+
* adapter schedules the step body on the microtask queue, and
|
|
30
|
+
* out-of-process adapters may await an OS spawn).
|
|
31
|
+
*
|
|
32
|
+
* `executeNoAdapter` is used as the local executor so the adapter we just
|
|
33
|
+
* routed through does not re-route the same step. The handler still uses
|
|
34
|
+
* `execute()` for nested children, so per-step overrides on descendants
|
|
35
|
+
* keep working.
|
|
36
|
+
*
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
export function dispatchStepThroughAdapter(h, s, input, parentCtx, overrides) {
|
|
40
|
+
const childCtx = h.createContext({
|
|
41
|
+
parent: parentCtx,
|
|
42
|
+
threadId: overrides?.threadId ?? parentCtx.threadId,
|
|
43
|
+
resourceId: overrides?.resourceId ?? parentCtx.resourceId,
|
|
44
|
+
cwdInit: overrides?.cwdInit,
|
|
45
|
+
});
|
|
46
|
+
const adapter = resolveStepAdapter(s, overrides?.subprocess, h.subprocess);
|
|
47
|
+
const request = {
|
|
48
|
+
kind: 'step',
|
|
49
|
+
stepId: s.id,
|
|
50
|
+
serializedInput: input,
|
|
51
|
+
executionId: childCtx.id,
|
|
52
|
+
overrides: {
|
|
53
|
+
threadId: overrides?.threadId ?? parentCtx.threadId,
|
|
54
|
+
resourceId: overrides?.resourceId ?? parentCtx.resourceId,
|
|
55
|
+
cwdInit: overrides?.cwdInit,
|
|
56
|
+
},
|
|
57
|
+
_localExecutor: () => executeNoAdapter(s, input, childCtx),
|
|
58
|
+
};
|
|
59
|
+
const spawnPromise = adapter.spawn(request);
|
|
60
|
+
return new DetachedHandleImpl({
|
|
61
|
+
id: childCtx.id,
|
|
62
|
+
stepId: s.id,
|
|
63
|
+
adapter,
|
|
64
|
+
spawnPromise,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
import type { ChannelStore } from '../runtime/channel-store';
|
|
2
|
-
import { ContextImpl } from '../runtime/context-impl';
|
|
3
|
-
import type { Context } from '../types/context';
|
|
4
|
-
import type { FunctionCallItem, Item
|
|
5
|
-
import type { ContextMemory } from '../types/memory';
|
|
6
|
-
import type { MutableContext } from '../types/mutable-context';
|
|
1
|
+
import type { ChannelStore } from '../runtime/channel-store.js';
|
|
2
|
+
import { ContextImpl } from '../runtime/context-impl.js';
|
|
3
|
+
import type { Context } from '../types/context.js';
|
|
4
|
+
import type { FunctionCallItem, Item } from '../types/items.js';
|
|
5
|
+
import type { ContextMemory } from '../types/memory.js';
|
|
6
|
+
import type { MutableContext } from '../types/mutable-context.js';
|
|
7
|
+
export { isAssistantMessage, isOutputText, isUserMessage } from '../util/message-helpers.js';
|
|
7
8
|
export declare function isMutableContext(ctx: Context<ContextMemory>): ctx is MutableContext;
|
|
8
9
|
export declare function isContextImpl(ctx: Context<ContextMemory>): ctx is ContextImpl;
|
|
9
10
|
/**
|
|
10
11
|
* Co-located with `isContextImpl` so the import of `ContextImpl` flows through
|
|
11
12
|
* the same module other interpreters pull it from, sidestepping a circular
|
|
12
|
-
* TDZ when this helper is used by `execute-
|
|
13
|
+
* TDZ when this helper is used by `executeEvery` in `execute-control.ts`.
|
|
13
14
|
*/
|
|
14
15
|
export declare function getContextChannelStore<TMemory>(ctx: Context<TMemory>): ChannelStore | undefined;
|
|
15
|
-
export declare function isAssistantMessage(item: unknown): item is MessageItem;
|
|
16
|
-
export declare function isUserMessage(item: unknown): item is MessageItem;
|
|
17
16
|
export declare function isFunctionCall(item: Item): item is FunctionCallItem;
|
|
18
|
-
export declare function isOutputText(part: {
|
|
19
|
-
type: string;
|
|
20
|
-
}): part is {
|
|
21
|
-
type: 'output_text';
|
|
22
|
-
text: string;
|
|
23
|
-
};
|
|
24
17
|
declare function assertNever(_value: never): never;
|
|
25
18
|
export { assertNever };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { ContextImpl } from '../runtime/context-impl';
|
|
1
|
+
import { ContextImpl } from '../runtime/context-impl.js';
|
|
2
|
+
// Re-exports for backward compatibility; the pure variants live in util/.
|
|
3
|
+
export { isAssistantMessage, isOutputText, isUserMessage } from '../util/message-helpers.js';
|
|
2
4
|
export function isMutableContext(ctx) {
|
|
3
5
|
// Check if the context has writable mutable fields (ContextImpl or compatible mock)
|
|
4
6
|
if (ctx instanceof ContextImpl) {
|
|
@@ -14,7 +16,7 @@ export function isContextImpl(ctx) {
|
|
|
14
16
|
/**
|
|
15
17
|
* Co-located with `isContextImpl` so the import of `ContextImpl` flows through
|
|
16
18
|
* the same module other interpreters pull it from, sidestepping a circular
|
|
17
|
-
* TDZ when this helper is used by `execute-
|
|
19
|
+
* TDZ when this helper is used by `executeEvery` in `execute-control.ts`.
|
|
18
20
|
*/
|
|
19
21
|
export function getContextChannelStore(ctx) {
|
|
20
22
|
if (ctx instanceof ContextImpl) {
|
|
@@ -22,28 +24,9 @@ export function getContextChannelStore(ctx) {
|
|
|
22
24
|
}
|
|
23
25
|
return undefined;
|
|
24
26
|
}
|
|
25
|
-
export function isAssistantMessage(item) {
|
|
26
|
-
return (typeof item === 'object' &&
|
|
27
|
-
item !== null &&
|
|
28
|
-
'type' in item &&
|
|
29
|
-
item.type === 'message' &&
|
|
30
|
-
'role' in item &&
|
|
31
|
-
item.role === 'assistant');
|
|
32
|
-
}
|
|
33
|
-
export function isUserMessage(item) {
|
|
34
|
-
return (typeof item === 'object' &&
|
|
35
|
-
item !== null &&
|
|
36
|
-
'type' in item &&
|
|
37
|
-
item.type === 'message' &&
|
|
38
|
-
'role' in item &&
|
|
39
|
-
item.role === 'user');
|
|
40
|
-
}
|
|
41
27
|
export function isFunctionCall(item) {
|
|
42
28
|
return item.type === 'function_call' && 'callId' in item && 'name' in item;
|
|
43
29
|
}
|
|
44
|
-
export function isOutputText(part) {
|
|
45
|
-
return part.type === 'output_text';
|
|
46
|
-
}
|
|
47
30
|
function assertNever(_value) {
|
|
48
31
|
throw new Error('Exhaustive check failed');
|
|
49
32
|
}
|
package/dist/memory/budget.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Context } from '../types/context';
|
|
2
|
-
import type { MemoryLayer } from '../types/memory';
|
|
1
|
+
import type { Context } from '../types/context.js';
|
|
2
|
+
import type { MemoryLayer } from '../types/memory.js';
|
|
3
3
|
export interface BudgetAllocation {
|
|
4
4
|
layerId: string;
|
|
5
5
|
allocated: number;
|
package/dist/memory/budget.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Context } from '../types/context';
|
|
2
|
-
import type { ExecutionContext } from '../types/memory';
|
|
1
|
+
import type { Context } from '../types/context.js';
|
|
2
|
+
import type { ExecutionContext } from '../types/memory.js';
|
|
3
3
|
/**
|
|
4
4
|
* Optional overrides for `contextToExecCtx`. Used by `executeSpawn` to build
|
|
5
5
|
* parent and child execution contexts that reuse the same tokenize / trace /
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { estimateTokens } from '../
|
|
1
|
+
import { estimateTokens } from '../util/message-helpers.js';
|
|
2
2
|
/**
|
|
3
3
|
* Maps a full `Context` to the lightweight `ExecutionContext` consumed by memory layer hooks.
|
|
4
4
|
* Centralised here so the mapping stays consistent across agent-harness, layer-api, and the
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schema for plan-mode flow JSON.
|
|
3
|
+
*
|
|
4
|
+
* A "flow" is a JSON-serialisable subset of noetic's `Step` shape that the model
|
|
5
|
+
* may emit during plan mode. The runtime expands each node into a concrete `Step`
|
|
6
|
+
* via the existing builders (`step.llm`, `fork`, `spawn`) at execute time.
|
|
7
|
+
*
|
|
8
|
+
* Tool references inside `llm` nodes are name strings. They get resolved against
|
|
9
|
+
* the harness's live tool registry when the flow runs.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
interface NodeBase {
|
|
13
|
+
id: string;
|
|
14
|
+
subPlanRef?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface LlmFlowNode extends NodeBase {
|
|
17
|
+
kind: 'llm';
|
|
18
|
+
model?: string;
|
|
19
|
+
instructions: string;
|
|
20
|
+
tools?: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface SubagentFlowNode extends NodeBase {
|
|
23
|
+
kind: 'subagent';
|
|
24
|
+
preset: string;
|
|
25
|
+
prompt: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ForkFlowNode extends NodeBase {
|
|
28
|
+
kind: 'fork';
|
|
29
|
+
mode: 'all' | 'race' | 'settle';
|
|
30
|
+
paths: FlowNode[];
|
|
31
|
+
}
|
|
32
|
+
export interface SpawnFlowNode extends NodeBase {
|
|
33
|
+
kind: 'spawn';
|
|
34
|
+
child: FlowNode;
|
|
35
|
+
}
|
|
36
|
+
export interface SequenceFlowNode extends NodeBase {
|
|
37
|
+
kind: 'sequence';
|
|
38
|
+
steps: FlowNode[];
|
|
39
|
+
}
|
|
40
|
+
export type FlowNode = LlmFlowNode | SubagentFlowNode | ForkFlowNode | SpawnFlowNode | SequenceFlowNode;
|
|
41
|
+
/** Top-level flow document: a single root node. */
|
|
42
|
+
export declare const FlowSchema: z.ZodType<FlowNode, unknown, z.core.$ZodTypeInternals<FlowNode, unknown>>;
|
|
43
|
+
/** Validates a candidate flow document. Throws `ZodError` on invalid input. */
|
|
44
|
+
export declare function validateFlow(input: unknown): FlowNode;
|
|
45
|
+
/** Walks a flow tree depth-first, yielding each node. */
|
|
46
|
+
export declare function walkFlow(root: FlowNode): Iterable<FlowNode>;
|
|
47
|
+
/**
|
|
48
|
+
* Returns the maximum depth of a flow tree. A leaf (llm/subagent) is depth 0.
|
|
49
|
+
* Structural nodes (sequence/fork/spawn) contribute +1 over their deepest child.
|
|
50
|
+
*/
|
|
51
|
+
export declare function flowDepth(node: FlowNode): number;
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schema for plan-mode flow JSON.
|
|
3
|
+
*
|
|
4
|
+
* A "flow" is a JSON-serialisable subset of noetic's `Step` shape that the model
|
|
5
|
+
* may emit during plan mode. The runtime expands each node into a concrete `Step`
|
|
6
|
+
* via the existing builders (`step.llm`, `fork`, `spawn`) at execute time.
|
|
7
|
+
*
|
|
8
|
+
* Tool references inside `llm` nodes are name strings. They get resolved against
|
|
9
|
+
* the harness's live tool registry when the flow runs.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region Schema
|
|
14
|
+
const FlowNodeRef = z.lazy(() => FlowNodeSchema);
|
|
15
|
+
const SHARED_FIELDS = {
|
|
16
|
+
id: z.string().min(1),
|
|
17
|
+
subPlanRef: z.string().optional(),
|
|
18
|
+
};
|
|
19
|
+
const LlmNodeSchema = z.object({
|
|
20
|
+
kind: z.literal('llm'),
|
|
21
|
+
...SHARED_FIELDS,
|
|
22
|
+
model: z.string().optional(),
|
|
23
|
+
instructions: z.string(),
|
|
24
|
+
tools: z.array(z.string()).optional(),
|
|
25
|
+
});
|
|
26
|
+
const SubagentNodeSchema = z.object({
|
|
27
|
+
kind: z.literal('subagent'),
|
|
28
|
+
...SHARED_FIELDS,
|
|
29
|
+
preset: z.string().min(1),
|
|
30
|
+
prompt: z.string(),
|
|
31
|
+
});
|
|
32
|
+
const ForkNodeSchema = z.object({
|
|
33
|
+
kind: z.literal('fork'),
|
|
34
|
+
...SHARED_FIELDS,
|
|
35
|
+
mode: z.enum([
|
|
36
|
+
'all',
|
|
37
|
+
'race',
|
|
38
|
+
'settle',
|
|
39
|
+
]),
|
|
40
|
+
paths: z.array(FlowNodeRef).min(1),
|
|
41
|
+
});
|
|
42
|
+
const SpawnNodeSchema = z.object({
|
|
43
|
+
kind: z.literal('spawn'),
|
|
44
|
+
...SHARED_FIELDS,
|
|
45
|
+
child: FlowNodeRef,
|
|
46
|
+
});
|
|
47
|
+
const SequenceNodeSchema = z.object({
|
|
48
|
+
kind: z.literal('sequence'),
|
|
49
|
+
...SHARED_FIELDS,
|
|
50
|
+
steps: z.array(FlowNodeRef).min(1),
|
|
51
|
+
});
|
|
52
|
+
const FlowNodeSchema = z.discriminatedUnion('kind', [
|
|
53
|
+
LlmNodeSchema,
|
|
54
|
+
SubagentNodeSchema,
|
|
55
|
+
ForkNodeSchema,
|
|
56
|
+
SpawnNodeSchema,
|
|
57
|
+
SequenceNodeSchema,
|
|
58
|
+
]);
|
|
59
|
+
/** Top-level flow document: a single root node. */
|
|
60
|
+
export const FlowSchema = FlowNodeSchema;
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region Public API
|
|
63
|
+
/** Validates a candidate flow document. Throws `ZodError` on invalid input. */
|
|
64
|
+
export function validateFlow(input) {
|
|
65
|
+
return FlowSchema.parse(input);
|
|
66
|
+
}
|
|
67
|
+
/** Walks a flow tree depth-first, yielding each node. */
|
|
68
|
+
export function* walkFlow(root) {
|
|
69
|
+
yield root;
|
|
70
|
+
if (root.kind === 'fork') {
|
|
71
|
+
for (const path of root.paths) {
|
|
72
|
+
yield* walkFlow(path);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (root.kind === 'sequence') {
|
|
77
|
+
for (const step of root.steps) {
|
|
78
|
+
yield* walkFlow(step);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (root.kind === 'spawn') {
|
|
83
|
+
yield* walkFlow(root.child);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Returns the maximum depth of a flow tree. A leaf (llm/subagent) is depth 0.
|
|
88
|
+
* Structural nodes (sequence/fork/spawn) contribute +1 over their deepest child.
|
|
89
|
+
*/
|
|
90
|
+
export function flowDepth(node) {
|
|
91
|
+
if (node.kind === 'sequence') {
|
|
92
|
+
return 1 + Math.max(0, ...node.steps.map(flowDepth));
|
|
93
|
+
}
|
|
94
|
+
if (node.kind === 'fork') {
|
|
95
|
+
return 1 + Math.max(0, ...node.paths.map(flowDepth));
|
|
96
|
+
}
|
|
97
|
+
if (node.kind === 'spawn') {
|
|
98
|
+
return 1 + flowDepth(node.child);
|
|
99
|
+
}
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|