@noetic-tools/core 3.1.0 → 4.0.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-subprocess/manifest-persistence.d.ts +1 -1
- package/dist/adapters/in-memory-subprocess/types.d.ts +1 -1
- package/dist/adapters/openrouter.d.ts +2 -2
- package/dist/adapters/openrouter.js +2 -2
- package/dist/builders/context-builder.d.ts +11 -0
- package/dist/builders/context-builder.js +15 -0
- package/dist/builders/context-option.d.ts +23 -0
- package/dist/builders/context-option.js +22 -0
- package/dist/builders/control-flow-builders.d.ts +19 -19
- package/dist/builders/every.d.ts +4 -4
- package/dist/builders/layer-provides-builders.d.ts +1 -1
- package/dist/builders/layer-provides-builders.js +1 -1
- package/dist/builders/loop-builder.d.ts +3 -3
- package/dist/builders/provide-builder.d.ts +14 -7
- package/dist/builders/provide-builder.js +15 -4
- package/dist/builders/spawn-builder.d.ts +8 -6
- package/dist/builders/spawn-builder.js +7 -2
- package/dist/builders/step-builders.d.ts +19 -20
- package/dist/builders/tool-builder.d.ts +9 -5
- package/dist/builders/tool-builder.js +3 -2
- package/dist/builders/workflow-hydrator.d.ts +17 -4
- package/dist/builders/workflow-hydrator.js +87 -8
- package/dist/builders/workflow-step.d.ts +61 -0
- package/dist/builders/workflow-step.js +126 -0
- package/dist/conditions/conditions.d.ts +17 -17
- package/dist/conditions/conditions.js +1 -1
- package/dist/deprecated.d.ts +30 -0
- package/dist/deprecated.js +28 -0
- package/dist/harness/agent-harness.d.ts +84 -27
- package/dist/harness/agent-harness.js +101 -20
- package/dist/harness/deps/context.d.ts +2 -0
- package/dist/harness/deps/context.js +1 -0
- package/dist/harness/deps/runtime.d.ts +4 -4
- package/dist/harness/deps/runtime.js +3 -3
- package/dist/harness/deps/types.d.ts +1 -1
- package/dist/harness/model-schema.d.ts +2 -2
- package/dist/index.d.ts +23 -19
- package/dist/index.js +15 -9
- package/dist/interpreter/action-deps.d.ts +3 -3
- package/dist/interpreter/action-deps.js +2 -2
- package/dist/interpreter/action-types.d.ts +1 -1
- package/dist/interpreter/collect-tools.d.ts +1 -1
- package/dist/interpreter/execute-action.d.ts +10 -10
- package/dist/interpreter/execute-action.js +33 -14
- package/dist/interpreter/execute-control.d.ts +4 -4
- package/dist/interpreter/execute-control.js +25 -16
- package/dist/interpreter/execute-sub-harness.d.ts +1 -1
- package/dist/interpreter/execute-sub-harness.js +22 -1
- package/dist/interpreter/execute.d.ts +3 -3
- package/dist/interpreter/execute.js +78 -31
- package/dist/interpreter/step-dispatcher.d.ts +3 -3
- package/dist/interpreter/sub-harness-events.d.ts +2 -2
- package/dist/interpreter/typeguards.d.ts +4 -4
- package/dist/patterns/dynamic-workflow.d.ts +7 -4
- package/dist/patterns/dynamic-workflow.js +2 -0
- package/dist/patterns/interview.d.ts +2 -2
- package/dist/patterns/plans.d.ts +5 -5
- package/dist/patterns/ralph-wiggum.d.ts +2 -2
- package/dist/patterns/react.d.ts +7 -5
- package/dist/patterns/react.js +6 -4
- package/dist/portable/builders.js +2 -0
- package/dist/portable/context-layers.d.ts +8 -0
- package/dist/portable/context-layers.js +8 -0
- package/dist/portable.d.ts +2 -2
- package/dist/portable.js +2 -2
- package/dist/runtime/context-deps.d.ts +2 -2
- package/dist/runtime/context-deps.js +1 -1
- package/dist/runtime/context-impl.d.ts +48 -9
- package/dist/runtime/context-impl.js +95 -10
- package/dist/runtime/context-type-deps.d.ts +1 -1
- package/dist/runtime/durable/checkpoint-store.d.ts +1 -1
- package/dist/runtime/durable/harness-checkpoints.d.ts +17 -4
- package/dist/runtime/durable/harness-checkpoints.js +21 -3
- package/dist/runtime/durable/index.d.ts +2 -0
- package/dist/runtime/durable/index.js +1 -0
- package/dist/runtime/durable/step-ledger.d.ts +107 -5
- package/dist/runtime/durable/step-ledger.js +179 -10
- package/dist/runtime/in-memory-storage.d.ts +1 -1
- package/dist/runtime/session-runner.d.ts +1 -1
- package/dist/runtime/step-registry.d.ts +3 -3
- package/dist/runtime/{tool-memory.d.ts → tool-context.d.ts} +1 -2
- package/dist/runtime/{tool-memory.js → tool-context.js} +5 -2
- package/dist/runtime/tool-ui.d.ts +1 -1
- package/dist/runtime/tool-ui.js +1 -1
- package/dist/schemas/workflow.d.ts +5 -279
- package/dist/schemas/workflow.js +4 -376
- package/dist/types/mutable-context.d.ts +2 -2
- package/dist/types/step-registrar.d.ts +1 -1
- package/dist/unstable.d.ts +5 -5
- package/dist/unstable.js +3 -3
- package/dist/until/predicates.d.ts +1 -1
- package/package.json +4 -4
- package/schema/noetic-workflow.schema.json +28 -0
- package/dist/builders/memory-builder.d.ts +0 -11
- package/dist/builders/memory-builder.js +0 -15
- package/dist/harness/deps/memory.d.ts +0 -2
- package/dist/harness/deps/memory.js +0 -1
- package/dist/patterns/flow.d.ts +0 -2
- package/dist/patterns/flow.js +0 -1
- package/dist/portable/memory-layers.d.ts +0 -8
- package/dist/portable/memory-layers.js +0 -8
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ export { createOpenRouterEmbed } from './adapters/openrouter.js';
|
|
|
12
12
|
/** @public */
|
|
13
13
|
export { channel } from './builders/channel-builder.js';
|
|
14
14
|
/** @public */
|
|
15
|
+
export { context } from './builders/context-builder.js';
|
|
16
|
+
/** @public */
|
|
15
17
|
export { branch, fork } from './builders/control-flow-builders.js';
|
|
16
18
|
/** @public */
|
|
17
19
|
export { every } from './builders/every.js';
|
|
@@ -20,18 +22,16 @@ export { layerData, layerFn } from './builders/layer-provides-builders.js';
|
|
|
20
22
|
/** @public */
|
|
21
23
|
export { loop } from './builders/loop-builder.js';
|
|
22
24
|
/** @public */
|
|
23
|
-
export { memory } from './builders/memory-builder.js';
|
|
24
|
-
/** @public */
|
|
25
25
|
export { provide } from './builders/provide-builder.js';
|
|
26
26
|
/** @public */
|
|
27
27
|
export { spawn } from './builders/spawn-builder.js';
|
|
28
28
|
/** @public */
|
|
29
|
-
export { step } from './builders/step-builders.js';
|
|
30
|
-
/** @public */
|
|
31
29
|
export { tool, toolWithGenerator } from './builders/tool-builder.js';
|
|
32
30
|
/** @public */
|
|
33
31
|
export { hydrateNode, hydrateWorkflow } from './builders/workflow-hydrator.js';
|
|
34
32
|
/** @public */
|
|
33
|
+
export { step } from './builders/workflow-step.js';
|
|
34
|
+
/** @public */
|
|
35
35
|
export { aiCondition, allCondition, anyCondition, embeddingMatch, otherwise, semanticRoute, semanticSwitch, when, } from './conditions/conditions.js';
|
|
36
36
|
/** @public */
|
|
37
37
|
export { cosineSimilarity } from './conditions/cosine-similarity.js';
|
|
@@ -58,7 +58,7 @@ export { execute } from './interpreter/execute.js';
|
|
|
58
58
|
/** @public */
|
|
59
59
|
/** @public */
|
|
60
60
|
/** @public */
|
|
61
|
-
export { durableTaskState, fileReference, findFunctionCall, historyWindow,
|
|
61
|
+
export { durableTaskState, fileReference, findFunctionCall, historyWindow, observationalContext, PlanPhase, planContext, staticContent, steering, storageGetMany, stripUnresolvedToolCalls, temporalContext, toolContextLayer, workingMemoryContext, } from '@noetic-tools/context';
|
|
62
62
|
//#endregion
|
|
63
63
|
//#region Message Utilities
|
|
64
64
|
/** @public */
|
|
@@ -74,8 +74,6 @@ export { createInMemoryStorage } from './runtime/in-memory-storage.js';
|
|
|
74
74
|
/** @public */
|
|
75
75
|
export { dynamicWorkflow, parseAndRunWorkflow } from './patterns/dynamic-workflow.js';
|
|
76
76
|
/** @public */
|
|
77
|
-
export { FlowSchema, flowDepth, validateFlow, walkFlow } from './patterns/flow.js';
|
|
78
|
-
/** @public */
|
|
79
77
|
export { interview } from './patterns/interview.js';
|
|
80
78
|
/** @public */
|
|
81
79
|
export { adaptivePlan, compilePlan, PlanNodeSchema } from './patterns/plans.js';
|
|
@@ -88,7 +86,7 @@ export { react } from './patterns/react.js';
|
|
|
88
86
|
/** @public */
|
|
89
87
|
export { AgentHarness } from './harness/agent-harness.js';
|
|
90
88
|
/** @public */
|
|
91
|
-
export { CheckpointKeys, createCheckpointStore, createDetachedSignal, createNudgeMessage, createStallNudgeHook, DEFAULT_NUDGE_MESSAGE_TEXT, runnableLoop, seedFromItems, } from './runtime/durable/index.js';
|
|
89
|
+
export { CheckpointKeys, createCheckpointStore, createDetachedSignal, createNudgeMessage, createStallNudgeHook, DEFAULT_NUDGE_MESSAGE_TEXT, DEFAULT_STEP_LEDGER_RETENTION, runnableLoop, seedFromItems, } from './runtime/durable/index.js';
|
|
92
90
|
/** @public */
|
|
93
91
|
export { getRegistry, lookupStep, registerStep } from './runtime/step-registry.js';
|
|
94
92
|
//#endregion
|
|
@@ -106,7 +104,7 @@ export { DetachedStatus } from '@noetic-tools/types';
|
|
|
106
104
|
/** @public */
|
|
107
105
|
export { CheckpointSchemaVersion, CheckpointSnapshotSchema, CwdSnapshotSchema, FrontierFrameSchema, ItemLogSnapshotSchema, PendingAskUserSnapshotSchema, } from './types/checkpoint.js';
|
|
108
106
|
/** @public */
|
|
109
|
-
export { Slot } from '@noetic-tools/
|
|
107
|
+
export { Slot } from '@noetic-tools/context';
|
|
110
108
|
/** @public */
|
|
111
109
|
export { LedgerEntryKind, SteeringAction } from '@noetic-tools/types';
|
|
112
110
|
/** @public */
|
|
@@ -122,3 +120,11 @@ export { all, any } from './until/combinators.js';
|
|
|
122
120
|
/** @public */
|
|
123
121
|
export { until } from './until/predicates.js';
|
|
124
122
|
//#endregion
|
|
123
|
+
//#region Deprecated aliases
|
|
124
|
+
/**
|
|
125
|
+
* Pre-rename names for the context layer system — `MemoryLayer`, `memory()`,
|
|
126
|
+
* `workingMemory`, and friends. Each carries its own `@deprecated` pointer to
|
|
127
|
+
* its replacement in `./deprecated`. Removed in the next major.
|
|
128
|
+
*/
|
|
129
|
+
export * from './deprecated.js';
|
|
130
|
+
//#endregion
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export type { LayerStateStore } from '@noetic-tools/
|
|
2
|
-
export { allocateBudgets, assembleView, commitLayerUsage, computeLayerUsage, contextToExecCtx, DEFAULT_PROJECTION, resolveLayerTools, returnLayers, spawnLayers, } from '@noetic-tools/
|
|
1
|
+
export type { LayerStateStore } from '@noetic-tools/context';
|
|
2
|
+
export { allocateBudgets, assembleView, commitLayerUsage, computeLayerUsage, contextToExecCtx, DEFAULT_PROJECTION, resolveLayerTools, returnLayers, spawnLayers, } from '@noetic-tools/context';
|
|
3
3
|
export type { ItemSchemaRegistry } from '@noetic-tools/types';
|
|
4
4
|
export { defaultItemSchemaRegistry } from '@noetic-tools/types';
|
|
5
5
|
export { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils.js';
|
|
6
6
|
export { ContextImpl } from '../runtime/context-impl.js';
|
|
7
7
|
export { snapshotCwdState } from '../runtime/cwd-helpers.js';
|
|
8
|
-
export { buildToolExecutionContext } from '../runtime/tool-
|
|
8
|
+
export { buildToolExecutionContext } from '../runtime/tool-context.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { allocateBudgets, assembleView, commitLayerUsage, computeLayerUsage, contextToExecCtx, DEFAULT_PROJECTION, resolveLayerTools, returnLayers, spawnLayers, } from '@noetic-tools/
|
|
1
|
+
export { allocateBudgets, assembleView, commitLayerUsage, computeLayerUsage, contextToExecCtx, DEFAULT_PROJECTION, resolveLayerTools, returnLayers, spawnLayers, } from '@noetic-tools/context';
|
|
2
2
|
export { defaultItemSchemaRegistry } from '@noetic-tools/types';
|
|
3
3
|
export { emitFrameworkEvent, getBroadcaster, shouldEmit } from '../runtime/broadcaster-utils.js';
|
|
4
4
|
export { ContextImpl } from '../runtime/context-impl.js';
|
|
5
5
|
export { snapshotCwdState } from '../runtime/cwd-helpers.js';
|
|
6
|
-
export { buildToolExecutionContext } from '../runtime/tool-
|
|
6
|
+
export { buildToolExecutionContext } from '../runtime/tool-context.js';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { ContextConfig, ContextData, ContextLayer, ExecutionContext, } from '@noetic-tools/context';
|
|
2
2
|
export type { AgentHarnessContract, Context, ContextRerenderRequest, ExecuteStepFn, FunctionCallItem, Item, ProjectionPolicy, RecallLayerOutput, RetryPolicy, ServerToolSpec, StepLLM, StepMeta, StepProvide, StepRun, StepSpawn, StepTool, Tool, } from '@noetic-tools/types';
|
|
3
3
|
export { isServerToolSpec, SteeringAction } from '@noetic-tools/types';
|
|
@@ -4,7 +4,7 @@ import type { Step, Tool } from '@noetic-tools/types';
|
|
|
4
4
|
* declared on LLM steps. Used to build the unified tool set
|
|
5
5
|
* that gets sent with every LLM call for prompt cache efficiency.
|
|
6
6
|
*/
|
|
7
|
-
export declare function collectAllTools<
|
|
7
|
+
export declare function collectAllTools<TContext = unknown, I = unknown, O = unknown>(step: Step<TContext, I, O>): Tool[];
|
|
8
8
|
/**
|
|
9
9
|
* Deduplicates tools by name, keeping the first occurrence.
|
|
10
10
|
*/
|
|
@@ -2,31 +2,31 @@
|
|
|
2
2
|
* Action step handlers: run, llm, spawn, provide, tool.
|
|
3
3
|
*/
|
|
4
4
|
import type { ItemSchemaRegistry, LayerStateStore } from './action-deps.js';
|
|
5
|
-
import type { AgentHarnessContract, Context,
|
|
6
|
-
export declare function executeRun<
|
|
5
|
+
import type { AgentHarnessContract, Context, ContextLayer, ExecuteStepFn, StepLLM, StepProvide, StepRun, StepSpawn, StepTool } from './action-types.js';
|
|
6
|
+
export declare function executeRun<TContext, I, O>(step: StepRun<TContext, I, O>, input: I, ctx: Context<TContext>): Promise<O>;
|
|
7
7
|
/**
|
|
8
8
|
* Resolves a `Lazy<T>` step field. If the value is a function, it is invoked
|
|
9
9
|
* with the current context; otherwise the value is returned as-is. Supports
|
|
10
10
|
* both sync and async getters.
|
|
11
11
|
*
|
|
12
12
|
* Lazy fields let a step inspect `ctx.harness.config.params`, `ctx.unifiedTools`,
|
|
13
|
-
* or
|
|
13
|
+
* or context layer state to produce per-execution values (model, instructions,
|
|
14
14
|
* tools) without baking them in at build time.
|
|
15
15
|
*/
|
|
16
|
-
export declare function resolveLazy<T,
|
|
17
|
-
export declare function executeLLM<
|
|
16
|
+
export declare function resolveLazy<T, TContext>(value: T | ((ctx: Context<TContext>) => T | Promise<T>), ctx: Context<TContext>): Promise<T>;
|
|
17
|
+
export declare function executeLLM<TContext, I, O>(step: StepLLM<TContext, I, O>, input: I, ctx: Context<TContext>, layers?: ContextLayer[]): Promise<O>;
|
|
18
18
|
export interface ExecuteSpawnOpts {
|
|
19
19
|
layerStore?: LayerStateStore;
|
|
20
|
-
parentLayers?:
|
|
20
|
+
parentLayers?: ContextLayer[];
|
|
21
21
|
itemSchemas?: ItemSchemaRegistry;
|
|
22
22
|
}
|
|
23
|
-
export declare function executeSpawn<
|
|
23
|
+
export declare function executeSpawn<TContext, I, O>(step: StepSpawn<TContext, I, O>, input: I, ctx: Context<TContext>, executeStep: ExecuteStepFn, opts?: ExecuteSpawnOpts): Promise<O>;
|
|
24
24
|
/**
|
|
25
|
-
* Executes a provide step by attaching
|
|
25
|
+
* Executes a provide step by attaching context layers to the current context
|
|
26
26
|
* without creating an isolated child context.
|
|
27
27
|
*
|
|
28
28
|
* Unlike spawn, provide does not create a new itemLog or clone state.
|
|
29
29
|
* Events flow through to the parent in real-time.
|
|
30
30
|
*/
|
|
31
|
-
export declare function executeProvide<
|
|
32
|
-
export declare function executeTool<
|
|
31
|
+
export declare function executeProvide<TContext, I, O>(step: StepProvide<TContext, I, O>, input: I, ctx: Context<TContext>, executeStep: ExecuteStepFn): Promise<O>;
|
|
32
|
+
export declare function executeTool<TContext, I, O>(step: StepTool<TContext, I, O>, input: I, ctx: Context<TContext>, harness: AgentHarnessContract, layers?: ContextLayer[]): Promise<O>;
|
|
@@ -9,7 +9,7 @@ import { isServerToolSpec, SteeringAction } from './action-types.js';
|
|
|
9
9
|
import { cloneWithGuard } from './clone-guard.js';
|
|
10
10
|
import { collectAllTools, deduplicateTools } from './collect-tools.js';
|
|
11
11
|
import { trackUsage } from './message-helpers.js';
|
|
12
|
-
import { getContextChannelStore, isFunctionCall, isMutableContext } from './typeguards.js';
|
|
12
|
+
import { getContextChannelStore, isContextImpl, isFunctionCall, isMutableContext, } from './typeguards.js';
|
|
13
13
|
//#region run
|
|
14
14
|
export async function executeRun(step, input, ctx) {
|
|
15
15
|
const retry = step.retry;
|
|
@@ -93,7 +93,7 @@ function mergeRecallResults(base, overrides, slotOf) {
|
|
|
93
93
|
* both sync and async getters.
|
|
94
94
|
*
|
|
95
95
|
* Lazy fields let a step inspect `ctx.harness.config.params`, `ctx.unifiedTools`,
|
|
96
|
-
* or
|
|
96
|
+
* or context layer state to produce per-execution values (model, instructions,
|
|
97
97
|
* tools) without baking them in at build time.
|
|
98
98
|
*/
|
|
99
99
|
export async function resolveLazy(value, ctx) {
|
|
@@ -266,7 +266,7 @@ export async function executeLLM(step, input, ctx, layers) {
|
|
|
266
266
|
const hasLayers = layers !== undefined && layers.length > 0;
|
|
267
267
|
// Resolve lazy step fields up-front. Function-form getters run once per
|
|
268
268
|
// step execution with the live context, so `ctx.harness.config.params`,
|
|
269
|
-
// `ctx.unifiedTools`, and
|
|
269
|
+
// `ctx.unifiedTools`, and context layer state are all visible at resolution.
|
|
270
270
|
const resolvedModel = await resolveLazy(step.model, ctx);
|
|
271
271
|
if (!resolvedModel || resolvedModel.trim() === '') {
|
|
272
272
|
throw new NoeticConfigError({
|
|
@@ -400,6 +400,11 @@ export async function executeLLM(step, input, ctx, layers) {
|
|
|
400
400
|
];
|
|
401
401
|
}
|
|
402
402
|
const _serverTools = serverToolSpecs.length > 0 ? serverToolSpecs : undefined;
|
|
403
|
+
// Cancellation reaches INSIDE the model call, not just the step boundary:
|
|
404
|
+
// the caller's abort signal stops the stream and the tool-round loop mid
|
|
405
|
+
// flight, so `ctx.abort()` / `harness.cancel()` don't wait out a long
|
|
406
|
+
// generation before taking effect (spec 09, Cancellation item 2).
|
|
407
|
+
const signal = isContextImpl(baseCtx) ? baseCtx.abortSignal : undefined;
|
|
403
408
|
const request = resolvedTools
|
|
404
409
|
? {
|
|
405
410
|
model: resolvedModel,
|
|
@@ -415,6 +420,7 @@ export async function executeLLM(step, input, ctx, layers) {
|
|
|
415
420
|
allowedToolNames,
|
|
416
421
|
nodeId: step.id,
|
|
417
422
|
parentSpan: baseCtx.span,
|
|
423
|
+
signal,
|
|
418
424
|
}
|
|
419
425
|
: {
|
|
420
426
|
model: resolvedModel,
|
|
@@ -426,6 +432,7 @@ export async function executeLLM(step, input, ctx, layers) {
|
|
|
426
432
|
_serverTools,
|
|
427
433
|
nodeId: step.id,
|
|
428
434
|
parentSpan: baseCtx.span,
|
|
435
|
+
signal,
|
|
429
436
|
};
|
|
430
437
|
const response = await baseCtx.harness.callModel(request);
|
|
431
438
|
// Track tokens/cost for EVERY model call, including responses steering
|
|
@@ -433,6 +440,15 @@ export async function executeLLM(step, input, ctx, layers) {
|
|
|
433
440
|
// real, and until.maxCost / HarnessResponse.usage must see it (spec 07:
|
|
434
441
|
// tokens accumulate across all LLM calls).
|
|
435
442
|
trackUsage(baseCtx, response);
|
|
443
|
+
// An abort that landed mid-call returns whatever rounds completed before
|
|
444
|
+
// the signal fired. The spend is charged above, but a truncated generation
|
|
445
|
+
// is not this step's output — surface cancellation instead.
|
|
446
|
+
if (baseCtx.aborted) {
|
|
447
|
+
throw new NoeticErrorImpl({
|
|
448
|
+
kind: 'cancelled',
|
|
449
|
+
reason: baseCtx.abortReason ?? 'context aborted',
|
|
450
|
+
});
|
|
451
|
+
}
|
|
436
452
|
if (hasLayers) {
|
|
437
453
|
const decision = await baseCtx.harness.afterModelCall(layers, response, baseCtx);
|
|
438
454
|
if (decision.action === SteeringAction.Deny) {
|
|
@@ -493,19 +509,19 @@ export async function executeLLM(step, input, ctx, layers) {
|
|
|
493
509
|
retriesExhausted: true,
|
|
494
510
|
});
|
|
495
511
|
}
|
|
496
|
-
function
|
|
512
|
+
function isContextConfig(value) {
|
|
497
513
|
return typeof value === 'object' && value !== null && 'layers' in value;
|
|
498
514
|
}
|
|
499
515
|
function resolveLayersForSpawn(step, parentLayers) {
|
|
500
|
-
if (!step.
|
|
516
|
+
if (!step.context) {
|
|
501
517
|
return parentLayers ?? [];
|
|
502
518
|
}
|
|
503
|
-
if (
|
|
519
|
+
if (isContextConfig(step.context)) {
|
|
504
520
|
return [
|
|
505
|
-
...step.
|
|
521
|
+
...step.context.layers,
|
|
506
522
|
];
|
|
507
523
|
}
|
|
508
|
-
return step.
|
|
524
|
+
return step.context;
|
|
509
525
|
}
|
|
510
526
|
async function collectSpawnItems({ layers, parentExecutionCtx, childExecutionCtx, layerStore, itemSchemas = defaultItemSchemaRegistry, }) {
|
|
511
527
|
const spawnResults = await spawnLayers({
|
|
@@ -547,7 +563,7 @@ export async function executeSpawn(step, input, ctx, executeStep, opts) {
|
|
|
547
563
|
});
|
|
548
564
|
const layerStore = opts?.layerStore;
|
|
549
565
|
const hasLayers = layers.length > 0 && layerStore !== undefined;
|
|
550
|
-
// Collect items from
|
|
566
|
+
// Collect items from context layers via onSpawn hooks
|
|
551
567
|
let childItems = [];
|
|
552
568
|
let parentExecutionCtx;
|
|
553
569
|
if (hasLayers) {
|
|
@@ -578,7 +594,7 @@ export async function executeSpawn(step, input, ctx, executeStep, opts) {
|
|
|
578
594
|
// Create child context — empty by default, layers provide items via onSpawn.
|
|
579
595
|
// `id: childId` keeps the ContextImpl's id in sync with the executionId
|
|
580
596
|
// used by spawnLayers/returnLayers in the layer-state store, so writes via
|
|
581
|
-
// `ctx.
|
|
597
|
+
// `ctx.context[layerId]` and spawn-boundary hooks read from the same key.
|
|
582
598
|
const childCtx = new ContextImpl({
|
|
583
599
|
harness: baseCtx.harness,
|
|
584
600
|
parent: baseCtx,
|
|
@@ -624,17 +640,20 @@ export async function executeSpawn(step, input, ctx, executeStep, opts) {
|
|
|
624
640
|
if (hasLayers) {
|
|
625
641
|
layerStore.cleanup(childExecutionCtx.executionId);
|
|
626
642
|
}
|
|
643
|
+
// The child is settled: leave the parent's abort cascade so a parent that
|
|
644
|
+
// outlives many spawns does not retain every finished child.
|
|
645
|
+
childCtx.detachFromParent();
|
|
627
646
|
}
|
|
628
647
|
}
|
|
629
648
|
//#endregion
|
|
630
649
|
//#region provide
|
|
631
650
|
function resolveLayers(step) {
|
|
632
|
-
if (
|
|
651
|
+
if (isContextConfig(step.context)) {
|
|
633
652
|
return [
|
|
634
|
-
...step.
|
|
653
|
+
...step.context.layers,
|
|
635
654
|
];
|
|
636
655
|
}
|
|
637
|
-
return step.
|
|
656
|
+
return step.context;
|
|
638
657
|
}
|
|
639
658
|
function mergeLayers(existing, provided) {
|
|
640
659
|
if (!existing || existing.length === 0) {
|
|
@@ -649,7 +668,7 @@ function mergeLayers(existing, provided) {
|
|
|
649
668
|
];
|
|
650
669
|
}
|
|
651
670
|
/**
|
|
652
|
-
* Executes a provide step by attaching
|
|
671
|
+
* Executes a provide step by attaching context layers to the current context
|
|
653
672
|
* without creating an isolated child context.
|
|
654
673
|
*
|
|
655
674
|
* Unlike spawn, provide does not create a new itemLog or clone state.
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Context, ExecuteStepFn, StepBranch, StepEvery, StepFork, StepLoop } from '@noetic-tools/types';
|
|
5
5
|
import type { ItemSchemaRegistry, LayerStateStore } from './action-deps.js';
|
|
6
|
-
export declare function executeBranch<
|
|
6
|
+
export declare function executeBranch<TContext, I, O>(step: StepBranch<TContext, I, O>, input: I, ctx: Context<TContext>, executeStep: ExecuteStepFn): Promise<O>;
|
|
7
7
|
/** Layer-boundary wiring supplied by the interpreter (see `buildSpawnOpts`). */
|
|
8
8
|
export interface ExecuteForkOpts {
|
|
9
9
|
layerStore?: LayerStateStore;
|
|
10
10
|
itemSchemas?: ItemSchemaRegistry;
|
|
11
11
|
}
|
|
12
|
-
export declare function executeFork<
|
|
13
|
-
export declare function executeLoop<
|
|
12
|
+
export declare function executeFork<TContext, I, O>(step: StepFork<TContext, I, O>, input: I, ctx: Context<TContext>, rawExecuteStep: ExecuteStepFn, opts?: ExecuteForkOpts): Promise<O>;
|
|
13
|
+
export declare function executeLoop<TContext, I, O>(step: StepLoop<TContext, I, O>, input: I, ctx: Context<TContext>, executeStep: ExecuteStepFn): Promise<O>;
|
|
14
14
|
/**
|
|
15
15
|
* Executes an `every` step: runs the body step forever, paced by `ms ± jitter`,
|
|
16
16
|
* woken early by `wakeOn`. Throws `cancelled` when the context is aborted.
|
|
@@ -23,4 +23,4 @@ export declare function executeLoop<TMemory, I, O>(step: StepLoop<TMemory, I, O>
|
|
|
23
23
|
* The body's output is discarded — `every` runs forever and does not accumulate
|
|
24
24
|
* iteration outputs. Only ever returns by throwing on cancellation or `fail`.
|
|
25
25
|
*/
|
|
26
|
-
export declare function executeEvery<
|
|
26
|
+
export declare function executeEvery<TContext, I, O>(step: StepEvery<TContext, I, O>, input: I, ctx: Context<TContext>, executeStep: ExecuteStepFn): Promise<O>;
|
|
@@ -44,7 +44,7 @@ function createChildContexts(ctx, count, stepId) {
|
|
|
44
44
|
channelStore,
|
|
45
45
|
cwdState: snapshotCwdState(ctx),
|
|
46
46
|
// Layers and the harness tool pool cross the fork boundary: without
|
|
47
|
-
// them an `llm` step inside a path would run with no
|
|
47
|
+
// them an `llm` step inside a path would run with no context
|
|
48
48
|
// projection and no layer tools, and a nested `spawn` would have no
|
|
49
49
|
// parent layers to inherit. Per-path layer STATE is still isolated —
|
|
50
50
|
// see `createForkLayerBridge`.
|
|
@@ -143,21 +143,30 @@ export async function executeFork(step, input, ctx, rawExecuteStep, opts) {
|
|
|
143
143
|
childContexts,
|
|
144
144
|
opts,
|
|
145
145
|
}));
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
146
|
+
try {
|
|
147
|
+
switch (step.mode) {
|
|
148
|
+
case 'all':
|
|
149
|
+
return await executeAll(step, paths, input, ctx, childContexts, executeStep, concurrency);
|
|
150
|
+
case 'race':
|
|
151
|
+
return await executeRace(step, paths, input, ctx, childContexts, executeStep, concurrency);
|
|
152
|
+
case 'settle':
|
|
153
|
+
return await executeSettle(step, paths, input, ctx, childContexts, executeStep, concurrency);
|
|
154
|
+
default: {
|
|
155
|
+
const _exhaustive = step;
|
|
156
|
+
throw new NoeticErrorImpl({
|
|
157
|
+
kind: 'step_failed',
|
|
158
|
+
stepId: 'unknown',
|
|
159
|
+
cause: new Error('Unknown fork mode'),
|
|
160
|
+
retriesExhausted: false,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
// Every path has settled (or been abandoned in `race`): leave the parent's
|
|
167
|
+
// abort cascade so it retains only children that are still cancellable.
|
|
168
|
+
for (const child of childContexts) {
|
|
169
|
+
child.detachFromParent();
|
|
161
170
|
}
|
|
162
171
|
}
|
|
163
172
|
}
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* `executeLLM`.
|
|
6
6
|
*/
|
|
7
7
|
import type { Context, StepSubHarness } from '@noetic-tools/types';
|
|
8
|
-
export declare function executeSubHarness<
|
|
8
|
+
export declare function executeSubHarness<TContext, I, O>(step: StepSubHarness<TContext, I, O>, input: I, ctx: Context<TContext>): Promise<O>;
|
|
@@ -9,7 +9,7 @@ import { ZodError } from 'zod';
|
|
|
9
9
|
import { resolveLazy } from './execute-action.js';
|
|
10
10
|
import { trackUsage } from './message-helpers.js';
|
|
11
11
|
import { SubHarnessEventBridge } from './sub-harness-events.js';
|
|
12
|
-
import { isFunctionCall, isMutableContext } from './typeguards.js';
|
|
12
|
+
import { isContextImpl, isFunctionCall, isMutableContext } from './typeguards.js';
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region Helpers
|
|
15
15
|
function sessionStore(ctx) {
|
|
@@ -39,6 +39,14 @@ function resolveTurnText(resolvedPrompt, input) {
|
|
|
39
39
|
}
|
|
40
40
|
return typeof input === 'string' ? input : '';
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Abort signal of the executing context, when it is a framework context. Handed
|
|
44
|
+
* to the adapter so `ctx.abort()` / `harness.cancel()` stops the coding agent's
|
|
45
|
+
* in-flight turn instead of leaving a sub-process running until it finishes.
|
|
46
|
+
*/
|
|
47
|
+
function abortSignalOf(ctx) {
|
|
48
|
+
return isContextImpl(ctx) ? ctx.abortSignal : undefined;
|
|
49
|
+
}
|
|
42
50
|
function buildRunContext(ctx) {
|
|
43
51
|
return {
|
|
44
52
|
cwd: ctx.cwdState.cwd,
|
|
@@ -46,6 +54,7 @@ function buildRunContext(ctx) {
|
|
|
46
54
|
shell: ctx.shell,
|
|
47
55
|
subprocess: ctx.subprocess,
|
|
48
56
|
threadId: ctx.threadId,
|
|
57
|
+
signal: abortSignalOf(ctx),
|
|
49
58
|
};
|
|
50
59
|
}
|
|
51
60
|
function toLlmResponse(result) {
|
|
@@ -108,6 +117,7 @@ async function startOrReuseSession(step, harness, ctx, baseCtx, history) {
|
|
|
108
117
|
instructions: await resolveLazy(step.instructions, ctx),
|
|
109
118
|
history,
|
|
110
119
|
ctx: buildRunContext(baseCtx),
|
|
120
|
+
signal: abortSignalOf(baseCtx),
|
|
111
121
|
});
|
|
112
122
|
if (reuseKey) {
|
|
113
123
|
store.set(reuseKey, session);
|
|
@@ -167,6 +177,9 @@ export async function executeSubHarness(step, input, ctx) {
|
|
|
167
177
|
result = await resolution.session.doPromptTurn({
|
|
168
178
|
prompt: turnText,
|
|
169
179
|
emit: (part) => bridge.forward(part),
|
|
180
|
+
// Per-turn signal, so a session reused across turns is cancelled by the
|
|
181
|
+
// context running the CURRENT turn rather than the one that started it.
|
|
182
|
+
signal: abortSignalOf(baseCtx),
|
|
170
183
|
});
|
|
171
184
|
}
|
|
172
185
|
catch (e) {
|
|
@@ -178,6 +191,14 @@ export async function executeSubHarness(step, input, ctx) {
|
|
|
178
191
|
if (e instanceof NoeticErrorImpl) {
|
|
179
192
|
throw e;
|
|
180
193
|
}
|
|
194
|
+
// An adapter interrupted by the abort signal rejects with whatever its SDK
|
|
195
|
+
// throws; the context is the authority on why the turn ended.
|
|
196
|
+
if (baseCtx.aborted) {
|
|
197
|
+
throw new NoeticErrorImpl({
|
|
198
|
+
kind: 'cancelled',
|
|
199
|
+
reason: baseCtx.abortReason ?? 'context aborted',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
181
202
|
throw new NoeticErrorImpl({
|
|
182
203
|
kind: 'step_failed',
|
|
183
204
|
stepId: step.id,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContextData } from '@noetic-tools/context';
|
|
2
2
|
import type { Context, Step } from '@noetic-tools/types';
|
|
3
3
|
/**
|
|
4
4
|
* @internal
|
|
@@ -12,7 +12,7 @@ import type { Context, Step } from '@noetic-tools/types';
|
|
|
12
12
|
* through the adapter as normal, so per-step overrides on descendants
|
|
13
13
|
* continue to work.
|
|
14
14
|
*/
|
|
15
|
-
export declare function executeNoAdapter<
|
|
15
|
+
export declare function executeNoAdapter<TContext, I, O>(step: Step<TContext, I, O>, input: I, ctx: Context<TContext>): Promise<O>;
|
|
16
16
|
/**
|
|
17
17
|
* Executes a step within the interpreter, dispatching to the appropriate handler by step kind.
|
|
18
18
|
*
|
|
@@ -23,4 +23,4 @@ export declare function executeNoAdapter<TMemory, I, O>(step: Step<TMemory, I, O
|
|
|
23
23
|
* @throws `NoeticError` with kind `step_failed` if max depth is exceeded or an unknown step kind is encountered.
|
|
24
24
|
* @throws `NoeticError` with kind `cancelled` if the context is aborted.
|
|
25
25
|
*/
|
|
26
|
-
export declare function execute<
|
|
26
|
+
export declare function execute<TContext = ContextData, I = unknown, O = unknown>(step: Step<TContext, I, O>, input: I, ctx: Context<TContext>): Promise<O>;
|
|
@@ -112,6 +112,65 @@ export async function executeNoAdapter(step, input, ctx) {
|
|
|
112
112
|
return execute(step, input, ctx);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Resume path: when a previous run recorded this exact step at `ledgerPath`,
|
|
117
|
+
* replay its output instead of re-running. Held as a helper (rather than inline
|
|
118
|
+
* in `execute`) so the resume branch stays flat: it pops the frontier itself —
|
|
119
|
+
* the caller returns before the normal `leaveStep` runs — and emits
|
|
120
|
+
* `step_replayed`. Returns `{ hit: false }` when there is nothing to replay so
|
|
121
|
+
* the caller dispatches the step normally.
|
|
122
|
+
*/
|
|
123
|
+
function replayFromLedger(opts) {
|
|
124
|
+
const { ledger, ledgerPath, impl, step, broadcaster, agentName } = opts;
|
|
125
|
+
if (!ledger || ledgerPath === undefined || ledger.isEmpty) {
|
|
126
|
+
return {
|
|
127
|
+
hit: false,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const replayed = ledger.take(ledgerPath, {
|
|
131
|
+
id: step.id,
|
|
132
|
+
kind: step.kind,
|
|
133
|
+
});
|
|
134
|
+
if (!replayed) {
|
|
135
|
+
return {
|
|
136
|
+
hit: false,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (impl && step.id.length > 0) {
|
|
140
|
+
impl.leaveStep(step.id);
|
|
141
|
+
}
|
|
142
|
+
emitFrameworkEvent({
|
|
143
|
+
broadcaster,
|
|
144
|
+
agentName,
|
|
145
|
+
eventType: 'step_replayed',
|
|
146
|
+
data: {
|
|
147
|
+
stepId: step.id,
|
|
148
|
+
kind: step.kind,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
hit: true,
|
|
153
|
+
output: frameworkCast(replayed.output),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Record a completed step's output so a resumed run replays it rather than
|
|
158
|
+
* re-executing. A no-op without a ledger or ledger path. Only successes reach
|
|
159
|
+
* here — a step that threw skips this call in `execute` and runs again on resume.
|
|
160
|
+
*/
|
|
161
|
+
async function recordToLedger(opts) {
|
|
162
|
+
const { ledger, ledgerPath, step, output } = opts;
|
|
163
|
+
if (!ledger || ledgerPath === undefined) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
await ledger.record({
|
|
167
|
+
path: ledgerPath,
|
|
168
|
+
stepId: step.id,
|
|
169
|
+
kind: step.kind,
|
|
170
|
+
output,
|
|
171
|
+
completedAt: new Date().toISOString(),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
115
174
|
//#endregion
|
|
116
175
|
/**
|
|
117
176
|
* Executes a step within the interpreter, dispatching to the appropriate handler by step kind.
|
|
@@ -173,30 +232,21 @@ export async function execute(step, input, ctx) {
|
|
|
173
232
|
});
|
|
174
233
|
}
|
|
175
234
|
/* Resume: a previous run that completed this exact step replays its recorded
|
|
176
|
-
* output instead of re-running it.
|
|
177
|
-
* record below, because `leaveStep` pops the path
|
|
235
|
+
* output instead of re-running it. `ledgerPath`/`ledger` are captured before
|
|
236
|
+
* dispatch and reused by the record below, because `leaveStep` pops the path
|
|
237
|
+
* on the way out. */
|
|
178
238
|
const ledgerPath = impl && step.id.length > 0 ? impl.currentPath() : undefined;
|
|
179
239
|
const ledger = impl?.ledger;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
broadcaster,
|
|
191
|
-
agentName,
|
|
192
|
-
eventType: 'step_replayed',
|
|
193
|
-
data: {
|
|
194
|
-
stepId: step.id,
|
|
195
|
-
kind: step.kind,
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
return frameworkCast(replayed.output);
|
|
199
|
-
}
|
|
240
|
+
const replay = replayFromLedger({
|
|
241
|
+
ledger,
|
|
242
|
+
ledgerPath,
|
|
243
|
+
impl,
|
|
244
|
+
step,
|
|
245
|
+
broadcaster,
|
|
246
|
+
agentName,
|
|
247
|
+
});
|
|
248
|
+
if (replay.hit) {
|
|
249
|
+
return replay.output;
|
|
200
250
|
}
|
|
201
251
|
let result;
|
|
202
252
|
try {
|
|
@@ -272,15 +322,12 @@ export async function execute(step, input, ctx) {
|
|
|
272
322
|
/* Record the completed step so a resumed run replays this output rather than
|
|
273
323
|
* re-running the step. Only successes are recorded: a step that threw must run
|
|
274
324
|
* again. */
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
completedAt: new Date().toISOString(),
|
|
282
|
-
});
|
|
283
|
-
}
|
|
325
|
+
await recordToLedger({
|
|
326
|
+
ledger,
|
|
327
|
+
ledgerPath,
|
|
328
|
+
step,
|
|
329
|
+
output: result,
|
|
330
|
+
});
|
|
284
331
|
/* Durability boundary (spec 23): snapshot after each completed step, so a crash
|
|
285
332
|
* lands on a checkpoint that includes the item-log and layer-state mutations this
|
|
286
333
|
* step produced. A no-op unless the harness has a CheckpointStore, and a failing
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContextData } from '@noetic-tools/context';
|
|
2
2
|
import type { Context, DetachedHandle, Step, SubprocessAdapter } from '@noetic-tools/types';
|
|
3
3
|
/**
|
|
4
4
|
* Resolve the subprocess adapter used for a given step dispatch.
|
|
@@ -10,7 +10,7 @@ import type { Context, DetachedHandle, Step, SubprocessAdapter } from '@noetic-t
|
|
|
10
10
|
*
|
|
11
11
|
* @internal
|
|
12
12
|
*/
|
|
13
|
-
export declare function resolveStepAdapter<
|
|
13
|
+
export declare function resolveStepAdapter<TContext, I, O>(step: Step<TContext, I, O>, callOverride: SubprocessAdapter | undefined, fallback: SubprocessAdapter): SubprocessAdapter;
|
|
14
14
|
/**
|
|
15
15
|
* Minimum harness surface the dispatcher needs. `AgentHarness` satisfies
|
|
16
16
|
* this shape — kept structural so tests can pass a stub.
|
|
@@ -55,4 +55,4 @@ export interface DetachedSpawnOverrides {
|
|
|
55
55
|
*
|
|
56
56
|
* @internal
|
|
57
57
|
*/
|
|
58
|
-
export declare function dispatchStepThroughAdapter<I, O>(h: StepDispatchHandle, s: Step<
|
|
58
|
+
export declare function dispatchStepThroughAdapter<I, O>(h: StepDispatchHandle, s: Step<ContextData, I, O>, input: I, parentCtx: Context, overrides?: DetachedSpawnOverrides): DetachedHandle<O>;
|