@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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* a result without streaming any text/tool parts, the result's content is
|
|
13
13
|
* synthesized into events.
|
|
14
14
|
*/
|
|
15
|
-
import type { Context,
|
|
15
|
+
import type { Context, ContextData, StepSubHarness, SubHarnessStreamPart, SubHarnessTurnResult } from '@noetic-tools/types';
|
|
16
16
|
export declare class SubHarnessEventBridge {
|
|
17
17
|
private readonly broadcaster?;
|
|
18
18
|
private readonly agentName;
|
|
@@ -25,7 +25,7 @@ export declare class SubHarnessEventBridge {
|
|
|
25
25
|
private messageOutputIndex;
|
|
26
26
|
private sawText;
|
|
27
27
|
private sawToolCall;
|
|
28
|
-
constructor(step: Pick<StepSubHarness, 'id' | 'kind' | 'emit'>, ctx: Context<
|
|
28
|
+
constructor(step: Pick<StepSubHarness, 'id' | 'kind' | 'emit'>, ctx: Context<ContextData>);
|
|
29
29
|
/**
|
|
30
30
|
* Open the turn on the event surface. Emits `response.created` so every turn
|
|
31
31
|
* always brackets with a lifecycle marker — even a custom adapter that
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContextData } from '@noetic-tools/context';
|
|
2
2
|
import type { Context, FunctionCallItem, Item } from '@noetic-tools/types';
|
|
3
3
|
import type { ChannelStore } from '../runtime/channel-store.js';
|
|
4
4
|
import { ContextImpl } from '../runtime/context-impl.js';
|
|
5
5
|
import type { MutableContext } from '../types/mutable-context.js';
|
|
6
6
|
export { isAssistantMessage, isOutputText, isUserMessage } from '@noetic-tools/types';
|
|
7
|
-
export declare function isMutableContext(ctx: Context<
|
|
8
|
-
export declare function isContextImpl(ctx: Context<
|
|
7
|
+
export declare function isMutableContext(ctx: Context<ContextData>): ctx is MutableContext;
|
|
8
|
+
export declare function isContextImpl(ctx: Context<ContextData>): ctx is ContextImpl;
|
|
9
9
|
/**
|
|
10
10
|
* Co-located with `isContextImpl` so the import of `ContextImpl` flows through
|
|
11
11
|
* the same module other interpreters pull it from, sidestepping a circular
|
|
12
12
|
* TDZ when this helper is used by `executeEvery` in `execute-control.ts`.
|
|
13
13
|
*/
|
|
14
|
-
export declare function getContextChannelStore<
|
|
14
|
+
export declare function getContextChannelStore<TContext>(ctx: Context<TContext>): ChannelStore | undefined;
|
|
15
15
|
export declare function isFunctionCall(item: Item): item is FunctionCallItem;
|
|
16
16
|
declare function assertNever(_value: never): never;
|
|
17
17
|
export { assertNever };
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Pattern that lets an LLM generate a workflow as JSON during execution,
|
|
3
3
|
* then hydrates and runs it within the same harness session.
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
5
|
+
import type { ContextData, ContextLayer } from '@noetic-tools/context';
|
|
6
6
|
import type { AgentHarnessContract, Context, Step, Tool } from '@noetic-tools/types';
|
|
7
|
+
import type { WorkflowDocument } from '../schemas/workflow.js';
|
|
7
8
|
/** @public Options for creating a dynamic workflow step. */
|
|
8
9
|
export interface DynamicWorkflowOpts {
|
|
9
10
|
model?: string;
|
|
@@ -25,7 +26,7 @@ export interface DynamicWorkflowOpts {
|
|
|
25
26
|
* @param opts.maxRevisions - Retries with error feedback on validation failure. Default: 3.
|
|
26
27
|
* @returns A `Step` that dynamically plans and executes.
|
|
27
28
|
*/
|
|
28
|
-
export declare function dynamicWorkflow(opts: DynamicWorkflowOpts): Step<
|
|
29
|
+
export declare function dynamicWorkflow(opts: DynamicWorkflowOpts): Step<ContextData, string, string>;
|
|
29
30
|
/** @public Options for `parseAndRunWorkflow`. */
|
|
30
31
|
export interface ParseAndRunWorkflowOpts {
|
|
31
32
|
json: unknown;
|
|
@@ -35,11 +36,13 @@ export interface ParseAndRunWorkflowOpts {
|
|
|
35
36
|
input?: string;
|
|
36
37
|
maxDepth?: number;
|
|
37
38
|
/**
|
|
38
|
-
*
|
|
39
|
+
* Context layers the document's `provide` / `spawn` nodes may reference by name.
|
|
39
40
|
* Without a registry those nodes resolve to NO layers rather than failing, so a
|
|
40
41
|
* host that runs layer-bearing workflows must pass its layers here.
|
|
41
42
|
*/
|
|
42
|
-
layers?: ReadonlyMap<string,
|
|
43
|
+
layers?: ReadonlyMap<string, ContextLayer>;
|
|
44
|
+
/** Named sub-workflows the document's `subflow` nodes may reference via `ref`. */
|
|
45
|
+
workflows?: ReadonlyMap<string, WorkflowDocument>;
|
|
43
46
|
}
|
|
44
47
|
/**
|
|
45
48
|
* Parses raw JSON into a `WorkflowDocument`, hydrates it into a live step
|
|
@@ -28,6 +28,7 @@ A WorkflowNode is one of:
|
|
|
28
28
|
- { "kind": "loop", "id": "<unique>", "body": <WorkflowNode>, "until": { "kind": "maxSteps", "n": <number> } }
|
|
29
29
|
- { "kind": "branch", "id": "<unique>", "routes": [{ "match": "<substring>", "target": <WorkflowNode> }], "default": <WorkflowNode> }
|
|
30
30
|
- { "kind": "spawn", "id": "<unique>", "child": <WorkflowNode> }
|
|
31
|
+
- { "kind": "subflow", "id": "<unique>", "document": { "version": 1, "root": <WorkflowNode> } } (an inline sub-workflow run as one step; only emit the inline form — named refs require a registry this planner does not provide)
|
|
31
32
|
- { "kind": "claude-code"|"codex"|"opencode"|"pi", "id": "<unique>", "prompt": "<turn prompt>", "settings": { "model": "<optional>", "permissionMode": "<optional>" } }
|
|
32
33
|
|
|
33
34
|
SubHarness nodes (claude-code, codex, opencode, pi) delegate a turn to an external coding agent; only emit one if a matching harness adapter is registered for the workflow.
|
|
@@ -143,6 +144,7 @@ export async function parseAndRunWorkflow(opts) {
|
|
|
143
144
|
tools: buildToolMap(opts.tools),
|
|
144
145
|
executeStep,
|
|
145
146
|
layers: opts.layers,
|
|
147
|
+
workflows: opts.workflows,
|
|
146
148
|
};
|
|
147
149
|
const hydrated = hydrateWorkflow(parseResult.doc, hydrationCtx);
|
|
148
150
|
const runSpan = beginWorkflowRunSpan(opts.harness, opts.ctx, parseResult.doc);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContextData } from '@noetic-tools/context';
|
|
2
2
|
import type { Step } from '@noetic-tools/types';
|
|
3
3
|
import type { ZodType } from 'zod';
|
|
4
4
|
/** @public Answer captured by the host for a single interview question. */
|
|
@@ -50,4 +50,4 @@ export interface InterviewOpts<Q, C> {
|
|
|
50
50
|
* @returns A `Step` whose output is `InterviewResult<Q, C>` — either the completion envelope or
|
|
51
51
|
* `maxQuestions` when the budget is exhausted before the model emits `complete`.
|
|
52
52
|
*/
|
|
53
|
-
export declare function interview<Q, C>(opts: InterviewOpts<Q, C>): Step<
|
|
53
|
+
export declare function interview<Q, C>(opts: InterviewOpts<Q, C>): Step<ContextData, string, InterviewResult<Q, C>>;
|
package/dist/patterns/plans.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContextData } from '@noetic-tools/context';
|
|
2
2
|
import type { Context, ExecuteStepFn, Step } from '@noetic-tools/types';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
export interface PlanNode {
|
|
@@ -26,7 +26,7 @@ export interface PlanConstraints {
|
|
|
26
26
|
* @param executeStep - Optional step executor for non-run step kinds.
|
|
27
27
|
* @returns A compiled `Step` ready for execution.
|
|
28
28
|
*/
|
|
29
|
-
export declare function compilePlan<O, A = unknown>(plan: PlanNode, agents: Record<string, (prompt: string) => Step<
|
|
29
|
+
export declare function compilePlan<O, A = unknown>(plan: PlanNode, agents: Record<string, (prompt: string) => Step<ContextData, string, A>>, constraints?: PlanConstraints, executeStep?: ExecuteStepFn): Step<ContextData, string, O>;
|
|
30
30
|
/**
|
|
31
31
|
* Creates a step that dynamically generates a plan via a planner step and executes it,
|
|
32
32
|
* retrying with error feedback up to `maxRevisions` times on failure.
|
|
@@ -36,9 +36,9 @@ export declare function compilePlan<O, A = unknown>(plan: PlanNode, agents: Reco
|
|
|
36
36
|
* @returns A `Step` that adaptively plans and executes.
|
|
37
37
|
*/
|
|
38
38
|
export declare function adaptivePlan<O, A = unknown>(opts: {
|
|
39
|
-
planner: Step<
|
|
40
|
-
agents: Record<string, (prompt: string) => Step<
|
|
39
|
+
planner: Step<ContextData, string, PlanNode>;
|
|
40
|
+
agents: Record<string, (prompt: string) => Step<ContextData, string, A>>;
|
|
41
41
|
constraints?: PlanConstraints;
|
|
42
42
|
maxRevisions: number;
|
|
43
43
|
executeStep?: ExecuteStepFn;
|
|
44
|
-
}): Step<
|
|
44
|
+
}): Step<ContextData, string, O>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContextData } from '@noetic-tools/context';
|
|
2
2
|
import type { StepLoop, Tool } from '@noetic-tools/types';
|
|
3
3
|
import type { VerifyFn } from '../until/predicates.js';
|
|
4
4
|
/**
|
|
@@ -16,4 +16,4 @@ export declare function ralphWiggum(opts: {
|
|
|
16
16
|
verify: VerifyFn;
|
|
17
17
|
maxIterations?: number;
|
|
18
18
|
innerMaxSteps?: number;
|
|
19
|
-
}): StepLoop<
|
|
19
|
+
}): StepLoop<ContextData, string, string>;
|
package/dist/patterns/react.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContextConfig, ContextData, ContextLayer } from '@noetic-tools/context';
|
|
2
2
|
import type { StepLoop, StepSpawn, Tool } from '@noetic-tools/types';
|
|
3
3
|
/**
|
|
4
4
|
* Creates a ReAct (Reason + Act) agent loop: an LLM step with tools iterated until no tool calls or limits are hit.
|
|
5
5
|
*
|
|
6
6
|
* @public
|
|
7
|
-
* @param opts - Model, tools, optional instructions, step/cost limits, and
|
|
8
|
-
* @returns A `StepLoop` (no
|
|
7
|
+
* @param opts - Model, tools, optional instructions, step/cost limits, and context layers.
|
|
8
|
+
* @returns A `StepLoop` (no context layers) or `StepSpawn` wrapping a loop (with them).
|
|
9
9
|
*/
|
|
10
10
|
export declare function react(opts: {
|
|
11
11
|
model: string;
|
|
@@ -13,5 +13,7 @@ export declare function react(opts: {
|
|
|
13
13
|
tools: Tool[];
|
|
14
14
|
maxSteps?: number;
|
|
15
15
|
maxCost?: number;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
context?: ContextConfig | ContextLayer[];
|
|
17
|
+
/** @deprecated Renamed to `context`. */
|
|
18
|
+
memory?: ContextConfig | ContextLayer[];
|
|
19
|
+
}): StepLoop<ContextData, string, string> | StepSpawn<ContextData, string, string>;
|
package/dist/patterns/react.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveContextOption } from '../builders/context-option.js';
|
|
1
2
|
import { loop } from '../builders/loop-builder.js';
|
|
2
3
|
import { spawn } from '../builders/spawn-builder.js';
|
|
3
4
|
import { step } from '../builders/step-builders.js';
|
|
@@ -7,8 +8,8 @@ import { until } from '../until/predicates.js';
|
|
|
7
8
|
* Creates a ReAct (Reason + Act) agent loop: an LLM step with tools iterated until no tool calls or limits are hit.
|
|
8
9
|
*
|
|
9
10
|
* @public
|
|
10
|
-
* @param opts - Model, tools, optional instructions, step/cost limits, and
|
|
11
|
-
* @returns A `StepLoop` (no
|
|
11
|
+
* @param opts - Model, tools, optional instructions, step/cost limits, and context layers.
|
|
12
|
+
* @returns A `StepLoop` (no context layers) or `StepSpawn` wrapping a loop (with them).
|
|
12
13
|
*/
|
|
13
14
|
export function react(opts) {
|
|
14
15
|
const llmStep = step.llm({
|
|
@@ -28,12 +29,13 @@ export function react(opts) {
|
|
|
28
29
|
]
|
|
29
30
|
: [])),
|
|
30
31
|
});
|
|
31
|
-
|
|
32
|
+
const layers = resolveContextOption(opts);
|
|
33
|
+
if (!layers) {
|
|
32
34
|
return loopStep;
|
|
33
35
|
}
|
|
34
36
|
return spawn({
|
|
35
37
|
id: 'react-agent',
|
|
36
38
|
child: loopStep,
|
|
37
|
-
|
|
39
|
+
context: layers,
|
|
38
40
|
});
|
|
39
41
|
}
|
|
@@ -6,6 +6,8 @@ export { layerData, layerFn } from '../builders/layer-provides-builders.js';
|
|
|
6
6
|
export { loop } from '../builders/loop-builder.js';
|
|
7
7
|
/** @public */
|
|
8
8
|
export { spawn } from '../builders/spawn-builder.js';
|
|
9
|
+
// The base namespace, without `step.workflow` — the portable surface stays
|
|
10
|
+
// free of the hydrator so restricted runtimes don't pull it in.
|
|
9
11
|
/** @public */
|
|
10
12
|
export { step } from '../builders/step-builders.js';
|
|
11
13
|
/** @public */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** @public */
|
|
2
|
+
/** @public */
|
|
3
|
+
/** @public */
|
|
4
|
+
/** @public */
|
|
5
|
+
/** @public */
|
|
6
|
+
/** @public */
|
|
7
|
+
/** @public */
|
|
8
|
+
export { durableTaskState, historyWindow, observationalContext, planContext, temporalContext, toolContextLayer, workingMemoryContext, } from '@noetic-tools/context';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** @public */
|
|
2
|
+
/** @public */
|
|
3
|
+
/** @public */
|
|
4
|
+
/** @public */
|
|
5
|
+
/** @public */
|
|
6
|
+
/** @public */
|
|
7
|
+
/** @public */
|
|
8
|
+
export { durableTaskState, historyWindow, observationalContext, planContext, temporalContext, toolContextLayer, workingMemoryContext, } from '@noetic-tools/context';
|
package/dist/portable.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** @public */
|
|
2
|
-
export { Slot } from '@noetic-tools/
|
|
2
|
+
export { Slot } from '@noetic-tools/context';
|
|
3
3
|
export * from './portable/adapters.js';
|
|
4
4
|
export * from './portable/ask-user.js';
|
|
5
5
|
export * from './portable/builders.js';
|
|
6
|
-
export * from './portable/
|
|
6
|
+
export * from './portable/context-layers.js';
|
|
7
7
|
export * from './portable/runtime.js';
|
|
8
8
|
export * from './portable/until.js';
|
package/dist/portable.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** @public */
|
|
2
|
-
export { Slot } from '@noetic-tools/
|
|
2
|
+
export { Slot } from '@noetic-tools/context';
|
|
3
3
|
export * from './portable/adapters.js';
|
|
4
4
|
export * from './portable/ask-user.js';
|
|
5
5
|
export * from './portable/builders.js';
|
|
6
|
-
export * from './portable/
|
|
6
|
+
export * from './portable/context-layers.js';
|
|
7
7
|
export * from './portable/runtime.js';
|
|
8
8
|
export * from './portable/until.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { buildContextData } from '@noetic-tools/context';
|
|
2
2
|
export type { ItemSchemaRegistry } from '@noetic-tools/types';
|
|
3
3
|
export { defaultItemSchemaRegistry } from '@noetic-tools/types';
|
|
4
|
-
export type { AgentHarnessContract, Channel, ChannelStore, Context,
|
|
4
|
+
export type { AgentHarnessContract, Channel, ChannelStore, Context, ContextData, ContextLayer, CwdState, EventBroadcaster, FrontierFrame, FsAdapter, Item, ItemLog, LastLayerUsage, Span, StepMeta, TokenUsage, Tool, } from './context-type-deps.js';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { buildContextData } from '@noetic-tools/context';
|
|
2
2
|
export { defaultItemSchemaRegistry } from '@noetic-tools/types';
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import type { AgentHarnessContract, Channel, ChannelStore, Context,
|
|
1
|
+
import type { AgentHarnessContract, Channel, ChannelStore, Context, ContextData, ContextLayer, CwdState, EventBroadcaster, FrontierFrame, FsAdapter, Item, ItemLog, ItemSchemaRegistry, LastLayerUsage, Span, StepMeta, TokenUsage, Tool } from './context-deps.js';
|
|
2
2
|
import type { StepLedger } from './durable/step-ledger.js';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
* The cancellation tree rooted at `ctx`, in pre-order (a parent before its
|
|
6
|
+
* children). Reverse it to walk deepest-first. Only framework contexts track
|
|
7
|
+
* children, so a foreign `Context` implementation yields just itself.
|
|
8
|
+
*/
|
|
9
|
+
export declare function collectContextTree(ctx: Context): Context[];
|
|
10
|
+
export declare class ContextImpl implements Context<ContextData> {
|
|
4
11
|
readonly id: string;
|
|
5
12
|
stepCount: number;
|
|
6
13
|
tokens: TokenUsage;
|
|
7
14
|
cost: number;
|
|
8
15
|
state: unknown;
|
|
9
|
-
readonly parent: Context<
|
|
16
|
+
readonly parent: Context<ContextData> | null;
|
|
10
17
|
readonly depth: number;
|
|
11
18
|
readonly span: Span;
|
|
12
19
|
readonly threadId: string;
|
|
@@ -15,7 +22,7 @@ export declare class ContextImpl implements Context<ContextMemory> {
|
|
|
15
22
|
lastStepMeta: StepMeta | null;
|
|
16
23
|
lastLayerUsage: LastLayerUsage | undefined;
|
|
17
24
|
readonly harness: AgentHarnessContract;
|
|
18
|
-
readonly layers?:
|
|
25
|
+
readonly layers?: ContextLayer[];
|
|
19
26
|
unifiedTools?: ReadonlyArray<Tool>;
|
|
20
27
|
readonly itemSchemas?: ItemSchemaRegistry;
|
|
21
28
|
readonly cwdState: CwdState;
|
|
@@ -41,10 +48,19 @@ export declare class ContextImpl implements Context<ContextMemory> {
|
|
|
41
48
|
* reject promptly with `{ kind: 'cancelled' }` instead of hanging until
|
|
42
49
|
* their timeout (spec 09, Cancellation item 2). Each fork/spawn child
|
|
43
50
|
* constructs its own ContextImpl and therefore its own controller —
|
|
44
|
-
* aborting a child never rejects the parent's waiters
|
|
51
|
+
* aborting a child never rejects the parent's waiters, while aborting a
|
|
52
|
+
* parent DOES cascade down to every live child (see `_children`).
|
|
45
53
|
*/
|
|
46
54
|
private readonly _abortController;
|
|
47
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Live child contexts (fork paths, spawn children) registered for the abort
|
|
57
|
+
* cascade. A child adds itself at construction and removes itself via
|
|
58
|
+
* `detachFromParent()` when its execution settles, so a long-lived parent
|
|
59
|
+
* driving a loop of spawns does not accumulate finished children.
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
private readonly _children;
|
|
63
|
+
private _contextData?;
|
|
48
64
|
/**
|
|
49
65
|
* Stack of steps currently in flight on this context, most-recent last.
|
|
50
66
|
* Pushed by `enterStep` at the top of `execute()` and popped by
|
|
@@ -93,7 +109,7 @@ export declare class ContextImpl implements Context<ContextMemory> {
|
|
|
93
109
|
pathPrefix?: string;
|
|
94
110
|
/** The execution's completion ledger, shared with children by reference. */
|
|
95
111
|
ledger?: StepLedger;
|
|
96
|
-
layers?:
|
|
112
|
+
layers?: ContextLayer[];
|
|
97
113
|
unifiedTools?: ReadonlyArray<Tool>;
|
|
98
114
|
itemSchemas?: ItemSchemaRegistry;
|
|
99
115
|
cwdState?: CwdState;
|
|
@@ -102,17 +118,40 @@ export declare class ContextImpl implements Context<ContextMemory> {
|
|
|
102
118
|
* Pre-chosen context id. When set, the ContextImpl adopts this id instead
|
|
103
119
|
* of generating a fresh UUID. Used by `executeSpawn` so the child's
|
|
104
120
|
* `ctx.id` matches the `executionId` keyed into the layer-state store —
|
|
105
|
-
* otherwise writes via `ctx.
|
|
121
|
+
* otherwise writes via `ctx.context[layerId].state` land on one id while
|
|
106
122
|
* spawn's `onReturn` reads from another, silently losing the update.
|
|
107
123
|
*/
|
|
108
124
|
id?: string;
|
|
109
125
|
});
|
|
126
|
+
/**
|
|
127
|
+
* @internal
|
|
128
|
+
* Register `child` for the abort cascade. A child constructed under an
|
|
129
|
+
* already-aborted parent is aborted immediately — otherwise a spawn issued
|
|
130
|
+
* in the window between `abort()` and the interpreter noticing would run a
|
|
131
|
+
* whole sub-agent that nothing can stop.
|
|
132
|
+
*/
|
|
133
|
+
private adoptChild;
|
|
134
|
+
/**
|
|
135
|
+
* @internal
|
|
136
|
+
* Leave the parent's abort cascade. Called by the interpreter when a spawn
|
|
137
|
+
* child or fork path settles — a finished child has nothing left to cancel,
|
|
138
|
+
* and holding it would leak for the remaining life of the parent.
|
|
139
|
+
*/
|
|
140
|
+
detachFromParent(): void;
|
|
141
|
+
/**
|
|
142
|
+
* @internal
|
|
143
|
+
* Live child contexts, in registration order. Consumed by
|
|
144
|
+
* `AgentHarness.cancel` to walk the execution tree depth-first.
|
|
145
|
+
*/
|
|
146
|
+
get children(): ReadonlyArray<ContextImpl>;
|
|
110
147
|
get elapsed(): number;
|
|
111
148
|
get aborted(): boolean;
|
|
112
149
|
get fs(): FsAdapter;
|
|
113
150
|
get shell(): AgentHarnessContract['shell'];
|
|
114
151
|
get subprocess(): AgentHarnessContract['subprocess'];
|
|
115
|
-
get
|
|
152
|
+
get context(): ContextData;
|
|
153
|
+
/** @deprecated Renamed to `context`. Returns the same object. */
|
|
154
|
+
get memory(): ContextData;
|
|
116
155
|
recv<T>(ch: Channel<T>, opts?: {
|
|
117
156
|
timeout?: number;
|
|
118
157
|
}): Promise<T>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildContextData, defaultItemSchemaRegistry } from './context-deps.js';
|
|
2
2
|
import { ItemLogImpl } from './item-log-impl.js';
|
|
3
|
-
const
|
|
3
|
+
const EMPTY_CONTEXT = Object.freeze({});
|
|
4
4
|
class NoopSpan {
|
|
5
5
|
traceId = crypto.randomUUID();
|
|
6
6
|
spanId = crypto.randomUUID();
|
|
@@ -9,6 +9,24 @@ class NoopSpan {
|
|
|
9
9
|
addEvent(_name, _attributes) { }
|
|
10
10
|
end() { }
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
* The cancellation tree rooted at `ctx`, in pre-order (a parent before its
|
|
15
|
+
* children). Reverse it to walk deepest-first. Only framework contexts track
|
|
16
|
+
* children, so a foreign `Context` implementation yields just itself.
|
|
17
|
+
*/
|
|
18
|
+
export function collectContextTree(ctx) {
|
|
19
|
+
const tree = [
|
|
20
|
+
ctx,
|
|
21
|
+
];
|
|
22
|
+
if (!(ctx instanceof ContextImpl)) {
|
|
23
|
+
return tree;
|
|
24
|
+
}
|
|
25
|
+
for (const child of ctx.children) {
|
|
26
|
+
tree.push(...collectContextTree(child));
|
|
27
|
+
}
|
|
28
|
+
return tree;
|
|
29
|
+
}
|
|
12
30
|
export class ContextImpl {
|
|
13
31
|
id;
|
|
14
32
|
stepCount = 0;
|
|
@@ -54,10 +72,19 @@ export class ContextImpl {
|
|
|
54
72
|
* reject promptly with `{ kind: 'cancelled' }` instead of hanging until
|
|
55
73
|
* their timeout (spec 09, Cancellation item 2). Each fork/spawn child
|
|
56
74
|
* constructs its own ContextImpl and therefore its own controller —
|
|
57
|
-
* aborting a child never rejects the parent's waiters
|
|
75
|
+
* aborting a child never rejects the parent's waiters, while aborting a
|
|
76
|
+
* parent DOES cascade down to every live child (see `_children`).
|
|
58
77
|
*/
|
|
59
78
|
_abortController = new AbortController();
|
|
60
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Live child contexts (fork paths, spawn children) registered for the abort
|
|
81
|
+
* cascade. A child adds itself at construction and removes itself via
|
|
82
|
+
* `detachFromParent()` when its execution settles, so a long-lived parent
|
|
83
|
+
* driving a loop of spawns does not accumulate finished children.
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
_children = new Set();
|
|
87
|
+
_contextData;
|
|
61
88
|
/**
|
|
62
89
|
* Stack of steps currently in flight on this context, most-recent last.
|
|
63
90
|
* Pushed by `enterStep` at the top of `execute()` and popped by
|
|
@@ -120,6 +147,46 @@ export class ContextImpl {
|
|
|
120
147
|
}
|
|
121
148
|
}
|
|
122
149
|
this.itemLog = log;
|
|
150
|
+
// Join the parent's abort cascade last, so the child is fully constructed
|
|
151
|
+
// before an already-aborted parent aborts it.
|
|
152
|
+
if (this.parent instanceof ContextImpl) {
|
|
153
|
+
this.parent.adoptChild(this);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* @internal
|
|
158
|
+
* Register `child` for the abort cascade. A child constructed under an
|
|
159
|
+
* already-aborted parent is aborted immediately — otherwise a spawn issued
|
|
160
|
+
* in the window between `abort()` and the interpreter noticing would run a
|
|
161
|
+
* whole sub-agent that nothing can stop.
|
|
162
|
+
*/
|
|
163
|
+
adoptChild(child) {
|
|
164
|
+
if (this._aborted) {
|
|
165
|
+
child.abort(this._abortReason ?? 'parent context aborted');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
this._children.add(child);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* @internal
|
|
172
|
+
* Leave the parent's abort cascade. Called by the interpreter when a spawn
|
|
173
|
+
* child or fork path settles — a finished child has nothing left to cancel,
|
|
174
|
+
* and holding it would leak for the remaining life of the parent.
|
|
175
|
+
*/
|
|
176
|
+
detachFromParent() {
|
|
177
|
+
if (this.parent instanceof ContextImpl) {
|
|
178
|
+
this.parent._children.delete(this);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* @internal
|
|
183
|
+
* Live child contexts, in registration order. Consumed by
|
|
184
|
+
* `AgentHarness.cancel` to walk the execution tree depth-first.
|
|
185
|
+
*/
|
|
186
|
+
get children() {
|
|
187
|
+
return [
|
|
188
|
+
...this._children,
|
|
189
|
+
];
|
|
123
190
|
}
|
|
124
191
|
get elapsed() {
|
|
125
192
|
return Date.now() - this._createdAt;
|
|
@@ -136,11 +203,15 @@ export class ContextImpl {
|
|
|
136
203
|
get subprocess() {
|
|
137
204
|
return this.harness.subprocess;
|
|
138
205
|
}
|
|
139
|
-
get
|
|
140
|
-
if (!this.
|
|
141
|
-
this.
|
|
206
|
+
get context() {
|
|
207
|
+
if (!this._contextData) {
|
|
208
|
+
this._contextData = this.layers ? buildContextData(this.layers, this) : EMPTY_CONTEXT;
|
|
142
209
|
}
|
|
143
|
-
return this.
|
|
210
|
+
return this._contextData;
|
|
211
|
+
}
|
|
212
|
+
/** @deprecated Renamed to `context`. Returns the same object. */
|
|
213
|
+
get memory() {
|
|
214
|
+
return this.context;
|
|
144
215
|
}
|
|
145
216
|
recv(ch, opts) {
|
|
146
217
|
if (!this.channelStore) {
|
|
@@ -184,12 +255,26 @@ export class ContextImpl {
|
|
|
184
255
|
return this._abortReason;
|
|
185
256
|
}
|
|
186
257
|
abort(reason) {
|
|
258
|
+
if (this._aborted) {
|
|
259
|
+
// Idempotent (spec 09): the first abort owns the reason, and the signal
|
|
260
|
+
// has already fired — re-firing it would be a no-op anyway.
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
187
263
|
this._aborted = true;
|
|
188
264
|
this._abortReason = reason;
|
|
189
265
|
// Reject everything blocked on this context (channel recv waiters,
|
|
190
|
-
// parked back-pressure senders) with { kind: 'cancelled' }.
|
|
191
|
-
// a second abort() leaves the already-aborted signal untouched.
|
|
266
|
+
// parked back-pressure senders) with { kind: 'cancelled' }.
|
|
192
267
|
this._abortController.abort(reason ?? 'context aborted');
|
|
268
|
+
// Cascade to live children (fork paths, spawn children). The registry is
|
|
269
|
+
// cleared first so a child's own `detachFromParent()` during its unwind
|
|
270
|
+
// cannot mutate the set we are iterating.
|
|
271
|
+
const children = [
|
|
272
|
+
...this._children,
|
|
273
|
+
];
|
|
274
|
+
this._children.clear();
|
|
275
|
+
for (const child of children) {
|
|
276
|
+
child.abort(reason ?? 'parent context aborted');
|
|
277
|
+
}
|
|
193
278
|
}
|
|
194
279
|
/**
|
|
195
280
|
* @internal
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type { ContextData, ContextLayer } from '@noetic-tools/context';
|
|
2
2
|
export type { AgentHarnessContract, Channel, Context, CwdState, FsAdapter, Item, ItemLog, LastLayerUsage, Span, StepMeta, TokenUsage, Tool, } from '@noetic-tools/types';
|
|
3
3
|
export type { FrontierFrame } from '../types/checkpoint.js';
|
|
4
4
|
export type { ChannelStore } from './channel-store.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { LayerStateStore
|
|
1
|
+
import type { LayerStateStore } from '@noetic-tools/context';
|
|
2
2
|
import type { Context, Item, ItemSchemaRegistry, RestoreContextOptions } from '@noetic-tools/types';
|
|
3
3
|
import type { EventBroadcaster } from '../event-broadcaster.js';
|
|
4
4
|
import type { CheckpointStore } from './checkpoint-store.js';
|
|
5
|
-
import type { StepLedgerStore } from './step-ledger.js';
|
|
5
|
+
import type { StepLedgerRetention, StepLedgerStore } from './step-ledger.js';
|
|
6
6
|
/**
|
|
7
7
|
* Host wiring forwarded into the context `restoreFromCheckpoint` builds.
|
|
8
8
|
* `RestoreContextOptions` covers the portable fields; core widens it with the
|
|
@@ -26,9 +26,9 @@ export type RestoreCheckpointOptions = RestoreContextOptions & {
|
|
|
26
26
|
export interface CheckpointHarnessHandle {
|
|
27
27
|
readonly checkpointStore?: CheckpointStore;
|
|
28
28
|
readonly stepLedgerStore?: StepLedgerStore;
|
|
29
|
+
readonly stepLedgerRetention?: StepLedgerRetention;
|
|
29
30
|
readonly layerStateStore: LayerStateStore;
|
|
30
31
|
readonly itemSchemas: ItemSchemaRegistry;
|
|
31
|
-
readonly _memory?: MemoryLayer[];
|
|
32
32
|
createContext(opts?: RestoreCheckpointOptions & {
|
|
33
33
|
items?: Item[];
|
|
34
34
|
threadId?: string;
|
|
@@ -51,7 +51,7 @@ export declare function captureCheckpoint(h: CheckpointHarnessHandle, ctx: Conte
|
|
|
51
51
|
* `null` if no snapshot is recorded for `executionId`. Layer state is
|
|
52
52
|
* replayed into `layerStateStore` keyed by the original executionId so
|
|
53
53
|
* the restored context observes the pre-crash state through
|
|
54
|
-
* `readLayerState` and the
|
|
54
|
+
* `readLayerState` and the context projectors.
|
|
55
55
|
*
|
|
56
56
|
* `opts` carries the host's own context wiring (broadcaster, parent, state,
|
|
57
57
|
* layer overrides) into the rebuilt context. A snapshot cannot round-trip live
|
|
@@ -68,3 +68,16 @@ export declare function captureCheckpoint(h: CheckpointHarnessHandle, ctx: Conte
|
|
|
68
68
|
* @internal
|
|
69
69
|
*/
|
|
70
70
|
export declare function restoreFromCheckpoint(h: CheckpointHarnessHandle, executionId: string, opts?: RestoreCheckpointOptions): Promise<Context | null>;
|
|
71
|
+
/**
|
|
72
|
+
* Discard every recovery record for one execution: the snapshot and the completion
|
|
73
|
+
* ledger. Clearing the snapshot alone would strand the ledger's shards under
|
|
74
|
+
* `execution:<id>:ledger:*` forever, since nothing else enumerates them.
|
|
75
|
+
*
|
|
76
|
+
* This is the operation a host performs when resume is no longer valid — most often
|
|
77
|
+
* because the workflow changed. Replay happens at the coarsest completed granularity,
|
|
78
|
+
* so a step edited *beneath* a recorded parent is invisible to divergence detection and
|
|
79
|
+
* the old output would be replayed over the new tree. Clear, then start fresh.
|
|
80
|
+
*
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
export declare function clearCheckpoint(h: CheckpointHarnessHandle, executionId: string): Promise<void>;
|
|
@@ -63,7 +63,7 @@ export async function captureCheckpoint(h, ctx) {
|
|
|
63
63
|
* `null` if no snapshot is recorded for `executionId`. Layer state is
|
|
64
64
|
* replayed into `layerStateStore` keyed by the original executionId so
|
|
65
65
|
* the restored context observes the pre-crash state through
|
|
66
|
-
* `readLayerState` and the
|
|
66
|
+
* `readLayerState` and the context projectors.
|
|
67
67
|
*
|
|
68
68
|
* `opts` carries the host's own context wiring (broadcaster, parent, state,
|
|
69
69
|
* layer overrides) into the rebuilt context. A snapshot cannot round-trip live
|
|
@@ -93,7 +93,7 @@ export async function restoreFromCheckpoint(h, executionId, opts) {
|
|
|
93
93
|
}
|
|
94
94
|
const items = h.itemSchemas.parseMany(snapshot.itemLog.items);
|
|
95
95
|
const cwdInit = snapshot.cwd?.current ?? undefined;
|
|
96
|
-
/* Caller wiring first, snapshot second: a host may legitimately swap the
|
|
96
|
+
/* Caller wiring first, snapshot second: a host may legitimately swap the context
|
|
97
97
|
* layers or hang the restored execution under a new parent, but it must never be
|
|
98
98
|
* able to override the identity/history the snapshot is the record of. */
|
|
99
99
|
const ctx = h.createContext({
|
|
@@ -102,7 +102,6 @@ export async function restoreFromCheckpoint(h, executionId, opts) {
|
|
|
102
102
|
threadId: snapshot.threadId,
|
|
103
103
|
resourceId: snapshot.resourceId,
|
|
104
104
|
cwdInit,
|
|
105
|
-
memory: opts?.memory ?? h._memory,
|
|
106
105
|
});
|
|
107
106
|
if (ctx instanceof ContextImpl) {
|
|
108
107
|
Object.defineProperty(ctx, 'id', {
|
|
@@ -123,6 +122,7 @@ export async function restoreFromCheckpoint(h, executionId, opts) {
|
|
|
123
122
|
executionId,
|
|
124
123
|
store: h.stepLedgerStore,
|
|
125
124
|
recovered,
|
|
125
|
+
retention: h.stepLedgerRetention,
|
|
126
126
|
}),
|
|
127
127
|
configurable: false,
|
|
128
128
|
writable: false,
|
|
@@ -133,3 +133,21 @@ export async function restoreFromCheckpoint(h, executionId, opts) {
|
|
|
133
133
|
return ctx;
|
|
134
134
|
}
|
|
135
135
|
//#endregion
|
|
136
|
+
//#region clearCheckpoint
|
|
137
|
+
/**
|
|
138
|
+
* Discard every recovery record for one execution: the snapshot and the completion
|
|
139
|
+
* ledger. Clearing the snapshot alone would strand the ledger's shards under
|
|
140
|
+
* `execution:<id>:ledger:*` forever, since nothing else enumerates them.
|
|
141
|
+
*
|
|
142
|
+
* This is the operation a host performs when resume is no longer valid — most often
|
|
143
|
+
* because the workflow changed. Replay happens at the coarsest completed granularity,
|
|
144
|
+
* so a step edited *beneath* a recorded parent is invisible to divergence detection and
|
|
145
|
+
* the old output would be replayed over the new tree. Clear, then start fresh.
|
|
146
|
+
*
|
|
147
|
+
* @internal
|
|
148
|
+
*/
|
|
149
|
+
export async function clearCheckpoint(h, executionId) {
|
|
150
|
+
await h.checkpointStore?.clear(executionId);
|
|
151
|
+
await h.stepLedgerStore?.clear(executionId);
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|