@noetic-tools/core 0.0.1 → 0.1.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/in-memory-fs-adapter.js +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 +11 -2
- package/dist/adapters/openrouter.js +66 -5
- package/dist/builders/control-flow-builders.js +8 -3
- package/dist/builders/every.js +4 -1
- package/dist/builders/layer-provides-builders.d.ts +1 -32
- package/dist/builders/layer-provides-builders.js +1 -33
- package/dist/builders/loop-builder.js +4 -1
- package/dist/builders/memory-builder.js +1 -1
- package/dist/builders/provide-builder.js +4 -1
- package/dist/builders/spawn-builder.d.ts +20 -7
- package/dist/builders/spawn-builder.js +16 -2
- package/dist/builders/step-builders.d.ts +49 -28
- package/dist/builders/step-builders.js +30 -11
- package/dist/builders/tool-builder.d.ts +5 -1
- package/dist/builders/tool-builder.js +2 -0
- package/dist/builders/workflow-hydrator.d.ts +38 -0
- package/dist/builders/workflow-hydrator.js +336 -0
- package/dist/conditions/conditions.js +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 +48 -12
- package/dist/index.js +29 -7
- 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 +1 -1
- 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} +7 -1
- package/dist/interpreter/execute-control.js +530 -0
- package/dist/interpreter/execute.d.ts +13 -0
- package/dist/interpreter/execute.js +151 -48
- 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 +3 -10
- package/dist/interpreter/typeguards.js +3 -20
- 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.js +4 -2
- package/dist/memory/layer-api.d.ts +1 -1
- package/dist/memory/layer-api.js +2 -2
- package/dist/memory/layer-lifecycle.d.ts +1 -1
- package/dist/memory/layer-lifecycle.js +2 -2
- package/dist/memory/layer-provides.d.ts +32 -0
- package/dist/memory/layer-provides.js +33 -0
- package/dist/memory/layer-usage.d.ts +3 -2
- package/dist/memory/layer-usage.js +9 -3
- package/dist/memory/layers/durable-task-state.js +1 -1
- package/dist/memory/layers/file-reference.js +302 -246
- package/dist/memory/layers/history-window.js +1 -1
- package/dist/memory/layers/observational-memory.js +1 -2
- package/dist/memory/layers/plan.d.ts +1 -1
- package/dist/memory/layers/plan.js +13 -8
- package/dist/memory/layers/steering.js +1 -1
- package/dist/memory/layers/tool-memory-layer.d.ts +1 -1
- package/dist/memory/layers/working-memory.js +2 -2
- 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.js +1 -1
- package/dist/patterns/plans.d.ts +3 -3
- package/dist/patterns/plans.js +3 -3
- package/dist/patterns/ralph-wiggum.d.ts +1 -1
- package/dist/patterns/react.d.ts +1 -1
- 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 +5 -1
- package/dist/runtime/broadcaster-utils.js +11 -6
- package/dist/runtime/channel-store.js +1 -1
- 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 +51 -4
- package/dist/runtime/context-type-deps.d.ts +14 -0
- package/dist/runtime/context-type-deps.js +1 -0
- package/dist/runtime/detached-handle.d.ts +50 -2
- package/dist/runtime/detached-handle.js +216 -12
- 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/in-memory-storage.js +1 -1
- package/dist/runtime/item-log-impl.d.ts +1 -1
- package/dist/runtime/step-registry.d.ts +42 -0
- package/dist/runtime/step-registry.js +77 -0
- 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 +135 -26
- package/dist/types/error.d.ts +15 -0
- package/dist/types/harness-result.d.ts +1 -1
- 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 +35 -65
- package/dist/types/runtime.d.ts +2 -1
- package/dist/types/steering.d.ts +1 -1
- package/dist/types/step-registrar.d.ts +46 -0
- package/dist/types/step-registrar.js +56 -0
- package/dist/types/step.d.ts +30 -5
- package/dist/types/subprocess-adapter.d.ts +119 -3
- package/dist/types/subprocess-adapter.js +2 -0
- package/dist/types/tool.d.ts +64 -0
- package/dist/types/tool.js +1 -0
- package/dist/unstable.d.ts +2 -2
- package/dist/unstable.js +2 -2
- package/dist/until/predicates.d.ts +9 -0
- package/dist/until/predicates.js +14 -0
- package/dist/util/message-helpers.d.ts +17 -0
- package/dist/util/message-helpers.js +64 -0
- package/package.json +1 -1
- 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/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/{runtime → memory}/exec-context-factory.d.ts +0 -0
- /package/dist/{interpreter → util}/framework-cast.d.ts +0 -0
- /package/dist/{interpreter → util}/framework-cast.js +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a validated `WorkflowDocument` into a live `Step` tree.
|
|
3
|
+
*
|
|
4
|
+
* Each node kind maps to the corresponding builder (`step.llm`, `fork`,
|
|
5
|
+
* `loop`, etc.) so hydrated steps are indistinguishable from programmatic
|
|
6
|
+
* ones — they register in the step registry, carry retry policies, etc.
|
|
7
|
+
*/
|
|
8
|
+
import type { WorkflowDocument, WorkflowNode } from '../schemas/workflow';
|
|
9
|
+
import type { ContextMemory, MemoryLayer } from '../types/memory';
|
|
10
|
+
import type { ExecuteStepFn, Step } from '../types/step';
|
|
11
|
+
import type { Tool } from '../types/tool';
|
|
12
|
+
/** @public Context required to hydrate a JSON workflow into live steps. */
|
|
13
|
+
export interface HydrationContext {
|
|
14
|
+
tools: ReadonlyMap<string, Tool>;
|
|
15
|
+
executeStep: ExecuteStepFn;
|
|
16
|
+
layers?: ReadonlyMap<string, MemoryLayer>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Hydrates a single `WorkflowNode` into a live `Step`.
|
|
20
|
+
*
|
|
21
|
+
* @public
|
|
22
|
+
* @param node - Validated workflow node.
|
|
23
|
+
* @param ctx - Hydration context with tool registry and step executor.
|
|
24
|
+
* @returns A live `Step` ready for execution.
|
|
25
|
+
* @throws `NoeticConfigError` with code `UNKNOWN_NODE_KIND` if the node kind is unrecognised.
|
|
26
|
+
* @throws `NoeticConfigError` with code `UNKNOWN_TOOL_REFERENCE` if a tool name cannot be resolved.
|
|
27
|
+
* @throws `NoeticConfigError` with code `UNKNOWN_UNTIL_PREDICATE` if an until predicate kind is unrecognised.
|
|
28
|
+
*/
|
|
29
|
+
export declare function hydrateNode(node: WorkflowNode, ctx: HydrationContext): Step<ContextMemory, string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Hydrates a complete `WorkflowDocument` into a live `Step` tree.
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
* @param doc - Validated workflow document (version 1).
|
|
35
|
+
* @param ctx - Hydration context with tool registry and step executor.
|
|
36
|
+
* @returns The root `Step` of the hydrated workflow.
|
|
37
|
+
*/
|
|
38
|
+
export declare function hydrateWorkflow(doc: WorkflowDocument, ctx: HydrationContext): Step<ContextMemory, string, string>;
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a validated `WorkflowDocument` into a live `Step` tree.
|
|
3
|
+
*
|
|
4
|
+
* Each node kind maps to the corresponding builder (`step.llm`, `fork`,
|
|
5
|
+
* `loop`, etc.) so hydrated steps are indistinguishable from programmatic
|
|
6
|
+
* ones — they register in the step registry, carry retry policies, etc.
|
|
7
|
+
*/
|
|
8
|
+
import { NoeticConfigError } from '../errors/noetic-config-error';
|
|
9
|
+
import { all, any } from '../until/combinators';
|
|
10
|
+
import { until } from '../until/predicates';
|
|
11
|
+
import { frameworkCast } from '../util/framework-cast';
|
|
12
|
+
import { branch, fork } from './control-flow-builders';
|
|
13
|
+
import { every } from './every';
|
|
14
|
+
import { loop } from './loop-builder';
|
|
15
|
+
import { provide } from './provide-builder';
|
|
16
|
+
import { spawn } from './spawn-builder';
|
|
17
|
+
import { step } from './step-builders';
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region Until Predicate Hydration
|
|
20
|
+
function hydrateUntilPredicate(pred) {
|
|
21
|
+
switch (pred.kind) {
|
|
22
|
+
case 'maxSteps':
|
|
23
|
+
return until.maxSteps(pred.n);
|
|
24
|
+
case 'maxCost':
|
|
25
|
+
return until.maxCost(pred.usd);
|
|
26
|
+
case 'maxDuration':
|
|
27
|
+
return until.maxDuration(pred.ms);
|
|
28
|
+
case 'noToolCalls':
|
|
29
|
+
return until.noToolCalls();
|
|
30
|
+
case 'outputContains':
|
|
31
|
+
return until.outputContains(pred.marker);
|
|
32
|
+
case 'outputEquals':
|
|
33
|
+
return until.outputEquals(pred.sentinel);
|
|
34
|
+
case 'converged':
|
|
35
|
+
return until.converged({
|
|
36
|
+
threshold: pred.threshold,
|
|
37
|
+
});
|
|
38
|
+
case 'any':
|
|
39
|
+
return any(...pred.predicates.map(hydrateUntilPredicate));
|
|
40
|
+
case 'all':
|
|
41
|
+
return all(...pred.predicates.map(hydrateUntilPredicate));
|
|
42
|
+
default:
|
|
43
|
+
throw new NoeticConfigError({
|
|
44
|
+
code: 'UNKNOWN_UNTIL_PREDICATE',
|
|
45
|
+
message: `Unknown until predicate kind: '${frameworkCast(pred).kind}'.`,
|
|
46
|
+
hint: 'Supported kinds: maxSteps, maxCost, maxDuration, noToolCalls, outputContains, outputEquals, converged, any, all.',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region Node Hydrators
|
|
52
|
+
function hydrateLlmNode(node, ctx) {
|
|
53
|
+
if (node.kind !== 'llm') {
|
|
54
|
+
return frameworkCast(undefined);
|
|
55
|
+
}
|
|
56
|
+
const resolvedTools = resolveTools(node.tools, ctx.tools);
|
|
57
|
+
return step.llm({
|
|
58
|
+
id: node.id,
|
|
59
|
+
model: node.model ?? 'openai/gpt-4o',
|
|
60
|
+
instructions: node.instructions,
|
|
61
|
+
tools: resolvedTools.length > 0 ? resolvedTools : undefined,
|
|
62
|
+
params: node.params,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function hydrateToolNode(node, ctx) {
|
|
66
|
+
if (node.kind !== 'tool') {
|
|
67
|
+
return frameworkCast(undefined);
|
|
68
|
+
}
|
|
69
|
+
const resolved = ctx.tools.get(node.toolName);
|
|
70
|
+
if (!resolved) {
|
|
71
|
+
throw new NoeticConfigError({
|
|
72
|
+
code: 'UNKNOWN_TOOL_REFERENCE',
|
|
73
|
+
message: `Tool '${node.toolName}' referenced in workflow node '${node.id}' is not registered.`,
|
|
74
|
+
hint: `Available tools: ${[
|
|
75
|
+
...ctx.tools.keys(),
|
|
76
|
+
].join(', ') || '(none)'}`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return frameworkCast(step.run({
|
|
80
|
+
id: node.id,
|
|
81
|
+
execute: async (_input, execCtx) => {
|
|
82
|
+
const args = node.args ?? {};
|
|
83
|
+
const callId = `call-${node.id}-${Date.now()}`;
|
|
84
|
+
const callItem = {
|
|
85
|
+
id: callId,
|
|
86
|
+
type: 'function_call',
|
|
87
|
+
status: 'completed',
|
|
88
|
+
name: node.toolName,
|
|
89
|
+
callId,
|
|
90
|
+
arguments: JSON.stringify(args),
|
|
91
|
+
};
|
|
92
|
+
execCtx.itemLog.append(callItem);
|
|
93
|
+
const toolCtx = {
|
|
94
|
+
ctx: execCtx,
|
|
95
|
+
harness: execCtx.harness,
|
|
96
|
+
fs: execCtx.fs,
|
|
97
|
+
shell: execCtx.shell,
|
|
98
|
+
memory: {
|
|
99
|
+
get: (_layerId) => undefined,
|
|
100
|
+
set: (_layerId, _state) => { },
|
|
101
|
+
},
|
|
102
|
+
assembledView: execCtx.itemLog.items,
|
|
103
|
+
lastStepMeta: execCtx.lastStepMeta,
|
|
104
|
+
};
|
|
105
|
+
const result = await resolved.execute(args, toolCtx);
|
|
106
|
+
return stringifyResult(result);
|
|
107
|
+
},
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
function hydrateBranchNode(node, ctx) {
|
|
111
|
+
if (node.kind !== 'branch') {
|
|
112
|
+
return frameworkCast(undefined);
|
|
113
|
+
}
|
|
114
|
+
const hydratedRoutes = node.routes.map((r) => ({
|
|
115
|
+
match: r.match,
|
|
116
|
+
target: hydrateNode(r.target, ctx),
|
|
117
|
+
}));
|
|
118
|
+
const defaultTarget = node.default ? hydrateNode(node.default, ctx) : null;
|
|
119
|
+
const allTargets = hydratedRoutes.map((r) => r.target);
|
|
120
|
+
if (defaultTarget) {
|
|
121
|
+
allTargets.push(defaultTarget);
|
|
122
|
+
}
|
|
123
|
+
return branch({
|
|
124
|
+
id: node.id,
|
|
125
|
+
route: (input) => {
|
|
126
|
+
const trimmed = input.trim().toLowerCase();
|
|
127
|
+
for (const r of hydratedRoutes) {
|
|
128
|
+
if (trimmed.includes(r.match.toLowerCase())) {
|
|
129
|
+
return r.target;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return defaultTarget;
|
|
133
|
+
},
|
|
134
|
+
_optimizable: frameworkCast(allTargets),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function hydrateForkNode(node, ctx) {
|
|
138
|
+
if (node.kind !== 'fork') {
|
|
139
|
+
return frameworkCast(undefined);
|
|
140
|
+
}
|
|
141
|
+
const hydratedPaths = node.paths.map((p) => hydrateNode(p, ctx));
|
|
142
|
+
if (node.mode === 'race') {
|
|
143
|
+
return fork({
|
|
144
|
+
id: node.id,
|
|
145
|
+
mode: 'race',
|
|
146
|
+
paths: () => hydratedPaths,
|
|
147
|
+
concurrency: node.concurrency,
|
|
148
|
+
_optimizable: frameworkCast(hydratedPaths),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const mergeFn = buildMerge(node.merge ?? 'last');
|
|
152
|
+
if (node.mode === 'settle') {
|
|
153
|
+
return fork({
|
|
154
|
+
id: node.id,
|
|
155
|
+
mode: 'settle',
|
|
156
|
+
paths: () => hydratedPaths,
|
|
157
|
+
merge: (results) => {
|
|
158
|
+
const values = results.filter((r) => r.status === 'fulfilled').map((r) => r.value ?? '');
|
|
159
|
+
return mergeFn(values);
|
|
160
|
+
},
|
|
161
|
+
concurrency: node.concurrency,
|
|
162
|
+
_optimizable: frameworkCast(hydratedPaths),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return fork({
|
|
166
|
+
id: node.id,
|
|
167
|
+
mode: 'all',
|
|
168
|
+
paths: () => hydratedPaths,
|
|
169
|
+
merge: mergeFn,
|
|
170
|
+
concurrency: node.concurrency,
|
|
171
|
+
_optimizable: frameworkCast(hydratedPaths),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function hydrateSpawnNode(node, ctx) {
|
|
175
|
+
if (node.kind !== 'spawn') {
|
|
176
|
+
return frameworkCast(undefined);
|
|
177
|
+
}
|
|
178
|
+
return spawn({
|
|
179
|
+
id: node.id,
|
|
180
|
+
child: hydrateNode(node.child, ctx),
|
|
181
|
+
timeout: node.timeout,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function hydrateProvideNode(node, ctx) {
|
|
185
|
+
if (node.kind !== 'provide') {
|
|
186
|
+
return frameworkCast(undefined);
|
|
187
|
+
}
|
|
188
|
+
const resolvedLayers = ctx.layers
|
|
189
|
+
? node.layers.map((name) => {
|
|
190
|
+
const layer = ctx.layers?.get(name);
|
|
191
|
+
if (!layer) {
|
|
192
|
+
throw new NoeticConfigError({
|
|
193
|
+
code: 'UNKNOWN_LAYER_REFERENCE',
|
|
194
|
+
message: `Memory layer '${name}' referenced in provide node '${node.id}' is not registered.`,
|
|
195
|
+
hint: `Available layers: ${[
|
|
196
|
+
...(ctx.layers?.keys() ?? []),
|
|
197
|
+
].join(', ') || '(none)'}. Pass named layers via HydrationContext.layers.`,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return layer;
|
|
201
|
+
})
|
|
202
|
+
: [];
|
|
203
|
+
return provide({
|
|
204
|
+
id: node.id,
|
|
205
|
+
child: hydrateNode(node.child, ctx),
|
|
206
|
+
memory: resolvedLayers,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function hydrateLoopNode(node, ctx) {
|
|
210
|
+
if (node.kind !== 'loop') {
|
|
211
|
+
return frameworkCast(undefined);
|
|
212
|
+
}
|
|
213
|
+
return loop({
|
|
214
|
+
id: node.id,
|
|
215
|
+
steps: [
|
|
216
|
+
hydrateNode(node.body, ctx),
|
|
217
|
+
],
|
|
218
|
+
until: hydrateUntilPredicate(node.until),
|
|
219
|
+
maxIterations: node.maxIterations,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function hydrateSequenceNode(node, ctx) {
|
|
223
|
+
if (node.kind !== 'sequence') {
|
|
224
|
+
return frameworkCast(undefined);
|
|
225
|
+
}
|
|
226
|
+
const children = node.steps.map((s) => hydrateNode(s, ctx));
|
|
227
|
+
return frameworkCast(step.run({
|
|
228
|
+
id: node.id,
|
|
229
|
+
execute: async (input, execCtx) => {
|
|
230
|
+
let current = input;
|
|
231
|
+
for (const child of children) {
|
|
232
|
+
const childInput = stringifyResult(current);
|
|
233
|
+
current = await ctx.executeStep(child, childInput, execCtx);
|
|
234
|
+
}
|
|
235
|
+
return stringifyResult(current);
|
|
236
|
+
},
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
function hydrateEveryNode(node, ctx) {
|
|
240
|
+
if (node.kind !== 'every') {
|
|
241
|
+
return frameworkCast(undefined);
|
|
242
|
+
}
|
|
243
|
+
return every({
|
|
244
|
+
id: node.id,
|
|
245
|
+
step: hydrateNode(node.step, ctx),
|
|
246
|
+
ms: node.ms,
|
|
247
|
+
onError: node.onError,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region Handler Registry
|
|
252
|
+
const NODE_HYDRATORS = {
|
|
253
|
+
llm: hydrateLlmNode,
|
|
254
|
+
tool: hydrateToolNode,
|
|
255
|
+
branch: hydrateBranchNode,
|
|
256
|
+
fork: hydrateForkNode,
|
|
257
|
+
spawn: hydrateSpawnNode,
|
|
258
|
+
provide: hydrateProvideNode,
|
|
259
|
+
loop: hydrateLoopNode,
|
|
260
|
+
sequence: hydrateSequenceNode,
|
|
261
|
+
every: hydrateEveryNode,
|
|
262
|
+
};
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region Helpers
|
|
265
|
+
function resolveTools(toolNames, registry) {
|
|
266
|
+
if (!toolNames || toolNames.length === 0) {
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
return toolNames.map((name) => {
|
|
270
|
+
const resolved = registry.get(name);
|
|
271
|
+
if (!resolved) {
|
|
272
|
+
throw new NoeticConfigError({
|
|
273
|
+
code: 'UNKNOWN_TOOL_REFERENCE',
|
|
274
|
+
message: `Tool '${name}' referenced in workflow is not registered.`,
|
|
275
|
+
hint: `Available tools: ${[
|
|
276
|
+
...registry.keys(),
|
|
277
|
+
].join(', ') || '(none)'}`,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return resolved;
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function buildMerge(strategy) {
|
|
284
|
+
if (strategy === 'first') {
|
|
285
|
+
return (r) => r[0] ?? '';
|
|
286
|
+
}
|
|
287
|
+
if (strategy === 'concat') {
|
|
288
|
+
return (r) => r.join('\n');
|
|
289
|
+
}
|
|
290
|
+
return (r) => r[r.length - 1] ?? '';
|
|
291
|
+
}
|
|
292
|
+
function stringifyResult(value) {
|
|
293
|
+
if (typeof value === 'string') {
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
if (value === null || value === undefined) {
|
|
297
|
+
return '';
|
|
298
|
+
}
|
|
299
|
+
return JSON.stringify(value);
|
|
300
|
+
}
|
|
301
|
+
//#endregion
|
|
302
|
+
//#region Public API
|
|
303
|
+
/**
|
|
304
|
+
* Hydrates a single `WorkflowNode` into a live `Step`.
|
|
305
|
+
*
|
|
306
|
+
* @public
|
|
307
|
+
* @param node - Validated workflow node.
|
|
308
|
+
* @param ctx - Hydration context with tool registry and step executor.
|
|
309
|
+
* @returns A live `Step` ready for execution.
|
|
310
|
+
* @throws `NoeticConfigError` with code `UNKNOWN_NODE_KIND` if the node kind is unrecognised.
|
|
311
|
+
* @throws `NoeticConfigError` with code `UNKNOWN_TOOL_REFERENCE` if a tool name cannot be resolved.
|
|
312
|
+
* @throws `NoeticConfigError` with code `UNKNOWN_UNTIL_PREDICATE` if an until predicate kind is unrecognised.
|
|
313
|
+
*/
|
|
314
|
+
export function hydrateNode(node, ctx) {
|
|
315
|
+
const hydrator = NODE_HYDRATORS[node.kind];
|
|
316
|
+
if (!hydrator) {
|
|
317
|
+
throw new NoeticConfigError({
|
|
318
|
+
code: 'UNKNOWN_NODE_KIND',
|
|
319
|
+
message: `Unknown workflow node kind: '${node.kind}'.`,
|
|
320
|
+
hint: `Supported kinds: ${Object.keys(NODE_HYDRATORS).join(', ')}.`,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return hydrator(node, ctx);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Hydrates a complete `WorkflowDocument` into a live `Step` tree.
|
|
327
|
+
*
|
|
328
|
+
* @public
|
|
329
|
+
* @param doc - Validated workflow document (version 1).
|
|
330
|
+
* @param ctx - Hydration context with tool registry and step executor.
|
|
331
|
+
* @returns The root `Step` of the hydrated workflow.
|
|
332
|
+
*/
|
|
333
|
+
export function hydrateWorkflow(doc, ctx) {
|
|
334
|
+
return hydrateNode(doc.root, ctx);
|
|
335
|
+
}
|
|
336
|
+
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { createMessage, extractAssistantText, trackUsage } from '../
|
|
2
|
+
import { createMessage, extractAssistantText, trackUsage } from '../util/message-helpers';
|
|
3
3
|
import { cosineSimilarity } from './cosine-similarity';
|
|
4
4
|
//#endregion
|
|
5
5
|
//#region Helpers
|
|
@@ -43,6 +43,8 @@ function formatMessage(error) {
|
|
|
43
43
|
return `Budget exceeded: ${error.field} limit ${error.limit}, actual ${error.actual}`;
|
|
44
44
|
case 'steering_denied':
|
|
45
45
|
return `Steering denied${error.guidance ? `: ${error.guidance}` : ''}`;
|
|
46
|
+
case 'handle_evicted':
|
|
47
|
+
return `Subprocess handle '${error.handleId}' evicted (step '${error.stepId}'): adapter returned null for ${error.gracePeriodMs}ms`;
|
|
46
48
|
default: {
|
|
47
49
|
const _exhaustive = error;
|
|
48
50
|
void _exhaustive;
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import type { Context, CwdState } from '
|
|
6
|
-
|
|
7
|
-
import type { FsAdapter } from '../types/fs-adapter';
|
|
8
|
-
import type { HarnessResponse, StreamEvent, StreamingItem } from '../types/harness-result';
|
|
9
|
-
import type { ExecuteInput, Item, ItemSchemaExtensions } from '../types/items';
|
|
10
|
-
import type { ContextMemory, MemoryLayer, StorageAdapter } from '../types/memory';
|
|
11
|
-
import type { Span, TraceExporter } from '../types/observability';
|
|
12
|
-
import type { AgentConfig, AgentHarnessContract, AgentHooks, CallModelRequest, DeliveryMode, ExecuteOptions, HarnessStatus, RecallLayerOutput, SessionScope } from '../types/runtime';
|
|
13
|
-
import type { ShellAdapter } from '../types/shell-adapter';
|
|
14
|
-
import type { SteeringDecision } from '../types/steering';
|
|
15
|
-
import type { Step } from '../types/step';
|
|
16
|
-
import type { SubprocessAdapter } from '../types/subprocess-adapter';
|
|
17
|
-
import type { EventBroadcaster } from './event-broadcaster';
|
|
1
|
+
import { ItemSchemaRegistry } from '../schemas/item';
|
|
2
|
+
import type { DetachedSpawnOverrides } from './deps/interpreter';
|
|
3
|
+
import type { LayerStateStore } from './deps/memory';
|
|
4
|
+
import type { CheckpointStore, EventBroadcaster } from './deps/runtime';
|
|
5
|
+
import type { AgentConfig, AgentHarnessContract, AgentHooks, CallModelRequest, Channel, ChannelHandle, Context, ContextMemory, CwdState, DeliveryMode, DetachedHandle, ExecuteInput, ExecuteOptions, ExternalChannel, FsAdapter, HarnessResponse, HarnessStatus, Item, ItemSchemaExtensions, LLMResponse, LlmProviderConfig, MemoryLayer, RecallLayerOutput, SessionScope, ShellAdapter, Span, SteeringDecision, Step, StorageAdapter, StreamEvent, StreamingItem, SubprocessAdapter, Tool, TraceExporter, ZodType } from './deps/types';
|
|
6
|
+
export { createStreamIdleWatchdog } from './model-call';
|
|
18
7
|
interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<string, unknown>> {
|
|
19
8
|
name: string;
|
|
20
9
|
initialStep?: Step<ContextMemory, string, string>;
|
|
@@ -22,6 +11,13 @@ interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<stri
|
|
|
22
11
|
memory?: MemoryLayer[];
|
|
23
12
|
storage?: StorageAdapter;
|
|
24
13
|
hooks?: AgentHooks;
|
|
14
|
+
/**
|
|
15
|
+
* Harness-wide tool pool. Merged (identity-deduplicated) with tools
|
|
16
|
+
* collected from `initialStep` to form every context's `unifiedTools`.
|
|
17
|
+
* Use this when the workflow step tree is static and tools are supplied
|
|
18
|
+
* per harness instance rather than baked into individual `step.llm` calls.
|
|
19
|
+
*/
|
|
20
|
+
tools?: Tool[];
|
|
25
21
|
params: TParams;
|
|
26
22
|
paramsSchema?: ZodType<TParams>;
|
|
27
23
|
/** Filesystem adapter. Defaults to local node:fs when not provided. */
|
|
@@ -30,6 +26,13 @@ interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<stri
|
|
|
30
26
|
shell?: ShellAdapter;
|
|
31
27
|
/** Subprocess adapter. Defaults to an in-memory, same-process adapter. */
|
|
32
28
|
subprocess?: SubprocessAdapter;
|
|
29
|
+
/**
|
|
30
|
+
* Checkpoint store used by `harness.checkpoint(ctx)` / `harness.restore()`.
|
|
31
|
+
* When absent, checkpoint/restore are no-ops — a zero-config harness keeps
|
|
32
|
+
* its current ephemeral semantics. Construct with `createCheckpointStore`
|
|
33
|
+
* to enable durable execution.
|
|
34
|
+
*/
|
|
35
|
+
checkpointStore?: CheckpointStore;
|
|
33
36
|
llm?: LlmProviderConfig;
|
|
34
37
|
/** Harness-wide item schema extensions. */
|
|
35
38
|
itemSchemas?: ItemSchemaExtensions;
|
|
@@ -54,19 +57,6 @@ interface AgentHarnessOpts<TParams extends Record<string, unknown> = Record<stri
|
|
|
54
57
|
/** @internal Test-only escape hatch to inject a mock callModel implementation. */
|
|
55
58
|
_testCallModel?: (request: CallModelRequest) => Promise<LLMResponse>;
|
|
56
59
|
}
|
|
57
|
-
interface StreamIdleWatchdog {
|
|
58
|
-
/** Bump the deadline because a stream event just arrived. */
|
|
59
|
-
reset: () => void;
|
|
60
|
-
/** Clear the pending timer. Safe to call multiple times. */
|
|
61
|
-
stop: () => void;
|
|
62
|
-
}
|
|
63
|
-
/** @internal Create a watchdog that aborts `controller` when no
|
|
64
|
-
* {@link StreamIdleWatchdog.reset reset} is called within `timeoutMs`. When
|
|
65
|
-
* `timeoutMs <= 0`, returns an inert no-op so callers can always call `.reset()`
|
|
66
|
-
* / `.stop()` without a branch. Starts armed: the caller is responsible for
|
|
67
|
-
* `.stop()` in a `finally`. `onTimeout` runs before the abort so observers can
|
|
68
|
-
* emit a framework event with the original cause. Exported only for unit tests. */
|
|
69
|
-
export declare function createStreamIdleWatchdog(timeoutMs: number, controller: AbortController, onTimeout?: () => void): StreamIdleWatchdog;
|
|
70
60
|
/**
|
|
71
61
|
* Default agent harness for executing agent steps with built-in channel, memory, and trace support.
|
|
72
62
|
* Provides channel store, memory layer lifecycle, and trace export with no external dependencies.
|
|
@@ -82,8 +72,19 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
82
72
|
readonly fs: FsAdapter;
|
|
83
73
|
readonly shell: ShellAdapter;
|
|
84
74
|
readonly subprocess: SubprocessAdapter;
|
|
75
|
+
/**
|
|
76
|
+
* Optional durable-execution store. When present, `checkpoint(ctx)` writes
|
|
77
|
+
* a `CheckpointSnapshot`; when absent, it's a no-op.
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
readonly checkpointStore?: CheckpointStore;
|
|
85
81
|
private readonly initialStep?;
|
|
86
|
-
|
|
82
|
+
/** Harness-wide tool pool merged into every context's `unifiedTools`. */
|
|
83
|
+
private readonly harnessTools;
|
|
84
|
+
/** @internal Memory layers configured for this harness. Exposed non-private
|
|
85
|
+
* so free functions in `runtime/durable/` (checkpoint/restore) can read it
|
|
86
|
+
* without friend-class gymnastics. Do not access from outside core. */
|
|
87
|
+
readonly _memory?: MemoryLayer[];
|
|
87
88
|
private readonly client?;
|
|
88
89
|
private readonly channelStore;
|
|
89
90
|
private readonly callModelOverride?;
|
|
@@ -98,7 +99,10 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
98
99
|
* TUI — observe each other's `cd`s.
|
|
99
100
|
*/
|
|
100
101
|
readonly rootCwdState: CwdState;
|
|
101
|
-
private
|
|
102
|
+
/** @internal Item schema registry. Exposed non-private so free functions
|
|
103
|
+
* in `runtime/durable/` (restore) can parse persisted items. Do not
|
|
104
|
+
* access from outside core. */
|
|
105
|
+
readonly itemSchemas: ItemSchemaRegistry;
|
|
102
106
|
constructor(opts: AgentHarnessOpts<TParams>);
|
|
103
107
|
/**
|
|
104
108
|
* Update the harness-wide root cwd. The TUI calls this in response to a
|
|
@@ -122,20 +126,11 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
122
126
|
private getOrCreateSession;
|
|
123
127
|
private requireSession;
|
|
124
128
|
callModel(request: CallModelRequest): Promise<LLMResponse>;
|
|
125
|
-
/** @internal Drain only messages tagged `between-rounds` from the queue. */
|
|
126
|
-
private drainBetweenRoundsMessages;
|
|
127
129
|
run<I, O>(s: Step<ContextMemory, I, O>, input: I, ctx: Context): Promise<O>;
|
|
128
130
|
/** Run all layer `init` hooks before the first step executes. Per spec 11,
|
|
129
131
|
* init MUST complete before any recall fires so layer state is populated. */
|
|
130
132
|
private initAndRun;
|
|
131
|
-
detachedSpawn<I, O>(s: Step<ContextMemory, I, O>, input: I, parentCtx: Context, overrides?:
|
|
132
|
-
/** Override the child's thread id. Default inherits from `parentCtx.threadId`. */
|
|
133
|
-
threadId?: string;
|
|
134
|
-
/** Override the child's resource id. Default inherits from `parentCtx.resourceId`. */
|
|
135
|
-
resourceId?: string;
|
|
136
|
-
/** Override the child's initial cwd. Used by worktree isolation to root the child at the worktree path. */
|
|
137
|
-
cwdInit?: string;
|
|
138
|
-
}): DetachedHandle<O>;
|
|
133
|
+
detachedSpawn<I, O>(s: Step<ContextMemory, I, O>, input: I, parentCtx: Context, overrides?: DetachedSpawnOverrides): DetachedHandle<O>;
|
|
139
134
|
createContext(opts?: {
|
|
140
135
|
parent?: Context;
|
|
141
136
|
items?: Item[];
|
|
@@ -174,8 +169,34 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
174
169
|
previewRequestItems(scope?: SessionScope): Promise<ReadonlyArray<Item>>;
|
|
175
170
|
storeLayers(layers: MemoryLayer[], response: LLMResponse, ctx: Context): Promise<void>;
|
|
176
171
|
disposeLayers(layers: MemoryLayer[], ctx: Context): Promise<void>;
|
|
177
|
-
|
|
178
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Snapshot the execution state at a checkpoint boundary.
|
|
174
|
+
*
|
|
175
|
+
* Fires at four well-defined points on the happy path:
|
|
176
|
+
* 1. End of every `execute()` that mutated the item log — so a crash
|
|
177
|
+
* between turns lands on a snapshot that includes the user/assistant
|
|
178
|
+
* items that actually flowed.
|
|
179
|
+
* 2. After `detachedSpawn()` settles (success or failure) — the parent's
|
|
180
|
+
* record of running/completed children stays consistent with the
|
|
181
|
+
* adapter's handle manifest.
|
|
182
|
+
* 3. After an ask-user enqueue — a restart can replay the pending modal
|
|
183
|
+
* to the TUI.
|
|
184
|
+
* 4. After `runAppendPipeline` — layer state can mutate as items land,
|
|
185
|
+
* so the snapshot must follow the mutation.
|
|
186
|
+
*
|
|
187
|
+
* Delegates to `captureCheckpoint` / `restoreFromCheckpoint` in
|
|
188
|
+
* `runtime/durable/harness-checkpoints` so the ~140 lines of snapshot
|
|
189
|
+
* logic live beside the other durability machinery.
|
|
190
|
+
*/
|
|
191
|
+
checkpoint(ctx: Context): Promise<void>;
|
|
192
|
+
/**
|
|
193
|
+
* Rebuild a `Context` from a previously-persisted snapshot. Returns `null`
|
|
194
|
+
* if no snapshot is recorded for `executionId`. Surface a typed
|
|
195
|
+
* `NoeticConfigError(CHECKPOINT_SCHEMA_MISMATCH)` when the snapshot's
|
|
196
|
+
* schema version is unrecognised — the caller is expected to discard the
|
|
197
|
+
* checkpoint via `CheckpointStore.clear()` and start a fresh execution.
|
|
198
|
+
*/
|
|
199
|
+
restore(executionId: string): Promise<Context | null>;
|
|
179
200
|
cancel(_ctx: Context, _reason?: string): Promise<void>;
|
|
180
201
|
createSpan(name: string, parent: Span | null): Span;
|
|
181
202
|
getLayerState<T>(executionId: string, layerId: string): T | undefined;
|
|
@@ -203,4 +224,3 @@ export declare class AgentHarness<TParams extends Record<string, unknown> = Reco
|
|
|
203
224
|
tokenCount: number;
|
|
204
225
|
}[]>;
|
|
205
226
|
}
|
|
206
|
-
export {};
|