@noetic-tools/core 0.0.1

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 (207) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +38 -0
  3. package/dist/adapters/in-memory-fs-adapter.d.ts +3 -0
  4. package/dist/adapters/in-memory-fs-adapter.js +245 -0
  5. package/dist/adapters/in-memory-shell-adapter.d.ts +9 -0
  6. package/dist/adapters/in-memory-shell-adapter.js +20 -0
  7. package/dist/adapters/in-memory-subprocess-adapter.d.ts +5 -0
  8. package/dist/adapters/in-memory-subprocess-adapter.js +126 -0
  9. package/dist/adapters/local-fs-adapter.d.ts +3 -0
  10. package/dist/adapters/local-fs-adapter.js +43 -0
  11. package/dist/adapters/local-shell-adapter.d.ts +51 -0
  12. package/dist/adapters/local-shell-adapter.js +167 -0
  13. package/dist/adapters/local-subprocess-adapter.d.ts +16 -0
  14. package/dist/adapters/local-subprocess-adapter.js +187 -0
  15. package/dist/adapters/openrouter.d.ts +57 -0
  16. package/dist/adapters/openrouter.js +248 -0
  17. package/dist/builders/channel-builder.d.ts +36 -0
  18. package/dist/builders/channel-builder.js +15 -0
  19. package/dist/builders/control-flow-builders.d.ts +79 -0
  20. package/dist/builders/control-flow-builders.js +53 -0
  21. package/dist/builders/every.d.ts +45 -0
  22. package/dist/builders/every.js +60 -0
  23. package/dist/builders/layer-provides-builders.d.ts +32 -0
  24. package/dist/builders/layer-provides-builders.js +33 -0
  25. package/dist/builders/loop-builder.d.ts +23 -0
  26. package/dist/builders/loop-builder.js +46 -0
  27. package/dist/builders/memory-builder.d.ts +11 -0
  28. package/dist/builders/memory-builder.js +15 -0
  29. package/dist/builders/provide-builder.d.ts +20 -0
  30. package/dist/builders/provide-builder.js +34 -0
  31. package/dist/builders/spawn-builder.d.ts +20 -0
  32. package/dist/builders/spawn-builder.js +33 -0
  33. package/dist/builders/step-builders.d.ts +62 -0
  34. package/dist/builders/step-builders.js +99 -0
  35. package/dist/builders/tool-builder.d.ts +52 -0
  36. package/dist/builders/tool-builder.js +59 -0
  37. package/dist/conditions/conditions.d.ts +114 -0
  38. package/dist/conditions/conditions.js +271 -0
  39. package/dist/conditions/cosine-similarity.d.ts +11 -0
  40. package/dist/conditions/cosine-similarity.js +28 -0
  41. package/dist/conditions/index.d.ts +3 -0
  42. package/dist/conditions/index.js +2 -0
  43. package/dist/errors/noetic-config-error.d.ts +41 -0
  44. package/dist/errors/noetic-config-error.js +44 -0
  45. package/dist/errors/noetic-error.d.ts +18 -0
  46. package/dist/errors/noetic-error.js +52 -0
  47. package/dist/index.d.ts +152 -0
  48. package/dist/index.js +112 -0
  49. package/dist/internal-test.d.ts +11 -0
  50. package/dist/internal-test.js +11 -0
  51. package/dist/interpreter/clone-guard.d.ts +4 -0
  52. package/dist/interpreter/clone-guard.js +18 -0
  53. package/dist/interpreter/collect-tools.d.ts +12 -0
  54. package/dist/interpreter/collect-tools.js +70 -0
  55. package/dist/interpreter/execute-branch.d.ts +3 -0
  56. package/dist/interpreter/execute-branch.js +10 -0
  57. package/dist/interpreter/execute-every.d.ts +15 -0
  58. package/dist/interpreter/execute-every.js +143 -0
  59. package/dist/interpreter/execute-fork.d.ts +3 -0
  60. package/dist/interpreter/execute-fork.js +215 -0
  61. package/dist/interpreter/execute-llm.d.ts +4 -0
  62. package/dist/interpreter/execute-llm.js +217 -0
  63. package/dist/interpreter/execute-loop.d.ts +3 -0
  64. package/dist/interpreter/execute-loop.js +171 -0
  65. package/dist/interpreter/execute-provide.d.ts +10 -0
  66. package/dist/interpreter/execute-provide.js +64 -0
  67. package/dist/interpreter/execute-run.d.ts +3 -0
  68. package/dist/interpreter/execute-run.js +39 -0
  69. package/dist/interpreter/execute-spawn.d.ts +11 -0
  70. package/dist/interpreter/execute-spawn.js +113 -0
  71. package/dist/interpreter/execute-tool.d.ts +5 -0
  72. package/dist/interpreter/execute-tool.js +81 -0
  73. package/dist/interpreter/execute.d.ts +14 -0
  74. package/dist/interpreter/execute.js +119 -0
  75. package/dist/interpreter/framework-cast.d.ts +10 -0
  76. package/dist/interpreter/framework-cast.js +16 -0
  77. package/dist/interpreter/message-helpers.d.ts +9 -0
  78. package/dist/interpreter/message-helpers.js +42 -0
  79. package/dist/interpreter/typeguards.d.ts +25 -0
  80. package/dist/interpreter/typeguards.js +50 -0
  81. package/dist/memory/budget.d.ts +23 -0
  82. package/dist/memory/budget.js +117 -0
  83. package/dist/memory/function-call-utils.d.ts +6 -0
  84. package/dist/memory/function-call-utils.js +25 -0
  85. package/dist/memory/layer-api.d.ts +14 -0
  86. package/dist/memory/layer-api.js +91 -0
  87. package/dist/memory/layer-lifecycle.d.ts +160 -0
  88. package/dist/memory/layer-lifecycle.js +552 -0
  89. package/dist/memory/layer-usage.d.ts +15 -0
  90. package/dist/memory/layer-usage.js +48 -0
  91. package/dist/memory/layers/durable-task-state.d.ts +79 -0
  92. package/dist/memory/layers/durable-task-state.js +118 -0
  93. package/dist/memory/layers/file-reference.d.ts +55 -0
  94. package/dist/memory/layers/file-reference.js +538 -0
  95. package/dist/memory/layers/history-window.d.ts +29 -0
  96. package/dist/memory/layers/history-window.js +114 -0
  97. package/dist/memory/layers/observational-memory.d.ts +53 -0
  98. package/dist/memory/layers/observational-memory.js +110 -0
  99. package/dist/memory/layers/plan.d.ts +55 -0
  100. package/dist/memory/layers/plan.js +369 -0
  101. package/dist/memory/layers/static-content.d.ts +27 -0
  102. package/dist/memory/layers/static-content.js +35 -0
  103. package/dist/memory/layers/steering.d.ts +51 -0
  104. package/dist/memory/layers/steering.js +258 -0
  105. package/dist/memory/layers/tool-memory-layer.d.ts +16 -0
  106. package/dist/memory/layers/tool-memory-layer.js +43 -0
  107. package/dist/memory/layers/working-memory.d.ts +44 -0
  108. package/dist/memory/layers/working-memory.js +92 -0
  109. package/dist/memory/projector.d.ts +10 -0
  110. package/dist/memory/projector.js +19 -0
  111. package/dist/memory/scope.d.ts +3 -0
  112. package/dist/memory/scope.js +31 -0
  113. package/dist/memory/strip-unresolved.d.ts +13 -0
  114. package/dist/memory/strip-unresolved.js +35 -0
  115. package/dist/observability/genai-attributes.d.ts +11 -0
  116. package/dist/observability/genai-attributes.js +12 -0
  117. package/dist/observability/span-impl.d.ts +20 -0
  118. package/dist/observability/span-impl.js +33 -0
  119. package/dist/observability/trace-exporter.d.ts +15 -0
  120. package/dist/observability/trace-exporter.js +26 -0
  121. package/dist/patterns/flow.d.ts +52 -0
  122. package/dist/patterns/flow.js +103 -0
  123. package/dist/patterns/interview.d.ts +53 -0
  124. package/dist/patterns/interview.js +113 -0
  125. package/dist/patterns/plans.d.ts +45 -0
  126. package/dist/patterns/plans.js +125 -0
  127. package/dist/patterns/ralph-wiggum.d.ts +20 -0
  128. package/dist/patterns/ralph-wiggum.js +37 -0
  129. package/dist/patterns/react.d.ts +18 -0
  130. package/dist/patterns/react.js +39 -0
  131. package/dist/portable.d.ts +26 -0
  132. package/dist/portable.js +34 -0
  133. package/dist/runtime/agent-harness.d.ts +206 -0
  134. package/dist/runtime/agent-harness.js +1170 -0
  135. package/dist/runtime/broadcaster-utils.d.ts +30 -0
  136. package/dist/runtime/broadcaster-utils.js +53 -0
  137. package/dist/runtime/channel-store.d.ts +18 -0
  138. package/dist/runtime/channel-store.js +187 -0
  139. package/dist/runtime/context-impl.d.ts +80 -0
  140. package/dist/runtime/context-impl.js +140 -0
  141. package/dist/runtime/cwd-helpers.d.ts +42 -0
  142. package/dist/runtime/cwd-helpers.js +68 -0
  143. package/dist/runtime/detached-handle.d.ts +14 -0
  144. package/dist/runtime/detached-handle.js +56 -0
  145. package/dist/runtime/event-broadcaster.d.ts +61 -0
  146. package/dist/runtime/event-broadcaster.js +187 -0
  147. package/dist/runtime/exec-context-factory.d.ts +25 -0
  148. package/dist/runtime/exec-context-factory.js +30 -0
  149. package/dist/runtime/in-memory-storage.d.ts +9 -0
  150. package/dist/runtime/in-memory-storage.js +33 -0
  151. package/dist/runtime/item-log-impl.d.ts +11 -0
  152. package/dist/runtime/item-log-impl.js +25 -0
  153. package/dist/runtime/message-queue.d.ts +30 -0
  154. package/dist/runtime/message-queue.js +46 -0
  155. package/dist/runtime/session-runner.d.ts +77 -0
  156. package/dist/runtime/session-runner.js +253 -0
  157. package/dist/runtime/session-streams.d.ts +11 -0
  158. package/dist/runtime/session-streams.js +204 -0
  159. package/dist/runtime/tool-memory.d.ts +6 -0
  160. package/dist/runtime/tool-memory.js +22 -0
  161. package/dist/schemas/item.d.ts +35 -0
  162. package/dist/schemas/item.js +155 -0
  163. package/dist/types/channel.d.ts +29 -0
  164. package/dist/types/channel.js +1 -0
  165. package/dist/types/common.d.ts +111 -0
  166. package/dist/types/common.js +1 -0
  167. package/dist/types/context.d.ts +93 -0
  168. package/dist/types/context.js +1 -0
  169. package/dist/types/detached.d.ts +17 -0
  170. package/dist/types/detached.js +7 -0
  171. package/dist/types/embed.d.ts +5 -0
  172. package/dist/types/embed.js +1 -0
  173. package/dist/types/error.d.ts +51 -0
  174. package/dist/types/error.js +1 -0
  175. package/dist/types/fs-adapter.d.ts +51 -0
  176. package/dist/types/fs-adapter.js +3 -0
  177. package/dist/types/harness-result.d.ts +43 -0
  178. package/dist/types/harness-result.js +2 -0
  179. package/dist/types/items.d.ts +115 -0
  180. package/dist/types/items.js +2 -0
  181. package/dist/types/memory.d.ts +336 -0
  182. package/dist/types/memory.js +13 -0
  183. package/dist/types/mutable-context.d.ts +13 -0
  184. package/dist/types/mutable-context.js +1 -0
  185. package/dist/types/observability.d.ts +30 -0
  186. package/dist/types/observability.js +1 -0
  187. package/dist/types/runtime.d.ts +255 -0
  188. package/dist/types/runtime.js +1 -0
  189. package/dist/types/shell-adapter.d.ts +34 -0
  190. package/dist/types/shell-adapter.js +10 -0
  191. package/dist/types/steering.d.ts +89 -0
  192. package/dist/types/steering.js +14 -0
  193. package/dist/types/step.d.ts +206 -0
  194. package/dist/types/step.js +1 -0
  195. package/dist/types/subprocess-adapter.d.ts +41 -0
  196. package/dist/types/subprocess-adapter.js +1 -0
  197. package/dist/types/tool-context.d.ts +34 -0
  198. package/dist/types/tool-context.js +1 -0
  199. package/dist/unstable.d.ts +28 -0
  200. package/dist/unstable.js +24 -0
  201. package/dist/until/combinators.d.ts +17 -0
  202. package/dist/until/combinators.js +49 -0
  203. package/dist/until/never.d.ts +9 -0
  204. package/dist/until/never.js +12 -0
  205. package/dist/until/predicates.d.ts +43 -0
  206. package/dist/until/predicates.js +126 -0
  207. package/package.json +81 -0
