@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.
Files changed (101) hide show
  1. package/dist/adapters/in-memory-subprocess/manifest-persistence.d.ts +1 -1
  2. package/dist/adapters/in-memory-subprocess/types.d.ts +1 -1
  3. package/dist/adapters/openrouter.d.ts +2 -2
  4. package/dist/adapters/openrouter.js +2 -2
  5. package/dist/builders/context-builder.d.ts +11 -0
  6. package/dist/builders/context-builder.js +15 -0
  7. package/dist/builders/context-option.d.ts +23 -0
  8. package/dist/builders/context-option.js +22 -0
  9. package/dist/builders/control-flow-builders.d.ts +19 -19
  10. package/dist/builders/every.d.ts +4 -4
  11. package/dist/builders/layer-provides-builders.d.ts +1 -1
  12. package/dist/builders/layer-provides-builders.js +1 -1
  13. package/dist/builders/loop-builder.d.ts +3 -3
  14. package/dist/builders/provide-builder.d.ts +14 -7
  15. package/dist/builders/provide-builder.js +15 -4
  16. package/dist/builders/spawn-builder.d.ts +8 -6
  17. package/dist/builders/spawn-builder.js +7 -2
  18. package/dist/builders/step-builders.d.ts +19 -20
  19. package/dist/builders/tool-builder.d.ts +9 -5
  20. package/dist/builders/tool-builder.js +3 -2
  21. package/dist/builders/workflow-hydrator.d.ts +17 -4
  22. package/dist/builders/workflow-hydrator.js +87 -8
  23. package/dist/builders/workflow-step.d.ts +61 -0
  24. package/dist/builders/workflow-step.js +126 -0
  25. package/dist/conditions/conditions.d.ts +17 -17
  26. package/dist/conditions/conditions.js +1 -1
  27. package/dist/deprecated.d.ts +30 -0
  28. package/dist/deprecated.js +28 -0
  29. package/dist/harness/agent-harness.d.ts +84 -27
  30. package/dist/harness/agent-harness.js +101 -20
  31. package/dist/harness/deps/context.d.ts +2 -0
  32. package/dist/harness/deps/context.js +1 -0
  33. package/dist/harness/deps/runtime.d.ts +4 -4
  34. package/dist/harness/deps/runtime.js +3 -3
  35. package/dist/harness/deps/types.d.ts +1 -1
  36. package/dist/harness/model-schema.d.ts +2 -2
  37. package/dist/index.d.ts +23 -19
  38. package/dist/index.js +15 -9
  39. package/dist/interpreter/action-deps.d.ts +3 -3
  40. package/dist/interpreter/action-deps.js +2 -2
  41. package/dist/interpreter/action-types.d.ts +1 -1
  42. package/dist/interpreter/collect-tools.d.ts +1 -1
  43. package/dist/interpreter/execute-action.d.ts +10 -10
  44. package/dist/interpreter/execute-action.js +33 -14
  45. package/dist/interpreter/execute-control.d.ts +4 -4
  46. package/dist/interpreter/execute-control.js +25 -16
  47. package/dist/interpreter/execute-sub-harness.d.ts +1 -1
  48. package/dist/interpreter/execute-sub-harness.js +22 -1
  49. package/dist/interpreter/execute.d.ts +3 -3
  50. package/dist/interpreter/execute.js +78 -31
  51. package/dist/interpreter/step-dispatcher.d.ts +3 -3
  52. package/dist/interpreter/sub-harness-events.d.ts +2 -2
  53. package/dist/interpreter/typeguards.d.ts +4 -4
  54. package/dist/patterns/dynamic-workflow.d.ts +7 -4
  55. package/dist/patterns/dynamic-workflow.js +2 -0
  56. package/dist/patterns/interview.d.ts +2 -2
  57. package/dist/patterns/plans.d.ts +5 -5
  58. package/dist/patterns/ralph-wiggum.d.ts +2 -2
  59. package/dist/patterns/react.d.ts +7 -5
  60. package/dist/patterns/react.js +6 -4
  61. package/dist/portable/builders.js +2 -0
  62. package/dist/portable/context-layers.d.ts +8 -0
  63. package/dist/portable/context-layers.js +8 -0
  64. package/dist/portable.d.ts +2 -2
  65. package/dist/portable.js +2 -2
  66. package/dist/runtime/context-deps.d.ts +2 -2
  67. package/dist/runtime/context-deps.js +1 -1
  68. package/dist/runtime/context-impl.d.ts +48 -9
  69. package/dist/runtime/context-impl.js +95 -10
  70. package/dist/runtime/context-type-deps.d.ts +1 -1
  71. package/dist/runtime/durable/checkpoint-store.d.ts +1 -1
  72. package/dist/runtime/durable/harness-checkpoints.d.ts +17 -4
  73. package/dist/runtime/durable/harness-checkpoints.js +21 -3
  74. package/dist/runtime/durable/index.d.ts +2 -0
  75. package/dist/runtime/durable/index.js +1 -0
  76. package/dist/runtime/durable/step-ledger.d.ts +107 -5
  77. package/dist/runtime/durable/step-ledger.js +179 -10
  78. package/dist/runtime/in-memory-storage.d.ts +1 -1
  79. package/dist/runtime/session-runner.d.ts +1 -1
  80. package/dist/runtime/step-registry.d.ts +3 -3
  81. package/dist/runtime/{tool-memory.d.ts → tool-context.d.ts} +1 -2
  82. package/dist/runtime/{tool-memory.js → tool-context.js} +5 -2
  83. package/dist/runtime/tool-ui.d.ts +1 -1
  84. package/dist/runtime/tool-ui.js +1 -1
  85. package/dist/schemas/workflow.d.ts +5 -279
  86. package/dist/schemas/workflow.js +4 -376
  87. package/dist/types/mutable-context.d.ts +2 -2
  88. package/dist/types/step-registrar.d.ts +1 -1
  89. package/dist/unstable.d.ts +5 -5
  90. package/dist/unstable.js +3 -3
  91. package/dist/until/predicates.d.ts +1 -1
  92. package/package.json +4 -4
  93. package/schema/noetic-workflow.schema.json +28 -0
  94. package/dist/builders/memory-builder.d.ts +0 -11
  95. package/dist/builders/memory-builder.js +0 -15
  96. package/dist/harness/deps/memory.d.ts +0 -2
  97. package/dist/harness/deps/memory.js +0 -1
  98. package/dist/patterns/flow.d.ts +0 -2
  99. package/dist/patterns/flow.js +0 -1
  100. package/dist/portable/memory-layers.d.ts +0 -8
  101. 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, ContextMemory, StepSubHarness, SubHarnessStreamPart, SubHarnessTurnResult } from '@noetic-tools/types';
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<ContextMemory>);
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 { ContextMemory } from '@noetic-tools/memory';
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<ContextMemory>): ctx is MutableContext;
8
- export declare function isContextImpl(ctx: Context<ContextMemory>): ctx is ContextImpl;
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<TMemory>(ctx: Context<TMemory>): ChannelStore | undefined;
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 { ContextMemory, MemoryLayer } from '@noetic-tools/memory';
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<ContextMemory, string, string>;
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
- * Memory layers the document's `provide` / `spawn` nodes may reference by name.
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, MemoryLayer>;
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 { ContextMemory } from '@noetic-tools/memory';
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<ContextMemory, string, InterviewResult<Q, C>>;
53
+ export declare function interview<Q, C>(opts: InterviewOpts<Q, C>): Step<ContextData, string, InterviewResult<Q, C>>;
@@ -1,4 +1,4 @@
1
- import type { ContextMemory } from '@noetic-tools/memory';
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<ContextMemory, string, A>>, constraints?: PlanConstraints, executeStep?: ExecuteStepFn): Step<ContextMemory, string, O>;
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<ContextMemory, string, PlanNode>;
40
- agents: Record<string, (prompt: string) => Step<ContextMemory, string, A>>;
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<ContextMemory, string, O>;
44
+ }): Step<ContextData, string, O>;
@@ -1,4 +1,4 @@
1
- import type { ContextMemory } from '@noetic-tools/memory';
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<ContextMemory, string, string>;
19
+ }): StepLoop<ContextData, string, string>;
@@ -1,11 +1,11 @@
1
- import type { ContextMemory, MemoryConfig, MemoryLayer } from '@noetic-tools/memory';
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 memory layers.
8
- * @returns A `StepLoop` (no memory) or `StepSpawn` wrapping a loop (with memory).
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
- memory?: MemoryConfig | MemoryLayer[];
17
- }): StepLoop<ContextMemory, string, string> | StepSpawn<ContextMemory, string, string>;
16
+ context?: ContextConfig | ContextLayer[];
17
+ /** @deprecated Renamed to `context`. */
18
+ memory?: ContextConfig | ContextLayer[];
19
+ }): StepLoop<ContextData, string, string> | StepSpawn<ContextData, string, string>;
@@ -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 memory layers.
11
- * @returns A `StepLoop` (no memory) or `StepSpawn` wrapping a loop (with memory).
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
- if (!opts.memory) {
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
- memory: opts.memory,
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';
@@ -1,8 +1,8 @@
1
1
  /** @public */
2
- export { Slot } from '@noetic-tools/memory';
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/memory-layers.js';
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/memory';
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/memory-layers.js';
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 { buildContextMemory } from '@noetic-tools/memory';
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, ContextMemory, CwdState, EventBroadcaster, FrontierFrame, FsAdapter, Item, ItemLog, LastLayerUsage, MemoryLayer, Span, StepMeta, TokenUsage, Tool, } from './context-type-deps.js';
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 { buildContextMemory } from '@noetic-tools/memory';
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, ContextMemory, CwdState, EventBroadcaster, FrontierFrame, FsAdapter, Item, ItemLog, ItemSchemaRegistry, LastLayerUsage, MemoryLayer, Span, StepMeta, TokenUsage, Tool } from './context-deps.js';
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
- export declare class ContextImpl implements Context<ContextMemory> {
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<ContextMemory> | null;
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?: MemoryLayer[];
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
- private _memory?;
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?: MemoryLayer[];
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.memory[layerId].state` land on one id while
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 memory(): ContextMemory;
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 { buildContextMemory, defaultItemSchemaRegistry } from './context-deps.js';
1
+ import { buildContextData, defaultItemSchemaRegistry } from './context-deps.js';
2
2
  import { ItemLogImpl } from './item-log-impl.js';
3
- const EMPTY_MEMORY = Object.freeze({});
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
- _memory;
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 memory() {
140
- if (!this._memory) {
141
- this._memory = this.layers ? buildContextMemory(this.layers, this) : EMPTY_MEMORY;
206
+ get context() {
207
+ if (!this._contextData) {
208
+ this._contextData = this.layers ? buildContextData(this.layers, this) : EMPTY_CONTEXT;
142
209
  }
143
- return this._memory;
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' }. Idempotent —
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 { ContextMemory, MemoryLayer } from '@noetic-tools/memory';
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,4 +1,4 @@
1
- import type { StorageAdapter } from '@noetic-tools/memory';
1
+ import type { StorageAdapter } from '@noetic-tools/context';
2
2
  import type { CheckpointSnapshot } from '../../types/checkpoint.js';
3
3
  export declare const CheckpointKeys: {
4
4
  readonly ExecPrefix: "execution:";
@@ -1,8 +1,8 @@
1
- import type { LayerStateStore, MemoryLayer } from '@noetic-tools/memory';
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 memory projectors.
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 memory projectors.
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 memory
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