@noetic-tools/core 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dist/adapters/in-memory-fs-adapter.js +1 -1
  2. package/dist/adapters/in-memory-subprocess/manifest-persistence.d.ts +8 -0
  3. package/dist/adapters/in-memory-subprocess/manifest-persistence.js +56 -0
  4. package/dist/adapters/in-memory-subprocess/metadata.d.ts +12 -0
  5. package/dist/adapters/in-memory-subprocess/metadata.js +58 -0
  6. package/dist/adapters/in-memory-subprocess/step-completion.d.ts +3 -0
  7. package/dist/adapters/in-memory-subprocess/step-completion.js +86 -0
  8. package/dist/adapters/in-memory-subprocess/types.d.ts +88 -0
  9. package/dist/adapters/in-memory-subprocess/types.js +2 -0
  10. package/dist/adapters/in-memory-subprocess-adapter.d.ts +12 -4
  11. package/dist/adapters/in-memory-subprocess-adapter.js +164 -55
  12. package/dist/adapters/openrouter.d.ts +11 -2
  13. package/dist/adapters/openrouter.js +66 -5
  14. package/dist/builders/control-flow-builders.js +8 -3
  15. package/dist/builders/every.js +4 -1
  16. package/dist/builders/layer-provides-builders.d.ts +1 -32
  17. package/dist/builders/layer-provides-builders.js +1 -33
  18. package/dist/builders/loop-builder.js +4 -1
  19. package/dist/builders/memory-builder.js +1 -1
  20. package/dist/builders/provide-builder.js +4 -1
  21. package/dist/builders/spawn-builder.d.ts +20 -7
  22. package/dist/builders/spawn-builder.js +16 -2
  23. package/dist/builders/step-builders.d.ts +49 -28
  24. package/dist/builders/step-builders.js +30 -11
  25. package/dist/builders/tool-builder.d.ts +5 -1
  26. package/dist/builders/tool-builder.js +2 -0
  27. package/dist/builders/workflow-hydrator.d.ts +38 -0
  28. package/dist/builders/workflow-hydrator.js +336 -0
  29. package/dist/conditions/conditions.js +1 -1
  30. package/dist/errors/noetic-error.js +2 -0
  31. package/dist/{runtime → harness}/agent-harness.d.ts +65 -45
  32. package/dist/harness/agent-harness.js +576 -0
  33. package/dist/harness/deps/adapters.d.ts +4 -0
  34. package/dist/harness/deps/adapters.js +4 -0
  35. package/dist/harness/deps/interpreter.d.ts +5 -0
  36. package/dist/harness/deps/interpreter.js +4 -0
  37. package/dist/harness/deps/memory.d.ts +5 -0
  38. package/dist/harness/deps/memory.js +4 -0
  39. package/dist/harness/deps/runtime.d.ts +10 -0
  40. package/dist/harness/deps/runtime.js +7 -0
  41. package/dist/harness/deps/types.d.ts +17 -0
  42. package/dist/harness/deps/types.js +1 -0
  43. package/dist/harness/model-call.d.ts +50 -0
  44. package/dist/harness/model-call.js +694 -0
  45. package/dist/harness/model-schema.d.ts +19 -0
  46. package/dist/harness/model-schema.js +46 -0
  47. package/dist/harness/step-bootstrap.d.ts +25 -0
  48. package/dist/harness/step-bootstrap.js +170 -0
  49. package/dist/index.d.ts +48 -12
  50. package/dist/index.js +29 -7
  51. package/dist/interpreter/action-deps.d.ts +12 -0
  52. package/dist/interpreter/action-deps.js +10 -0
  53. package/dist/interpreter/action-types.d.ts +8 -0
  54. package/dist/interpreter/action-types.js +1 -0
  55. package/dist/interpreter/collect-tools.d.ts +1 -1
  56. package/dist/interpreter/collect-tools.js +6 -2
  57. package/dist/interpreter/execute-action.d.ts +22 -0
  58. package/dist/interpreter/execute-action.js +527 -0
  59. package/dist/interpreter/{execute-every.d.ts → execute-control.d.ts} +7 -1
  60. package/dist/interpreter/execute-control.js +530 -0
  61. package/dist/interpreter/execute.d.ts +13 -0
  62. package/dist/interpreter/execute.js +151 -48
  63. package/dist/interpreter/message-helpers.d.ts +1 -9
  64. package/dist/interpreter/message-helpers.js +1 -42
  65. package/dist/interpreter/step-dispatcher.d.ts +61 -0
  66. package/dist/interpreter/step-dispatcher.js +67 -0
  67. package/dist/interpreter/typeguards.d.ts +3 -10
  68. package/dist/interpreter/typeguards.js +3 -20
  69. package/dist/{runtime → memory}/exec-context-factory.js +1 -1
  70. package/dist/memory/flow-schema.d.ts +52 -0
  71. package/dist/memory/flow-schema.js +102 -0
  72. package/dist/memory/function-call-utils.js +4 -2
  73. package/dist/memory/layer-api.d.ts +1 -1
  74. package/dist/memory/layer-api.js +2 -2
  75. package/dist/memory/layer-lifecycle.d.ts +1 -1
  76. package/dist/memory/layer-lifecycle.js +2 -2
  77. package/dist/memory/layer-provides.d.ts +32 -0
  78. package/dist/memory/layer-provides.js +33 -0
  79. package/dist/memory/layer-usage.d.ts +3 -2
  80. package/dist/memory/layer-usage.js +9 -3
  81. package/dist/memory/layers/durable-task-state.js +1 -1
  82. package/dist/memory/layers/file-reference.js +302 -246
  83. package/dist/memory/layers/history-window.js +1 -1
  84. package/dist/memory/layers/observational-memory.js +1 -2
  85. package/dist/memory/layers/plan.d.ts +1 -1
  86. package/dist/memory/layers/plan.js +13 -8
  87. package/dist/memory/layers/steering.js +1 -1
  88. package/dist/memory/layers/tool-memory-layer.d.ts +1 -1
  89. package/dist/memory/layers/working-memory.js +2 -2
  90. package/dist/patterns/dynamic-workflow.d.ts +55 -0
  91. package/dist/patterns/dynamic-workflow.js +194 -0
  92. package/dist/patterns/flow.d.ts +2 -52
  93. package/dist/patterns/flow.js +1 -103
  94. package/dist/patterns/interview.js +1 -1
  95. package/dist/patterns/plans.d.ts +3 -3
  96. package/dist/patterns/plans.js +3 -3
  97. package/dist/patterns/ralph-wiggum.d.ts +1 -1
  98. package/dist/patterns/react.d.ts +1 -1
  99. package/dist/portable/adapters.d.ts +6 -0
  100. package/dist/portable/adapters.js +6 -0
  101. package/dist/portable/ask-user.d.ts +4 -0
  102. package/dist/portable/ask-user.js +2 -0
  103. package/dist/portable/builders.d.ts +12 -0
  104. package/dist/portable/builders.js +12 -0
  105. package/dist/portable/memory-layers.d.ts +12 -0
  106. package/dist/portable/memory-layers.js +12 -0
  107. package/dist/portable/runtime.d.ts +4 -0
  108. package/dist/portable/runtime.js +4 -0
  109. package/dist/portable/until.d.ts +4 -0
  110. package/dist/portable/until.js +4 -0
  111. package/dist/portable.d.ts +8 -26
  112. package/dist/portable.js +8 -34
  113. package/dist/runtime/broadcaster-utils.d.ts +5 -1
  114. package/dist/runtime/broadcaster-utils.js +11 -6
  115. package/dist/runtime/channel-store.js +1 -1
  116. package/dist/runtime/context-deps.d.ts +4 -0
  117. package/dist/runtime/context-deps.js +2 -0
  118. package/dist/runtime/context-impl.d.ts +42 -12
  119. package/dist/runtime/context-impl.js +51 -4
  120. package/dist/runtime/context-type-deps.d.ts +14 -0
  121. package/dist/runtime/context-type-deps.js +1 -0
  122. package/dist/runtime/detached-handle.d.ts +50 -2
  123. package/dist/runtime/detached-handle.js +216 -12
  124. package/dist/runtime/durable/checkpoint-store.d.ts +38 -0
  125. package/dist/runtime/durable/checkpoint-store.js +101 -0
  126. package/dist/runtime/durable/detached-signal.d.ts +37 -0
  127. package/dist/runtime/durable/detached-signal.js +52 -0
  128. package/dist/runtime/durable/harness-checkpoints.d.ts +51 -0
  129. package/dist/runtime/durable/harness-checkpoints.js +104 -0
  130. package/dist/runtime/durable/index.d.ts +10 -0
  131. package/dist/runtime/durable/index.js +5 -0
  132. package/dist/runtime/durable/runnable-loop.d.ts +100 -0
  133. package/dist/runtime/durable/runnable-loop.js +90 -0
  134. package/dist/runtime/durable/session-seed.d.ts +28 -0
  135. package/dist/runtime/durable/session-seed.js +27 -0
  136. package/dist/runtime/durable/stall-nudge.d.ts +82 -0
  137. package/dist/runtime/durable/stall-nudge.js +87 -0
  138. package/dist/runtime/in-memory-storage.js +1 -1
  139. package/dist/runtime/item-log-impl.d.ts +1 -1
  140. package/dist/runtime/step-registry.d.ts +42 -0
  141. package/dist/runtime/step-registry.js +77 -0
  142. package/dist/schemas/workflow.d.ts +139 -0
  143. package/dist/schemas/workflow.js +232 -0
  144. package/dist/types/ask-user-types.d.ts +57 -0
  145. package/dist/types/ask-user-types.js +74 -0
  146. package/dist/types/checkpoint.d.ts +98 -0
  147. package/dist/types/checkpoint.js +81 -0
  148. package/dist/types/common.d.ts +1 -50
  149. package/dist/types/context-parts/item-log.d.ts +6 -0
  150. package/dist/types/context-parts/item-log.js +1 -0
  151. package/dist/types/context-parts/layer-usage.d.ts +18 -0
  152. package/dist/types/context-parts/layer-usage.js +1 -0
  153. package/dist/types/context.d.ts +135 -26
  154. package/dist/types/error.d.ts +15 -0
  155. package/dist/types/harness-result.d.ts +1 -1
  156. package/dist/types/items.d.ts +18 -2
  157. package/dist/types/memory-context.d.ts +39 -0
  158. package/dist/types/memory-context.js +1 -0
  159. package/dist/types/memory.d.ts +35 -65
  160. package/dist/types/runtime.d.ts +2 -1
  161. package/dist/types/steering.d.ts +1 -1
  162. package/dist/types/step-registrar.d.ts +46 -0
  163. package/dist/types/step-registrar.js +56 -0
  164. package/dist/types/step.d.ts +30 -5
  165. package/dist/types/subprocess-adapter.d.ts +119 -3
  166. package/dist/types/subprocess-adapter.js +2 -0
  167. package/dist/types/tool.d.ts +64 -0
  168. package/dist/types/tool.js +1 -0
  169. package/dist/unstable.d.ts +2 -2
  170. package/dist/unstable.js +2 -2
  171. package/dist/until/predicates.d.ts +9 -0
  172. package/dist/until/predicates.js +14 -0
  173. package/dist/util/message-helpers.d.ts +17 -0
  174. package/dist/util/message-helpers.js +64 -0
  175. package/package.json +1 -1
  176. package/dist/adapters/local-fs-adapter.d.ts +0 -3
  177. package/dist/adapters/local-fs-adapter.js +0 -43
  178. package/dist/adapters/local-shell-adapter.d.ts +0 -51
  179. package/dist/adapters/local-shell-adapter.js +0 -167
  180. package/dist/adapters/local-subprocess-adapter.d.ts +0 -16
  181. package/dist/adapters/local-subprocess-adapter.js +0 -187
  182. package/dist/interpreter/execute-branch.d.ts +0 -3
  183. package/dist/interpreter/execute-branch.js +0 -10
  184. package/dist/interpreter/execute-every.js +0 -143
  185. package/dist/interpreter/execute-fork.d.ts +0 -3
  186. package/dist/interpreter/execute-fork.js +0 -215
  187. package/dist/interpreter/execute-llm.d.ts +0 -4
  188. package/dist/interpreter/execute-llm.js +0 -217
  189. package/dist/interpreter/execute-loop.d.ts +0 -3
  190. package/dist/interpreter/execute-loop.js +0 -171
  191. package/dist/interpreter/execute-provide.d.ts +0 -10
  192. package/dist/interpreter/execute-provide.js +0 -64
  193. package/dist/interpreter/execute-run.d.ts +0 -3
  194. package/dist/interpreter/execute-run.js +0 -39
  195. package/dist/interpreter/execute-spawn.d.ts +0 -11
  196. package/dist/interpreter/execute-spawn.js +0 -113
  197. package/dist/interpreter/execute-tool.d.ts +0 -5
  198. package/dist/interpreter/execute-tool.js +0 -81
  199. package/dist/runtime/agent-harness.js +0 -1170
  200. /package/dist/{runtime → memory}/exec-context-factory.d.ts +0 -0
  201. /package/dist/{interpreter → util}/framework-cast.d.ts +0 -0
  202. /package/dist/{interpreter → util}/framework-cast.js +0 -0