@@ -0,0 +1,160 @@
1
+ import type { ItemSchemaRegistry } from '../schemas/item';
2
+ import type { LLMResponse } from '../types/common';
3
+ import type { ItemLog } from '../types/context';
4
+ import type { Item } from '../types/items';
5
+ import type { ExecutionContext, MemoryLayer, StorageAdapter } from '../types/memory';
6
+ import type { SteeringDecision } from '../types/steering';
7
+ export interface LayerStateStore {
8
+ get<T>(executionId: string, layerId: string): T | undefined;
9
+ set<T>(executionId: string, layerId: string, state: T): void;
10
+ cleanup(executionId: string): void;
11
+ diagnostic: (layerId: string, hook: string, error: unknown) => void;
12
+ }
13
+ interface InitLayersParams {
14
+ layers: MemoryLayer[];
15
+ ctx: ExecutionContext;
16
+ storage: StorageAdapter;
17
+ store: LayerStateStore;
18
+ }
19
+ interface RecallLayersParams {
20
+ layers: MemoryLayer[];
21
+ query: string;
22
+ ctx: ExecutionContext;
23
+ log: ItemLog;
24
+ budgets: Map<string, number>;
25
+ store: LayerStateStore;
26
+ itemSchemas?: ItemSchemaRegistry;
27
+ }
28
+ interface StoreLayersParams {
29
+ layers: MemoryLayer[];
30
+ response: LLMResponse;
31
+ ctx: ExecutionContext;
32
+ log: ItemLog;
33
+ store: LayerStateStore;
34
+ storage: StorageAdapter;
35
+ }
36
+ interface SpawnLayersParams {
37
+ layers: MemoryLayer[];
38
+ parentCtx: ExecutionContext;
39
+ childCtx: ExecutionContext;
40
+ store: LayerStateStore;
41
+ itemSchemas?: ItemSchemaRegistry;
42
+ }
43
+ interface ReturnLayersParams<T = unknown> {
44
+ layers: MemoryLayer[];
45
+ parentCtx: ExecutionContext;
46
+ childCtx: ExecutionContext;
47
+ childLog: ItemLog;
48
+ result: T;
49
+ store: LayerStateStore;
50
+ }
51
+ interface CompleteLayersParams {
52
+ layers: MemoryLayer[];
53
+ ctx: ExecutionContext;
54
+ log: ItemLog;
55
+ outcome: 'success' | 'failure' | 'aborted';
56
+ store: LayerStateStore;
57
+ }
58
+ interface DisposeLayersParams {
59
+ layers: MemoryLayer[];
60
+ ctx: ExecutionContext;
61
+ store: LayerStateStore;
62
+ }
63
+ interface BeforeToolCallLayersParams {
64
+ layers: MemoryLayer[];
65
+ toolName: string;
66
+ toolArgs: unknown;
67
+ ctx: ExecutionContext;
68
+ store: LayerStateStore;
69
+ }
70
+ interface AfterModelCallLayersParams {
71
+ layers: MemoryLayer[];
72
+ response: LLMResponse;
73
+ ctx: ExecutionContext;
74
+ store: LayerStateStore;
75
+ }
76
+ interface ProjectHistoryLayersParams {
77
+ layers: MemoryLayer[];
78
+ items: ReadonlyArray<Item>;
79
+ ctx: ExecutionContext;
80
+ store: LayerStateStore;
81
+ }
82
+ /** @public A request to re-render the context window, collected from onItemAppend hooks. */
83
+ export interface RerenderRequest {
84
+ layerId: string;
85
+ slot: number;
86
+ timing: 'immediate' | 'batched';
87
+ scope: 'self' | 'slot-after' | 'all';
88
+ }
89
+ /** @public Result of running items through the onItemAppend pipeline. */
90
+ export interface AppendPipelineResult {
91
+ /** Final items to append after all transformations. */
92
+ items: Item[];
93
+ /** Re-render requests collected from layers. */
94
+ rerenderRequests: RerenderRequest[];
95
+ }
96
+ interface RunAppendPipelineParams {
97
+ layers: MemoryLayer[];
98
+ items: Item[];
99
+ ctx: ExecutionContext;
100
+ log: ItemLog;
101
+ store: LayerStateStore;
102
+ }
103
+ interface ExecuteRerenderParams {
104
+ requests: RerenderRequest[];
105
+ layers: MemoryLayer[];
106
+ ctx: ExecutionContext;
107
+ log: ItemLog;
108
+ budgets: Map<string, number>;
109
+ store: LayerStateStore;
110
+ itemSchemas?: ItemSchemaRegistry;
111
+ /** Query for context-aware recall (e.g., last user message) */
112
+ query?: string;
113
+ /** Current re-render depth (for loop protection) */
114
+ depth?: number;
115
+ }
116
+ /** Resolve each layer's `BudgetConfig` to a concrete token budget for recall. */
117
+ export declare function resolveLayerBudgets(layers: ReadonlyArray<MemoryLayer>): Map<string, number>;
118
+ export declare function createLayerStateStore(diagnostic?: (layerId: string, hook: string, error: unknown) => void): LayerStateStore;
119
+ export declare function mostRestrictive(decisions: SteeringDecision[]): SteeringDecision;
120
+ export declare function initLayers({ layers, ctx, storage, store }: InitLayersParams): Promise<void>;
121
+ export declare function recallLayers({ layers, query, ctx, log, budgets, store, itemSchemas, }: RecallLayersParams): Promise<{
122
+ layerId: string;
123
+ items: Item[];
124
+ tokenCount: number;
125
+ }[]>;
126
+ export declare function storeLayers({ layers, response, ctx, log, store, storage, }: StoreLayersParams): Promise<void>;
127
+ export declare function disposeLayers({ layers, ctx, store }: DisposeLayersParams): Promise<void>;
128
+ export declare function spawnLayers({ layers, parentCtx, childCtx, store, itemSchemas, }: SpawnLayersParams): Promise<{
129
+ layerId: string;
130
+ childState: unknown;
131
+ items: Item[];
132
+ }[]>;
133
+ export declare function returnLayers<T>({ layers, parentCtx, childCtx, childLog, result, store, }: ReturnLayersParams<T>): Promise<T>;
134
+ export declare function completeLayers({ layers, ctx, log, outcome, store, }: CompleteLayersParams): Promise<void>;
135
+ export declare function beforeToolCallLayers({ layers, toolName, toolArgs, ctx, store, }: BeforeToolCallLayersParams): Promise<SteeringDecision>;
136
+ export declare function afterModelCallLayers({ layers, response, ctx, store, }: AfterModelCallLayersParams): Promise<SteeringDecision>;
137
+ /**
138
+ * Run history items through every layer's `projectHistory` hook in slot order.
139
+ * Each layer receives the previous layer's output. Storage (`itemLog`) is
140
+ * never mutated — this is a pure projection over the input array. Returns
141
+ * the projected items unchanged when no layer registers the hook.
142
+ */
143
+ export declare function projectHistoryLayers({ layers, items, ctx, store, }: ProjectHistoryLayersParams): Promise<ReadonlyArray<Item>>;
144
+ /**
145
+ * Run items through the onItemAppend pipeline.
146
+ * Each layer can filter, transform, or inject items.
147
+ * Returns final items to append and any re-render requests.
148
+ */
149
+ export declare function runAppendPipeline({ layers, items, ctx, log, store, }: RunAppendPipelineParams): Promise<AppendPipelineResult>;
150
+ /**
151
+ * Execute re-render based on collected requests.
152
+ * Determines which layers need re-recall based on scope and runs them.
153
+ * Returns new recall results to merge into view.
154
+ */
155
+ export declare function executeRerender({ requests, layers, ctx, log, budgets, store, itemSchemas, query, depth, }: ExecuteRerenderParams): Promise<{
156
+ layerId: string;
157
+ items: Item[];
158
+ tokenCount: number;
159
+ }[]>;
160
+ export {};
@@ -0,0 +1,552 @@
1
+ import { NoeticConfigError } from '../errors/noetic-config-error';
2
+ import { frameworkCast } from '../interpreter/framework-cast';
3
+ import { createMessage, estimateTokens } from '../interpreter/message-helpers';
4
+ import { defaultItemSchemaRegistry } from '../schemas/item';
5
+ import { SteeringAction } from '../types/steering';
6
+ import { createScopedStorage, resolveScopeKey } from './scope';
7
+ /** Maximum allowed re-render depth to prevent infinite loops */
8
+ const MAX_RERENDER_DEPTH = 3;
9
+ /**
10
+ * Default per-layer token budget when a layer has no `budget` config or uses
11
+ * `'auto'`. Big enough that budget-respecting layers (e.g. file-reference)
12
+ * render their content; real auto-allocation across layers is future work.
13
+ */
14
+ const DEFAULT_LAYER_BUDGET = 1.6e4;
15
+ /** Resolve each layer's `BudgetConfig` to a concrete token budget for recall. */
16
+ export function resolveLayerBudgets(layers) {
17
+ const budgets = new Map();
18
+ for (const layer of layers) {
19
+ const cfg = layer.budget;
20
+ if (typeof cfg === 'number') {
21
+ budgets.set(layer.id, cfg);
22
+ continue;
23
+ }
24
+ if (typeof cfg === 'object') {
25
+ budgets.set(layer.id, cfg.max);
26
+ continue;
27
+ }
28
+ // 'auto' or undefined → use the default ceiling.
29
+ budgets.set(layer.id, DEFAULT_LAYER_BUDGET);
30
+ }
31
+ return budgets;
32
+ }
33
+ //#endregion
34
+ //#region Helper Functions
35
+ export function createLayerStateStore(diagnostic) {
36
+ const states = new Map();
37
+ return {
38
+ get(executionId, layerId) {
39
+ return frameworkCast(states.get(executionId)?.get(layerId));
40
+ },
41
+ set(executionId, layerId, state) {
42
+ let execMap = states.get(executionId);
43
+ if (!execMap) {
44
+ execMap = new Map();
45
+ states.set(executionId, execMap);
46
+ }
47
+ execMap.set(layerId, state);
48
+ },
49
+ cleanup(executionId) {
50
+ states.delete(executionId);
51
+ },
52
+ diagnostic: diagnostic ?? (() => { }),
53
+ };
54
+ }
55
+ async function withTimeout(promise, ms) {
56
+ if (ms <= 0) {
57
+ return promise;
58
+ }
59
+ let timer;
60
+ return Promise.race([
61
+ promise.finally(() => clearTimeout(timer)),
62
+ new Promise((_, reject) => {
63
+ timer = setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms);
64
+ }),
65
+ ]);
66
+ }
67
+ export function mostRestrictive(decisions) {
68
+ let result = {
69
+ action: SteeringAction.Allow,
70
+ };
71
+ const guidances = [];
72
+ for (const d of decisions) {
73
+ if (d.action === SteeringAction.Deny) {
74
+ return d;
75
+ }
76
+ if (d.action === SteeringAction.Guide) {
77
+ result = d;
78
+ if (d.guidance) {
79
+ guidances.push(d.guidance);
80
+ }
81
+ }
82
+ }
83
+ if (result.action === SteeringAction.Guide && guidances.length > 1) {
84
+ return {
85
+ action: SteeringAction.Guide,
86
+ guidance: guidances.join('\n'),
87
+ };
88
+ }
89
+ return result;
90
+ }
91
+ function layerItemSchemas(layer, base = defaultItemSchemaRegistry) {
92
+ return base.extend(layer.itemSchemas);
93
+ }
94
+ //#endregion
95
+ //#region Public API
96
+ export async function initLayers({ layers, ctx, storage, store }) {
97
+ // Sequential, array order
98
+ for (const layer of layers) {
99
+ if (!layer.hooks.init) {
100
+ continue;
101
+ }
102
+ const scopeKey = resolveScopeKey(layer.scope, ctx);
103
+ const scopedStorage = createScopedStorage(storage, layer.id, scopeKey);
104
+ try {
105
+ const timeout = layer.timeouts?.init ?? 1e4;
106
+ const result = await withTimeout(layer.hooks.init({
107
+ storage: scopedStorage,
108
+ scopeKey,
109
+ ctx,
110
+ }), timeout);
111
+ store.set(ctx.executionId, layer.id, result.state);
112
+ }
113
+ catch (e) {
114
+ store.diagnostic(layer.id, 'init', e);
115
+ }
116
+ }
117
+ }
118
+ export async function recallLayers({ layers, query, ctx, log, budgets, store, itemSchemas = defaultItemSchemaRegistry, }) {
119
+ const sorted = [
120
+ ...layers,
121
+ ].sort((a, b) => a.slot - b.slot);
122
+ const results = [];
123
+ for (const layer of sorted) {
124
+ if (!layer.hooks.recall) {
125
+ continue;
126
+ }
127
+ const state = store.get(ctx.executionId, layer.id);
128
+ if (state === undefined && layer.hooks.init) {
129
+ continue; // was disabled
130
+ }
131
+ const budget = budgets.get(layer.id) ?? 0;
132
+ try {
133
+ const timeout = layer.timeouts?.recall ?? 5e3;
134
+ const result = await withTimeout(layer.hooks.recall({
135
+ log,
136
+ query,
137
+ ctx,
138
+ state,
139
+ budget,
140
+ }), timeout);
141
+ if (!result) {
142
+ continue;
143
+ }
144
+ const layerSchemas = layerItemSchemas(layer, itemSchemas);
145
+ if (typeof result === 'string') {
146
+ results.push({
147
+ layerId: layer.id,
148
+ items: [
149
+ layerSchemas.parseWithCategory(createMessage(result, 'developer'), 'developerMessages'),
150
+ ],
151
+ tokenCount: estimateTokens(result),
152
+ });
153
+ continue;
154
+ }
155
+ results.push({
156
+ layerId: layer.id,
157
+ items: layerSchemas.parseMany(result.items),
158
+ tokenCount: result.tokenCount,
159
+ });
160
+ if (result.state !== undefined) {
161
+ store.set(ctx.executionId, layer.id, result.state);
162
+ }
163
+ }
164
+ catch (e) {
165
+ store.diagnostic(layer.id, 'recall', e);
166
+ }
167
+ }
168
+ return results;
169
+ }
170
+ export async function storeLayers({ layers, response, ctx, log, store, storage, }) {
171
+ // Concurrent via Promise.allSettled — each layer gets its own state snapshot
172
+ const snapshots = [];
173
+ for (const layer of layers) {
174
+ if (!layer.hooks.store) {
175
+ continue;
176
+ }
177
+ const state = store.get(ctx.executionId, layer.id);
178
+ if (state === undefined && layer.hooks.init) {
179
+ continue;
180
+ }
181
+ snapshots.push({
182
+ layer,
183
+ state,
184
+ storeFn: layer.hooks.store,
185
+ });
186
+ }
187
+ await Promise.allSettled(snapshots.map(async ({ layer, state, storeFn }) => {
188
+ const timeout = layer.timeouts?.store ?? 3e4;
189
+ try {
190
+ const result = await withTimeout(storeFn({
191
+ newItems: response.items,
192
+ log,
193
+ response,
194
+ ctx,
195
+ state,
196
+ }), timeout);
197
+ if (result?.state !== undefined) {
198
+ store.set(ctx.executionId, layer.id, result.state);
199
+ // Mirror to durable storage so the next execution's init() can
200
+ // rehydrate. Skip 'execution' scope — its key rotates each run.
201
+ if (layer.scope !== 'execution') {
202
+ const scopedStorage = createScopedStorage(storage, layer.id, resolveScopeKey(layer.scope, ctx));
203
+ try {
204
+ await scopedStorage.set('state', result.state);
205
+ }
206
+ catch (persistErr) {
207
+ store.diagnostic(layer.id, 'store', persistErr);
208
+ }
209
+ }
210
+ }
211
+ }
212
+ catch (e) {
213
+ store.diagnostic(layer.id, 'store', e);
214
+ }
215
+ }));
216
+ }
217
+ export async function disposeLayers({ layers, ctx, store }) {
218
+ // Sequential, REVERSE array order
219
+ const reversed = [
220
+ ...layers,
221
+ ].reverse();
222
+ for (const layer of reversed) {
223
+ if (!layer.hooks.dispose) {
224
+ continue;
225
+ }
226
+ const state = store.get(ctx.executionId, layer.id);
227
+ try {
228
+ const timeout = layer.timeouts?.dispose ?? 5e3;
229
+ await withTimeout(layer.hooks.dispose({
230
+ state,
231
+ }), timeout);
232
+ }
233
+ catch (e) {
234
+ store.diagnostic(layer.id, 'dispose', e);
235
+ }
236
+ }
237
+ store.cleanup(ctx.executionId);
238
+ }
239
+ export async function spawnLayers({ layers, parentCtx, childCtx, store, itemSchemas = defaultItemSchemaRegistry, }) {
240
+ const results = [];
241
+ const sorted = [
242
+ ...layers,
243
+ ].sort((a, b) => a.slot - b.slot);
244
+ for (const layer of sorted) {
245
+ if (!layer.hooks.onSpawn) {
246
+ continue;
247
+ }
248
+ const parentState = store.get(parentCtx.executionId, layer.id);
249
+ if (parentState === undefined) {
250
+ continue;
251
+ }
252
+ try {
253
+ const timeout = layer.timeouts?.onSpawn ?? 1e4;
254
+ const result = await withTimeout(layer.hooks.onSpawn({
255
+ parentState,
256
+ childCtx,
257
+ }), timeout);
258
+ if (result && result.childState !== null) {
259
+ store.set(childCtx.executionId, layer.id, result.childState);
260
+ results.push({
261
+ layerId: layer.id,
262
+ childState: result.childState,
263
+ items: layerItemSchemas(layer, itemSchemas).parseMany(result.items ?? []),
264
+ });
265
+ }
266
+ }
267
+ catch (e) {
268
+ store.diagnostic(layer.id, 'onSpawn', e);
269
+ }
270
+ }
271
+ return results;
272
+ }
273
+ export async function returnLayers({ layers, parentCtx, childCtx, childLog, result, store, }) {
274
+ const sorted = [
275
+ ...layers,
276
+ ].sort((a, b) => a.slot - b.slot);
277
+ let currentResult = result;
278
+ for (const layer of sorted) {
279
+ if (!layer.hooks.onReturn) {
280
+ continue;
281
+ }
282
+ const childState = store.get(childCtx.executionId, layer.id);
283
+ const parentState = store.get(parentCtx.executionId, layer.id);
284
+ if (childState === undefined || parentState === undefined) {
285
+ continue;
286
+ }
287
+ try {
288
+ const timeout = layer.timeouts?.onReturn ?? 1e4;
289
+ const returnResult = await withTimeout(layer.hooks.onReturn({
290
+ childState,
291
+ childLog,
292
+ parentState,
293
+ result: currentResult,
294
+ }), timeout);
295
+ if (returnResult?.parentState !== undefined) {
296
+ store.set(parentCtx.executionId, layer.id, returnResult.parentState);
297
+ }
298
+ if (returnResult?.result !== undefined) {
299
+ currentResult = returnResult.result;
300
+ }
301
+ }
302
+ catch (e) {
303
+ store.diagnostic(layer.id, 'onReturn', e);
304
+ }
305
+ }
306
+ return frameworkCast(currentResult);
307
+ }
308
+ export async function completeLayers({ layers, ctx, log, outcome, store, }) {
309
+ for (const layer of layers) {
310
+ if (!layer.hooks.onComplete) {
311
+ continue;
312
+ }
313
+ const state = store.get(ctx.executionId, layer.id);
314
+ try {
315
+ const timeout = layer.timeouts?.onComplete ?? 3e4;
316
+ const result = await withTimeout(layer.hooks.onComplete({
317
+ log,
318
+ ctx,
319
+ state,
320
+ outcome,
321
+ }), timeout);
322
+ if (result && 'state' in result) {
323
+ store.set(ctx.executionId, layer.id, result.state);
324
+ }
325
+ }
326
+ catch (e) {
327
+ store.diagnostic(layer.id, 'onComplete', e);
328
+ }
329
+ }
330
+ }
331
+ export async function beforeToolCallLayers({ layers, toolName, toolArgs, ctx, store, }) {
332
+ const sorted = [
333
+ ...layers,
334
+ ].sort((a, b) => a.slot - b.slot);
335
+ const decisions = [];
336
+ for (const layer of sorted) {
337
+ if (!layer.hooks.beforeToolCall) {
338
+ continue;
339
+ }
340
+ const state = store.get(ctx.executionId, layer.id);
341
+ if (state === undefined && layer.hooks.init) {
342
+ continue;
343
+ }
344
+ try {
345
+ const timeout = layer.timeouts?.beforeToolCall ?? 5e3;
346
+ const result = await withTimeout(layer.hooks.beforeToolCall({
347
+ toolName,
348
+ toolArgs,
349
+ ctx,
350
+ state,
351
+ }), timeout);
352
+ if (result.state !== undefined) {
353
+ store.set(ctx.executionId, layer.id, result.state);
354
+ }
355
+ if (result.decision.action === SteeringAction.Deny) {
356
+ return result.decision;
357
+ }
358
+ decisions.push(result.decision);
359
+ }
360
+ catch (e) {
361
+ if (e instanceof NoeticConfigError) {
362
+ throw e;
363
+ }
364
+ store.diagnostic(layer.id, 'beforeToolCall', e);
365
+ }
366
+ }
367
+ return mostRestrictive(decisions);
368
+ }
369
+ export async function afterModelCallLayers({ layers, response, ctx, store, }) {
370
+ const sorted = [
371
+ ...layers,
372
+ ].sort((a, b) => a.slot - b.slot);
373
+ const decisions = [];
374
+ for (const layer of sorted) {
375
+ if (!layer.hooks.afterModelCall) {
376
+ continue;
377
+ }
378
+ const state = store.get(ctx.executionId, layer.id);
379
+ if (state === undefined && layer.hooks.init) {
380
+ continue;
381
+ }
382
+ try {
383
+ const timeout = layer.timeouts?.afterModelCall ?? 1e4;
384
+ const result = await withTimeout(layer.hooks.afterModelCall({
385
+ response,
386
+ ctx,
387
+ state,
388
+ }), timeout);
389
+ if (result.state !== undefined) {
390
+ store.set(ctx.executionId, layer.id, result.state);
391
+ }
392
+ if (result.decision.action === SteeringAction.Deny) {
393
+ return result.decision;
394
+ }
395
+ decisions.push(result.decision);
396
+ }
397
+ catch (e) {
398
+ if (e instanceof NoeticConfigError) {
399
+ throw e;
400
+ }
401
+ store.diagnostic(layer.id, 'afterModelCall', e);
402
+ }
403
+ }
404
+ return mostRestrictive(decisions);
405
+ }
406
+ /**
407
+ * Run history items through every layer's `projectHistory` hook in slot order.
408
+ * Each layer receives the previous layer's output. Storage (`itemLog`) is
409
+ * never mutated — this is a pure projection over the input array. Returns
410
+ * the projected items unchanged when no layer registers the hook.
411
+ */
412
+ export async function projectHistoryLayers({ layers, items, ctx, store, }) {
413
+ const sorted = [
414
+ ...layers,
415
+ ].sort((a, b) => a.slot - b.slot);
416
+ let current = items;
417
+ for (const layer of sorted) {
418
+ if (!layer.hooks.projectHistory) {
419
+ continue;
420
+ }
421
+ const state = store.get(ctx.executionId, layer.id);
422
+ if (state === undefined && layer.hooks.init) {
423
+ continue;
424
+ }
425
+ try {
426
+ const timeout = layer.timeouts?.projectHistory ?? 5e3;
427
+ const result = await withTimeout(layer.hooks.projectHistory({
428
+ items: current,
429
+ ctx,
430
+ state,
431
+ }), timeout);
432
+ current = result.items;
433
+ }
434
+ catch (e) {
435
+ store.diagnostic(layer.id, 'projectHistory', e);
436
+ }
437
+ }
438
+ return current;
439
+ }
440
+ const DEFAULT_ON_ITEM_APPEND_TIMEOUT = 5e3;
441
+ /**
442
+ * Run items through the onItemAppend pipeline.
443
+ * Each layer can filter, transform, or inject items.
444
+ * Returns final items to append and any re-render requests.
445
+ */
446
+ export async function runAppendPipeline({ layers, items, ctx, log, store, }) {
447
+ const requests = [];
448
+ let currentItems = items;
449
+ // Run pipeline in slot order
450
+ const sorted = [
451
+ ...layers,
452
+ ].sort((a, b) => a.slot - b.slot);
453
+ for (const layer of sorted) {
454
+ if (!layer.hooks.onItemAppend) {
455
+ continue;
456
+ }
457
+ // Skip if no items left (all filtered by previous layer)
458
+ if (currentItems.length === 0) {
459
+ break;
460
+ }
461
+ const state = store.get(ctx.executionId, layer.id);
462
+ // Skip if layer was disabled (has init but no state)
463
+ if (state === undefined && layer.hooks.init) {
464
+ continue;
465
+ }
466
+ try {
467
+ const timeout = layer.timeouts?.onItemAppend ?? DEFAULT_ON_ITEM_APPEND_TIMEOUT;
468
+ const result = await withTimeout(layer.hooks.onItemAppend({
469
+ items: currentItems,
470
+ log,
471
+ ctx,
472
+ state,
473
+ }), timeout);
474
+ // Update items for next layer in pipeline
475
+ currentItems = result.items;
476
+ // Update state if provided
477
+ if (result.state !== undefined) {
478
+ store.set(ctx.executionId, layer.id, result.state);
479
+ }
480
+ // Collect re-render request if present
481
+ if (result.rerender) {
482
+ requests.push({
483
+ layerId: layer.id,
484
+ slot: layer.slot,
485
+ timing: result.timing ?? layer.rerenderTiming ?? 'batched',
486
+ scope: result.scope ?? 'slot-after',
487
+ });
488
+ }
489
+ }
490
+ catch (e) {
491
+ store.diagnostic(layer.id, 'onItemAppend', e);
492
+ // On error, pass through items unchanged for this layer
493
+ }
494
+ }
495
+ return {
496
+ items: currentItems,
497
+ rerenderRequests: requests,
498
+ };
499
+ }
500
+ /**
501
+ * Execute re-render based on collected requests.
502
+ * Determines which layers need re-recall based on scope and runs them.
503
+ * Returns new recall results to merge into view.
504
+ */
505
+ export async function executeRerender({ requests, layers, ctx, log, budgets, store, itemSchemas, query = '', depth = 0, }) {
506
+ if (requests.length === 0) {
507
+ return [];
508
+ }
509
+ // Prevent infinite re-render loops
510
+ if (depth >= MAX_RERENDER_DEPTH) {
511
+ // Log warning and return empty - don't throw to avoid breaking the flow
512
+ console.warn(`[noetic] Re-render depth exceeded (${depth} >= ${MAX_RERENDER_DEPTH}). ` +
513
+ 'Possible infinite re-render loop detected. Stopping re-render cascade.');
514
+ return [];
515
+ }
516
+ // Determine which layers need re-recall based on scope
517
+ const layersToRecall = new Set();
518
+ for (const req of requests) {
519
+ switch (req.scope) {
520
+ case 'self':
521
+ layersToRecall.add(req.layerId);
522
+ break;
523
+ case 'slot-after':
524
+ for (const layer of layers) {
525
+ if (layer.slot >= req.slot) {
526
+ layersToRecall.add(layer.id);
527
+ }
528
+ }
529
+ break;
530
+ case 'all':
531
+ for (const layer of layers) {
532
+ layersToRecall.add(layer.id);
533
+ }
534
+ break;
535
+ }
536
+ }
537
+ // Re-run recall for affected layers
538
+ const affectedLayers = layers
539
+ .filter((l) => layersToRecall.has(l.id))
540
+ .sort((a, b) => a.slot - b.slot);
541
+ // Use existing recallLayers with filtered layers
542
+ return recallLayers({
543
+ layers: affectedLayers,
544
+ query,
545
+ ctx,
546
+ log,
547
+ budgets,
548
+ store,
549
+ itemSchemas,
550
+ });
551
+ }
552
+ //#endregion