@@ -0,0 +1,39 @@
1
+ import type { LLMResponse } from './common';
2
+ import type { FsAdapter } from './fs-adapter';
3
+ import type { Item } from './items';
4
+ import type { ShellAdapter } from './shell-adapter';
5
+ /** @public Isolation scope controlling how a memory layer's state is keyed and shared. */
6
+ export type MemoryScope = 'thread' | 'resource' | 'global' | 'execution';
7
+ /** @public Terminal outcome of an execution run, reported to memory layers on completion. */
8
+ export type ExecutionOutcome = 'success' | 'failure' | 'aborted';
9
+ /** @public Minimal model-call surface exposed to memory-layer hooks. */
10
+ export interface MemoryCallModelRequest {
11
+ model: string;
12
+ items: ReadonlyArray<Item>;
13
+ instructions?: string;
14
+ }
15
+ /** @public Runtime metadata available to memory layer hooks during each lifecycle phase. */
16
+ export interface ExecutionContext {
17
+ executionId: string;
18
+ threadId: string;
19
+ resourceId?: string;
20
+ depth: number;
21
+ stepNumber: number;
22
+ tokenUsage: {
23
+ input: number;
24
+ output: number;
25
+ };
26
+ cost: number;
27
+ /** Filesystem adapter for virtual or real filesystem access. */
28
+ fs: FsAdapter;
29
+ /** Shell adapter for virtual or real shell command execution. */
30
+ shell: ShellAdapter;
31
+ callModel?: (request: MemoryCallModelRequest) => Promise<LLMResponse>;
32
+ tokenize(text: string): number;
33
+ trace: {
34
+ setAttribute(key: string, value: string | number | boolean): void;
35
+ addEvent(name: string, attributes?: Record<string, string | number | boolean>): void;
36
+ };
37
+ /** Snapshot a sibling memory layer's state by its `layer.id`. */
38
+ readLayerState<T>(layerId: string): T | undefined;
39
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,11 +1,29 @@
1
1
  import type { ZodType } from 'zod';
2
2
  import type { LLMResponse } from './common';
3
- import type { ItemLog } from './context';
4
- import type { FsAdapter } from './fs-adapter';
3
+ import type { ItemLog } from './context-parts/item-log';
5
4
  import type { Item, ItemSchemaExtensions } from './items';
6
- import type { CallModelRequest } from './runtime';
7
- import type { ShellAdapter } from './shell-adapter';
5
+ import type { ExecutionContext, ExecutionOutcome, MemoryScope } from './memory-context';
8
6
  import type { AfterModelCallParams, AfterModelCallResult, BeforeToolCallParams, BeforeToolCallResult } from './steering';
7
+ export type { ExecutionContext, ExecutionOutcome, MemoryCallModelRequest, MemoryScope, } from './memory-context';
8
+ /** @public Isolation scope controlling how a memory layer's state is keyed and shared. */
9
+ export type BudgetConfig = number | {
10
+ min: number;
11
+ max: number;
12
+ } | 'auto';
13
+ /** @public Per-hook timeout overrides in milliseconds for a memory layer. */
14
+ export interface LayerTimeouts {
15
+ init?: number;
16
+ recall?: number;
17
+ store?: number;
18
+ onSpawn?: number;
19
+ onReturn?: number;
20
+ onComplete?: number;
21
+ dispose?: number;
22
+ beforeToolCall?: number;
23
+ afterModelCall?: number;
24
+ onItemAppend?: number;
25
+ projectHistory?: number;
26
+ }
9
27
  /** @public A read-only data projection from layer state, accessible via `ctx.memory['layerId'].prop`. */
10
28
  export interface LayerDataDecl<T = unknown, TState = unknown> {
11
29
  kind: 'data';
@@ -65,8 +83,19 @@ export interface MemoryConfig<TLayers extends readonly MemoryLayer[] = readonly
65
83
  /** Phantom field carrying the inferred memory shape. Never accessed at runtime. */
66
84
  readonly _shape: InferMemoryShape<TLayers>;
67
85
  }
68
- /** @public Extract the typed memory shape from a MemoryConfig. Usage: `type Mem = InferMemory<typeof config>` */
69
- export type InferMemory<T extends MemoryConfig> = T['_shape'];
86
+ /**
87
+ * Extract the typed memory shape from a MemoryConfig.
88
+ * Constrains structurally on the phantom `_shape` field rather than on
89
+ * `MemoryConfig` itself: `MemoryConfig` is invariant in `TLayers` (the `_shape`
90
+ * field carries it in an invariant position), so a concrete
91
+ * `MemoryConfig<readonly [SomeLayer]>` does not satisfy the defaulted
92
+ * `MemoryConfig<readonly MemoryLayer[]>`. Since `InferMemory` only reads
93
+ * `_shape`, this constraint accepts any config the `memory()` builder produces.
94
+ * @public Usage: `type Mem = InferMemory<typeof config>`
95
+ */
96
+ export type InferMemory<T extends {
97
+ readonly _shape: unknown;
98
+ }> = T['_shape'];
70
99
  /** @public Well-known ordering slots for positioning memory layers in the recall/store pipeline. */
71
100
  export declare const Slot: {
72
101
  readonly REMINDER: 80;
@@ -79,65 +108,6 @@ export declare const Slot: {
79
108
  readonly RAG: 350;
80
109
  readonly SEMANTIC_RECALL: 400;
81
110
  };
82
- /** @public Isolation scope controlling how a memory layer's state is keyed and shared. */
83
- export type MemoryScope = 'thread' | 'resource' | 'global' | 'execution';
84
- /** @public Token budget specification for a memory layer: fixed number, min/max range, or automatic. */
85
- export type BudgetConfig = number | {
86
- min: number;
87
- max: number;
88
- } | 'auto';
89
- /** @public Per-hook timeout overrides in milliseconds for a memory layer. */
90
- export interface LayerTimeouts {
91
- init?: number;
92
- recall?: number;
93
- store?: number;
94
- onSpawn?: number;
95
- onReturn?: number;
96
- onComplete?: number;
97
- dispose?: number;
98
- beforeToolCall?: number;
99
- afterModelCall?: number;
100
- onItemAppend?: number;
101
- projectHistory?: number;
102
- }
103
- /** @public Terminal outcome of an execution run, reported to memory layers on completion. */
104
- export type ExecutionOutcome = 'success' | 'failure' | 'aborted';
105
- /** @public Runtime metadata available to memory layer hooks during each lifecycle phase. */
106
- export interface ExecutionContext {
107
- executionId: string;
108
- threadId: string;
109
- resourceId?: string;
110
- depth: number;
111
- stepNumber: number;
112
- tokenUsage: {
113
- input: number;
114
- output: number;
115
- };
116
- cost: number;
117
- /** Filesystem adapter for virtual or real filesystem access. */
118
- fs: FsAdapter;
119
- /** Shell adapter for virtual or real shell command execution. */
120
- shell: ShellAdapter;
121
- callModel?: (request: CallModelRequest) => Promise<LLMResponse>;
122
- tokenize(text: string): number;
123
- trace: {
124
- setAttribute(key: string, value: string | number | boolean): void;
125
- addEvent(name: string, attributes?: Record<string, string | number | boolean>): void;
126
- };
127
- /**
128
- * Snapshot a sibling memory layer's state by its `layer.id`.
129
- * Returns `undefined` if no layer with that ID has stored state in this execution.
130
- * Enables cross-layer coordination (e.g., a reminder layer reading a planning layer's mode flag).
131
- *
132
- * **Type safety:** The generic `T` is an author-assertion — it is NOT
133
- * runtime-validated. Any layer may register under the queried id with an
134
- * arbitrary state shape, so callers MUST add a runtime shape guard (e.g.
135
- * `Array.isArray`, a Zod parse, or a narrow `typeof` check) before
136
- * dereferencing fields. See `reminder-triggers.ts` (`hasSources`) in
137
- * `@noetic/cli` for the canonical pattern.
138
- */
139
- readLayerState<T>(layerId: string): T | undefined;
140
- }
141
111
  /** @public Low-level key-value persistence backend used by scoped storage and memory layers. */
142
112
  export interface StorageAdapter {
143
113
  get<T>(key: string): Promise<T | null>;
@@ -1,6 +1,6 @@
1
1
  import type { ZodType } from 'zod';
2
2
  import type { Channel, ChannelHandle, ExternalChannel } from './channel';
3
- import type { LLMResponse, ModelParams, Tool } from './common';
3
+ import type { LLMResponse, ModelParams } from './common';
4
4
  import type { Context, CwdState } from './context';
5
5
  import type { DetachedHandle } from './detached';
6
6
  import type { FsAdapter } from './fs-adapter';
@@ -12,6 +12,7 @@ import type { ShellAdapter } from './shell-adapter';
12
12
  import type { SteeringDecision } from './steering';
13
13
  import type { Step } from './step';
14
14
  import type { SubprocessAdapter } from './subprocess-adapter';
15
+ import type { Tool } from './tool';
15
16
  /** @public Optional lifecycle hooks invoked before and after each step execution. */
16
17
  export interface AgentHooks {
17
18
  beforeStep?: (step: Step, ctx: Context) => Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import type { LLMResponse } from './common';
2
- import type { ExecutionContext, MemoryScope } from './memory';
2
+ import type { ExecutionContext, MemoryScope } from './memory-context';
3
3
  /** @public Enumeration of actions a steering rule can take on a tool call or model response. */
4
4
  export declare const SteeringAction: {
5
5
  Allow: string;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `StepRegistrar` is the layer-safe indirection that lets builders
3
+ * (in `core-primitives`) register steps without importing the runtime
4
+ * registry directly (which lives in `core-runtime`, a lower layer).
5
+ *
6
+ * The runtime provides the concrete implementation via
7
+ * `setDefaultRegistrar()` on module load. Until then a no-op fallback
8
+ * is active so builders constructed outside a harness (e.g. in unit
9
+ * tests that never touch the interpreter) don't throw.
10
+ *
11
+ * The registry itself lives in `packages/core/src/runtime/step-registry.ts`
12
+ * and is consulted only by `packages/core/src/harness/step-bootstrap.ts`
13
+ * when an out-of-process child needs to look up a step by id. In-process
14
+ * execution walks the step tree directly and never touches either side.
15
+ */
16
+ import type { Step } from './step';
17
+ /**
18
+ * @public
19
+ * Registers a step under its `id` so the subprocess adapter can later
20
+ * look it up by name from an out-of-process child. Implementations
21
+ * typically call the runtime's underlying `registerStep` function.
22
+ */
23
+ export interface StepRegistrar {
24
+ register<TMemory, I, O>(step: Step<TMemory, I, O>): void;
25
+ }
26
+ /**
27
+ * @public
28
+ * Returns the active registrar. Called by every step builder to register
29
+ * the step it just constructed. Default is a no-op; the runtime installs
30
+ * the real implementation via `setDefaultRegistrar()` on module load.
31
+ */
32
+ export declare function getDefaultRegistrar(): StepRegistrar;
33
+ /**
34
+ * @public
35
+ * Installs a registrar implementation. Called exactly once by the
36
+ * runtime on module load; tests that want per-case isolation can
37
+ * install a throwing or counting registrar and restore the default
38
+ * with `resetDefaultRegistrar()` in their teardown.
39
+ */
40
+ export declare function setDefaultRegistrar(registrar: StepRegistrar): void;
41
+ /**
42
+ * @public
43
+ * Restores the no-op registrar. Primarily a test helper — production
44
+ * code paths rely on the runtime having installed the real registrar.
45
+ */
46
+ export declare function resetDefaultRegistrar(): void;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `StepRegistrar` is the layer-safe indirection that lets builders
3
+ * (in `core-primitives`) register steps without importing the runtime
4
+ * registry directly (which lives in `core-runtime`, a lower layer).
5
+ *
6
+ * The runtime provides the concrete implementation via
7
+ * `setDefaultRegistrar()` on module load. Until then a no-op fallback
8
+ * is active so builders constructed outside a harness (e.g. in unit
9
+ * tests that never touch the interpreter) don't throw.
10
+ *
11
+ * The registry itself lives in `packages/core/src/runtime/step-registry.ts`
12
+ * and is consulted only by `packages/core/src/harness/step-bootstrap.ts`
13
+ * when an out-of-process child needs to look up a step by id. In-process
14
+ * execution walks the step tree directly and never touches either side.
15
+ */
16
+ //#endregion
17
+ //#region Module state
18
+ /**
19
+ * No-op registrar used until the runtime installs the real one. Keeps
20
+ * builder-only callsites (e.g. tests that construct a step without
21
+ * instantiating `AgentHarness`) non-throwing.
22
+ */
23
+ const noopRegistrar = {
24
+ register: () => undefined,
25
+ };
26
+ let currentRegistrar = noopRegistrar;
27
+ //#endregion
28
+ //#region Public API
29
+ /**
30
+ * @public
31
+ * Returns the active registrar. Called by every step builder to register
32
+ * the step it just constructed. Default is a no-op; the runtime installs
33
+ * the real implementation via `setDefaultRegistrar()` on module load.
34
+ */
35
+ export function getDefaultRegistrar() {
36
+ return currentRegistrar;
37
+ }
38
+ /**
39
+ * @public
40
+ * Installs a registrar implementation. Called exactly once by the
41
+ * runtime on module load; tests that want per-case isolation can
42
+ * install a throwing or counting registrar and restore the default
43
+ * with `resetDefaultRegistrar()` in their teardown.
44
+ */
45
+ export function setDefaultRegistrar(registrar) {
46
+ currentRegistrar = registrar;
47
+ }
48
+ /**
49
+ * @public
50
+ * Restores the no-op registrar. Primarily a test helper — production
51
+ * code paths rely on the runtime having installed the real registrar.
52
+ */
53
+ export function resetDefaultRegistrar() {
54
+ currentRegistrar = noopRegistrar;
55
+ }
56
+ //#endregion
@@ -1,9 +1,11 @@
1
1
  import type { ZodType } from 'zod';
2
2
  import type { Channel } from './channel';
3
- import type { ModelParams, RetryPolicy, StepMeta, Tool } from './common';
3
+ import type { ModelParams, RetryPolicy, StepMeta } from './common';
4
4
  import type { Context } from './context';
5
5
  import type { NoeticError } from './error';
6
6
  import type { ContextMemory, MemoryConfig, MemoryLayer } from './memory';
7
+ import type { SubprocessAdapter } from './subprocess-adapter';
8
+ import type { Tool } from './tool';
7
9
  /**
8
10
  * Cumulative execution snapshot passed to loop `until` predicates.
9
11
  * @public
@@ -46,6 +48,13 @@ export interface Verdict {
46
48
  }
47
49
  /** @public Predicate function evaluated after each loop iteration to decide whether to stop. */
48
50
  export type Until = (snapshot: Snapshot) => Verdict | Promise<Verdict>;
51
+ /**
52
+ * Field type that accepts either an eager value or a `(ctx) => value` getter.
53
+ * Used by `step.llm` for params that may vary per execution (model, instructions,
54
+ * tool list). The getter runs at step execution time with the live context.
55
+ * @public
56
+ */
57
+ export type Lazy<T, TMemory = ContextMemory> = T | ((ctx: Context<TMemory>) => T | Promise<T>);
49
58
  /**
50
59
  * Outcome of a single path in a `settle`-mode fork (mirrors `Promise.allSettled`).
51
60
  * @public
@@ -68,14 +77,24 @@ export interface StepRun<TMemory = ContextMemory, I = unknown, O = unknown> {
68
77
  id: string;
69
78
  execute: (input: I, ctx: Context<TMemory>) => Promise<O>;
70
79
  retry?: RetryPolicy;
80
+ /**
81
+ * Per-step subprocess adapter override. When set, the interpreter
82
+ * dispatches this step through the given adapter instead of the harness
83
+ * default. Resolution order at dispatch is
84
+ * `detachedSpawn-overrides.subprocess ?? step.subprocess ?? harness.subprocess`.
85
+ */
86
+ subprocess?: SubprocessAdapter;
71
87
  }
72
88
  /** @public A step that sends a prompt to a language model and returns its response. */
73
- export interface StepLLM<_TMemory = ContextMemory, _I = unknown, O = unknown> {
89
+ export interface StepLLM<TMemory = ContextMemory, _I = unknown, O = unknown> {
74
90
  kind: 'llm';
75
91
  id: string;
76
- model: string;
77
- instructions?: string;
78
- tools?: Tool[];
92
+ /** Model id. Accepts either an eager string or a `(ctx) => string` getter. */
93
+ model: Lazy<string, TMemory>;
94
+ /** System instructions. Eager string or `(ctx) => string | undefined` getter. */
95
+ instructions?: Lazy<string | undefined, TMemory>;
96
+ /** Tools exposed to the LLM. Eager array or `(ctx) => Tool[] | undefined` getter. Function-form tools do not contribute to the pre-computed `ctx.unifiedTools`; they are resolved per execution. */
97
+ tools?: Lazy<Tool[] | undefined, TMemory>;
79
98
  output?: ZodType<O>;
80
99
  params?: ModelParams;
81
100
  /** Controls framework event emission for this step. Defaults to `true`. Set `false` to suppress all framework events. A filter function receives `(eventType, data)` and returns `boolean`. */
@@ -145,6 +164,12 @@ export interface StepSpawn<TMemory = ContextMemory, I = unknown, O = unknown> {
145
164
  child: Step<TMemory, I, O>;
146
165
  memory?: MemoryConfig | MemoryLayer[];
147
166
  timeout?: number;
167
+ /**
168
+ * Per-step subprocess adapter override applied when the interpreter
169
+ * dispatches this spawn. Resolution order is
170
+ * `detachedSpawn-overrides.subprocess ?? step.subprocess ?? harness.subprocess`.
171
+ */
172
+ subprocess?: SubprocessAdapter;
148
173
  }
149
174
  /**
150
175
  * A loop step that iterates a body step until a termination predicate is satisfied.
@@ -1,20 +1,107 @@
1
+ /** @public Lifecycle states for a subprocess handle. */
1
2
  export type SubprocessStatus = 'starting' | 'running' | 'paused' | 'completed' | 'failed' | 'stopped' | 'stale';
2
- export interface SubprocessRequest {
3
+ /**
4
+ * @public
5
+ * Plain-object shape used to transport an error from a subprocess back to the
6
+ * parent via `SubprocessHandle.metadata.error`. Non-serialisable fields (e.g.
7
+ * `Error.cause` chains with functions or cyclic data) are dropped at the
8
+ * producer boundary.
9
+ */
10
+ export interface SerializedError {
11
+ /** Human-readable message; always present. */
12
+ message: string;
13
+ /** Constructor `name` of the underlying error (`Error`, `NoeticError`, etc.). */
14
+ name?: string;
15
+ /** Best-effort stack trace, when the producing runtime retained one. */
16
+ stack?: string;
17
+ /**
18
+ * Structured `NoeticError` payload carried through for in-process
19
+ * transport. Consumers may reconstruct `NoeticErrorImpl` from this when
20
+ * they need typed error kinds.
21
+ */
22
+ noeticError?: unknown;
23
+ }
24
+ /** @public Fields common to every subprocess request variant. */
25
+ interface SubprocessRequestBase {
26
+ metadata?: Record<string, unknown>;
27
+ }
28
+ /**
29
+ * @public
30
+ * Launches an OS-level child process. This is the classic variant used by the
31
+ * tasks runner to spawn planner / implementer children.
32
+ *
33
+ * When `kind` is omitted the request is treated as a process request — this
34
+ * preserves backward compatibility with callers that predate the
35
+ * `StepSubprocessRequest` variant.
36
+ */
37
+ export interface ProcessSubprocessRequest extends SubprocessRequestBase {
38
+ kind?: 'process';
3
39
  command: string;
4
40
  args?: ReadonlyArray<string>;
5
41
  cwd?: string;
6
42
  env?: Record<string, string | undefined>;
7
43
  detached?: boolean;
8
44
  stdin?: string;
9
- metadata?: Record<string, unknown>;
10
45
  }
46
+ /** @public Overrides applied to the child context when executing a step request. */
47
+ export interface StepSubprocessOverrides {
48
+ /** Override the child's thread id. */
49
+ threadId?: string;
50
+ /** Override the child's resource id. */
51
+ resourceId?: string;
52
+ /** Override the child's initial cwd. */
53
+ cwdInit?: string;
54
+ }
55
+ /**
56
+ * @public
57
+ * Dispatches a step through the adapter. `stepId` resolves against the shared
58
+ * step registry — the in-memory adapter invokes the in-process execute pipeline,
59
+ * while out-of-process adapters spawn a child runtime that re-imports the same
60
+ * registry via a configured entry point.
61
+ */
62
+ export interface StepSubprocessRequest extends SubprocessRequestBase {
63
+ kind: 'step';
64
+ stepId: string;
65
+ /** Serialisable input for the step. Stored in `handle.metadata` when needed. */
66
+ serializedInput: unknown;
67
+ /** Stable id for the execution — used for handle lookup and durable checkpointing. */
68
+ executionId: string;
69
+ /** Child-context overrides (thread, resource, cwd). */
70
+ overrides: StepSubprocessOverrides;
71
+ /**
72
+ * @internal
73
+ * Closure used by the default in-memory adapter to run the step in the same
74
+ * process as the caller. Non-serialisable — adapters that cross a process
75
+ * boundary MUST ignore this field. Framework-internal callers set it; public
76
+ * consumers should not.
77
+ */
78
+ _localExecutor?: () => Promise<unknown>;
79
+ }
80
+ /** @public Discriminated union covering every shape `SubprocessAdapter.spawn()` accepts. */
81
+ export type SubprocessRequest = ProcessSubprocessRequest | StepSubprocessRequest;
82
+ /**
83
+ * @public
84
+ * Well-known keys carried on `SubprocessHandle.metadata`. Adapters populate
85
+ * these during their lifecycle; consumers read them via the helpers exported
86
+ * alongside the adapter types.
87
+ */
88
+ export interface SubprocessHandleMetadata extends Record<string, unknown> {
89
+ /** Awaited step result, stored when a step request completes successfully. */
90
+ result?: unknown;
91
+ /** Serialised error, stored when a step request fails. */
92
+ error?: SerializedError;
93
+ /** Execution id echoed from the originating `StepSubprocessRequest`. */
94
+ executionId?: string;
95
+ }
96
+ /** @public Handle returned by `SubprocessAdapter.spawn()` — a durable reference to a child. */
11
97
  export interface SubprocessHandle {
12
98
  id: string;
13
99
  status: SubprocessStatus;
14
100
  startedAt: string;
15
101
  updatedAt?: string;
16
- metadata?: Record<string, unknown>;
102
+ metadata?: SubprocessHandleMetadata;
17
103
  }
104
+ /** @public Outcome of `pause` / `resume` calls on a subprocess handle. */
18
105
  export type SubprocessControlResult = {
19
106
  kind: 'ok';
20
107
  handle: SubprocessHandle;
@@ -26,16 +113,45 @@ export type SubprocessControlResult = {
26
113
  kind: 'not_found';
27
114
  handleId: string;
28
115
  };
116
+ /** @public Outcome of a `stop()` call on a subprocess handle. */
29
117
  export interface SubprocessStopResult {
30
118
  kind: 'stopped' | 'not_found';
31
119
  handleId: string;
32
120
  handle?: SubprocessHandle;
33
121
  }
122
+ /**
123
+ * @public
124
+ * Contract for dispatching and managing subprocess-style executions — both
125
+ * OS-level child processes and in-process step runs. Implementations own the
126
+ * lifecycle of every handle they return, including persistence when they opt
127
+ * into durable execution via `reattach` / `listLive`.
128
+ */
34
129
  export interface SubprocessAdapter {
130
+ /** Dispatches a new request and returns its handle. */
35
131
  spawn(request: SubprocessRequest): Promise<SubprocessHandle>;
132
+ /** Fetches the current handle by id, or `null` when unknown. */
36
133
  get(handleId: string): Promise<SubprocessHandle | null>;
134
+ /** Requests cancellation of a running handle. */
37
135
  stop(handleId: string, reason?: string): Promise<SubprocessStopResult>;
136
+ /** Pauses a running handle (when the adapter supports it). */
38
137
  pause(handleId: string): Promise<SubprocessControlResult>;
138
+ /** Resumes a paused handle (when the adapter supports it). */
39
139
  resume(handleId: string): Promise<SubprocessControlResult>;
140
+ /** Returns true when the underlying execution is still alive. */
40
141
  isAlive(handle: SubprocessHandle): Promise<boolean>;
142
+ /**
143
+ * Rebinds to a handle persisted across a host restart. Returns `null` when
144
+ * no manifest exists for the given id. Durability is an adapter-level
145
+ * concern — the in-memory adapter returns `null` by default (handles are
146
+ * ephemeral), while the local adapter consults its storage.
147
+ */
148
+ reattach(handleId: string): Promise<SubprocessHandle | null>;
149
+ /**
150
+ * Lists every handle the adapter currently treats as live. Used by host
151
+ * recovery to rediscover running children on startup. The in-memory adapter
152
+ * returns the set of active in-process handles; the local adapter scans its
153
+ * persisted manifest.
154
+ */
155
+ listLive(): Promise<ReadonlyArray<SubprocessHandle>>;
41
156
  }
157
+ export {};
@@ -1 +1,3 @@
1
+ //#region Status
1
2
  export {};
3
+ //#endregion
@@ -0,0 +1,64 @@
1
+ import type { ZodTypeAny, z } from 'zod';
2
+ import type { FunctionCallItem, FunctionCallOutputItem, Item, ItemSchemaExtensions, ToolResultExtensionItem } from './items';
3
+ type ToolExecutionResult<O extends ZodTypeAny> = Promise<z.infer<O>> | AsyncGenerator<unknown, z.infer<O>>;
4
+ /**
5
+ * Declares tool-owned memory that the runtime materializes into a MemoryLayer.
6
+ * @public
7
+ */
8
+ export interface ToolMemoryDeclaration<TState = unknown> {
9
+ /** Shared id — tools with the same id share state. Defaults to `tool.name`. */
10
+ id?: string;
11
+ /** Factory for the initial state. */
12
+ init(): TState;
13
+ /**
14
+ * Project state into the LLM context. Return null to omit.
15
+ * Declared as a method (bivariant params) so a concrete
16
+ * `ToolMemoryDeclaration<MyState>` assigns to the erased
17
+ * `ToolMemoryDeclaration` the `tool()` builder and runtime consume.
18
+ */
19
+ recall(state: TState): string | null;
20
+ }
21
+ /**
22
+ * A tool definition that an LLM can invoke during execution.
23
+ *
24
+ * The runtime passes a `ToolExecutionContext` (from `./tool-context`) as
25
+ * the second argument to `execute`. Callers that need the concrete type
26
+ * should import `Tool` from the package root (`@noetic-tools/core`), which
27
+ * re-exports it with `ToolExecutionContext` substituted in.
28
+ * @public
29
+ */
30
+ export interface Tool<I extends ZodTypeAny = ZodTypeAny, O extends ZodTypeAny = ZodTypeAny> {
31
+ /** Unique tool name used by the LLM for selection. */
32
+ name: string;
33
+ /** Human-readable description shown to the LLM. */
34
+ description: string;
35
+ /** Zod schema validating tool input arguments. */
36
+ input: I;
37
+ /** Zod schema validating tool return value. */
38
+ output: O;
39
+ /** Optional Zod schema validating streaming events yielded during execution. */
40
+ event?: ZodTypeAny;
41
+ /** Optional item schemas contributed by this tool for tool call/result extensions. */
42
+ itemSchemas?: Pick<ItemSchemaExtensions, 'toolCalls' | 'toolResults' | 'items'>;
43
+ /** Decorate the harness-created tool result item before it is appended/emitted. */
44
+ decorateResultItem?(params: {
45
+ baseItem: FunctionCallOutputItem;
46
+ callItem: FunctionCallItem;
47
+ args: z.infer<I>;
48
+ result: z.infer<O> | undefined;
49
+ output: string;
50
+ error?: boolean;
51
+ }): Item | ToolResultExtensionItem;
52
+ /**
53
+ * Async function that performs the tool's work. `toolCtx` is a
54
+ * `ToolExecutionContext` at runtime — typed as `unknown` here to keep
55
+ * this type a dependency leaf. Use `tool()` from `builders/` or cast at
56
+ * the call site to get a typed handle.
57
+ */
58
+ execute(args: z.infer<I>, toolCtx: unknown): ToolExecutionResult<O>;
59
+ /** When true, execution pauses for human approval before running. */
60
+ needsApproval?: boolean;
61
+ /** Optional memory declaration — the runtime generates a MemoryLayer from this. */
62
+ memory?: ToolMemoryDeclaration;
63
+ }
64
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -6,8 +6,6 @@
6
6
  * They are intended for authors of custom memory layers, runtime backends,
7
7
  * and other framework extensions. Do not depend on them in application code.
8
8
  */
9
- /** @unstable Unsafe cast utility for bridging internal type boundaries. */
10
- export { frameworkCast } from './interpreter/framework-cast';
11
9
  /** @unstable Budget allocation algorithm for memory layer token budgets. */
12
10
  export type { BudgetAllocation, BudgetLimits } from './memory/budget';
13
11
  /** @unstable Budget allocation and checking utilities. */
@@ -26,3 +24,5 @@ export { createScopedStorage } from './memory/scope';
26
24
  * the public surface — call sites must hold exclusive use of the parent ctx.
27
25
  */
28
26
  export { retargetCwdForSpawn } from './runtime/cwd-helpers';
27
+ /** @unstable Unsafe cast utility for bridging internal type boundaries. */
28
+ export { frameworkCast } from './util/framework-cast';
package/dist/unstable.js CHANGED
@@ -6,8 +6,6 @@
6
6
  * They are intended for authors of custom memory layers, runtime backends,
7
7
  * and other framework extensions. Do not depend on them in application code.
8
8
  */
9
- /** @unstable Unsafe cast utility for bridging internal type boundaries. */
10
- export { frameworkCast } from './interpreter/framework-cast';
11
9
  /** @unstable Budget allocation and checking utilities. */
12
10
  export { allocateBudgets, checkBudget } from './memory/budget';
13
11
  /** @unstable Factory for creating layer state stores. */
@@ -22,3 +20,5 @@ export { createScopedStorage } from './memory/scope';
22
20
  * the public surface — call sites must hold exclusive use of the parent ctx.
23
21
  */
24
22
  export { retargetCwdForSpawn } from './runtime/cwd-helpers';
23
+ /** @unstable Unsafe cast utility for bridging internal type boundaries. */
24
+ export { frameworkCast } from './util/framework-cast';
@@ -38,6 +38,15 @@ export declare const until: {
38
38
  converged(opts: ConvergeConfig): Until;
39
39
  /** Stops the loop when the last output text contains the given marker string. @public */
40
40
  outputContains(marker: string): Until;
41
+ /**
42
+ * Stops the loop when the last output text exactly equals the given
43
+ * sentinel string. Prefer this over `outputContains` when the marker is a
44
+ * dedicated exit sentinel produced by a terminator step — exact equality
45
+ * avoids substring collisions with unrelated step output that happens to
46
+ * quote the sentinel.
47
+ * @public
48
+ */
49
+ outputEquals(sentinel: string): Until;
41
50
  /** Wraps an arbitrary `Until` function as a named predicate. @public */
42
51
  custom(fn: Until): Until;
43
52
  };
@@ -119,6 +119,20 @@ export const until = {
119
119
  reason: snap.lastText.includes(marker) ? `Output contains marker: ${marker}` : undefined,
120
120
  });
121
121
  },
122
+ /**
123
+ * Stops the loop when the last output text exactly equals the given
124
+ * sentinel string. Prefer this over `outputContains` when the marker is a
125
+ * dedicated exit sentinel produced by a terminator step — exact equality
126
+ * avoids substring collisions with unrelated step output that happens to
127
+ * quote the sentinel.
128
+ * @public
129
+ */
130
+ outputEquals(sentinel) {
131
+ return (snap) => ({
132
+ stop: snap.lastText === sentinel,
133
+ reason: snap.lastText === sentinel ? `Output matched sentinel: ${sentinel}` : undefined,
134
+ });
135
+ },
122
136
  /** Wraps an arbitrary `Until` function as a named predicate. @public */
123
137
  custom(fn) {
124
138
  return fn;