@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,51 @@
1
+ import type { MemoryScope } from '../../types/memory';
2
+ import type { AfterModelCallParams, BeforeToolCallParams, LedgerEntry, SteeringConfig, SteeringDecision, SteeringState } from '../../types/steering';
3
+ /**
4
+ * Creates a steering memory layer that evaluates rules before tool calls and after model calls.
5
+ *
6
+ * @public
7
+ * @param config - Steering configuration including rules, scope, and max ledger size.
8
+ * @returns A `MemoryLayer` that enforces steering rules via allow/deny/guide decisions.
9
+ */
10
+ export declare function steering(config: SteeringConfig): {
11
+ id: "steering";
12
+ name: string;
13
+ slot: 90;
14
+ scope: MemoryScope;
15
+ budget: {
16
+ min: number;
17
+ max: number;
18
+ };
19
+ timeouts: {
20
+ beforeToolCall: number;
21
+ afterModelCall: number;
22
+ };
23
+ hooks: {
24
+ init({ storage }: import("../..").InitParams): Promise<{
25
+ state: SteeringState;
26
+ }>;
27
+ recall({ state }: import("../..").RecallParams<SteeringState>): Promise<{
28
+ items: import("../..").InputMessageItem[];
29
+ tokenCount: number;
30
+ state: SteeringState;
31
+ } | null>;
32
+ store(): Promise<undefined>;
33
+ beforeToolCall({ toolName, toolArgs, ctx, state }: BeforeToolCallParams<SteeringState>): Promise<{
34
+ decision: SteeringDecision;
35
+ state: SteeringState;
36
+ }>;
37
+ afterModelCall({ response, ctx, state }: AfterModelCallParams<SteeringState>): Promise<{
38
+ decision: SteeringDecision;
39
+ state: SteeringState;
40
+ }>;
41
+ onSpawn({ parentState }: import("../..").SpawnParams<SteeringState>): Promise<{
42
+ childState: {
43
+ ledger: LedgerEntry[];
44
+ pendingAsync: never[];
45
+ };
46
+ }>;
47
+ onComplete({ state, outcome }: import("../..").CompleteParams<SteeringState>): Promise<{
48
+ state: SteeringState;
49
+ }>;
50
+ };
51
+ };
@@ -0,0 +1,258 @@
1
+ import { NoeticConfigError } from '../../errors/noetic-config-error';
2
+ import { createMessage, estimateTokens, extractAssistantText, } from '../../interpreter/message-helpers';
3
+ import { Slot } from '../../types/memory';
4
+ import { LedgerEntryKind, SteeringAction } from '../../types/steering';
5
+ import { mostRestrictive } from '../layer-lifecycle';
6
+ //#region Constants
7
+ const DEFAULT_MAX_LEDGER_ENTRIES = 100;
8
+ //#endregion
9
+ //#region Rule Evaluation Helpers
10
+ function requireCallModel(params, ruleId) {
11
+ if (!params.ctx.callModel) {
12
+ throw new NoeticConfigError({
13
+ code: 'MISSING_CALL_MODEL',
14
+ message: `LLM-evaluated steering rule "${ruleId}" requires a callModel but none is available.`,
15
+ hint: 'Pass `llm: { provider: "openrouter", apiKey: "..." }` to AgentHarness or set OPENROUTER_API_KEY.',
16
+ });
17
+ }
18
+ }
19
+ function evaluateProgrammaticRule(rule, params) {
20
+ if (!rule.predicate) {
21
+ return null;
22
+ }
23
+ return rule.predicate(params);
24
+ }
25
+ async function evaluateLlmRuleSync(rule, params) {
26
+ if (!rule.llmEval) {
27
+ return {
28
+ action: SteeringAction.Allow,
29
+ };
30
+ }
31
+ const { callModel } = params.ctx;
32
+ if (!callModel) {
33
+ return {
34
+ action: SteeringAction.Allow,
35
+ };
36
+ }
37
+ const contextSummary = 'toolName' in params
38
+ ? `Tool: ${params.toolName}, Args: ${JSON.stringify(params.toolArgs)}`
39
+ : `Response items: ${params.response.items.length}`;
40
+ const prompt = `${rule.llmEval.prompt}\n\nContext: ${contextSummary}\n\nRespond with exactly "ALLOW", "DENY", or "GUIDE: <guidance text>".`;
41
+ const userMessage = createMessage(prompt, 'user');
42
+ const model = rule.llmEval.model ?? 'openai/gpt-4o-mini';
43
+ const response = await callModel({
44
+ model,
45
+ items: [
46
+ userMessage,
47
+ ],
48
+ });
49
+ const text = extractAssistantText(response.items).trim().toUpperCase();
50
+ if (text.startsWith('DENY')) {
51
+ return {
52
+ action: SteeringAction.Deny,
53
+ guidance: text.slice(5).trim() || undefined,
54
+ };
55
+ }
56
+ if (text.startsWith('GUIDE:')) {
57
+ return {
58
+ action: SteeringAction.Guide,
59
+ guidance: text.slice(6).trim(),
60
+ };
61
+ }
62
+ return {
63
+ action: SteeringAction.Allow,
64
+ };
65
+ }
66
+ function fireLlmRuleAsync({ rule, params, pendingAsync }) {
67
+ evaluateLlmRuleSync(rule, params)
68
+ .then((decision) => {
69
+ if (decision.action !== SteeringAction.Allow) {
70
+ pendingAsync.push({
71
+ ruleId: rule.id,
72
+ guidance: decision.guidance ?? `Rule ${rule.id}: ${decision.action}`,
73
+ });
74
+ }
75
+ })
76
+ .catch(() => {
77
+ // Async LLM eval failures are silently swallowed — they must not disrupt the agent
78
+ });
79
+ }
80
+ function trimLedger(ledger, max) {
81
+ if (ledger.length <= max) {
82
+ return ledger;
83
+ }
84
+ return ledger.slice(ledger.length - max);
85
+ }
86
+ async function evaluateRules({ rules, hookName, params, state, }) {
87
+ const applicable = rules.filter((r) => r.appliesTo.includes(hookName));
88
+ const decisions = [];
89
+ for (const rule of applicable) {
90
+ // Programmatic predicate (fast)
91
+ const programmatic = evaluateProgrammaticRule(rule, params);
92
+ if (programmatic) {
93
+ decisions.push(programmatic);
94
+ if (programmatic.action === SteeringAction.Deny) {
95
+ return mostRestrictive(decisions);
96
+ }
97
+ continue;
98
+ }
99
+ // LLM eval — throws NoeticConfigError if callModel is missing
100
+ if (!rule.llmEval) {
101
+ continue;
102
+ }
103
+ requireCallModel(params, rule.id);
104
+ if (rule.llmEval.mode === 'sync') {
105
+ const decision = await evaluateLlmRuleSync(rule, params);
106
+ decisions.push(decision);
107
+ if (decision.action === SteeringAction.Deny) {
108
+ return mostRestrictive(decisions);
109
+ }
110
+ continue;
111
+ }
112
+ // Async — fire and forget
113
+ fireLlmRuleAsync({
114
+ rule,
115
+ params,
116
+ pendingAsync: state.pendingAsync,
117
+ });
118
+ }
119
+ return mostRestrictive(decisions);
120
+ }
121
+ //#endregion
122
+ //#region Public API
123
+ /**
124
+ * Creates a steering memory layer that evaluates rules before tool calls and after model calls.
125
+ *
126
+ * @public
127
+ * @param config - Steering configuration including rules, scope, and max ledger size.
128
+ * @returns A `MemoryLayer` that enforces steering rules via allow/deny/guide decisions.
129
+ */
130
+ export function steering(config) {
131
+ const maxLedger = config.maxLedgerEntries ?? DEFAULT_MAX_LEDGER_ENTRIES;
132
+ const scope = config.scope ?? 'execution';
133
+ return {
134
+ id: 'steering',
135
+ name: 'Steering',
136
+ slot: Slot.STEERING,
137
+ scope,
138
+ budget: {
139
+ min: 0,
140
+ max: 500,
141
+ },
142
+ timeouts: {
143
+ beforeToolCall: 5e3,
144
+ afterModelCall: 1e4,
145
+ },
146
+ hooks: {
147
+ async init({ storage }) {
148
+ const saved = await storage.get('state');
149
+ const state = saved ?? {
150
+ ledger: [],
151
+ pendingAsync: [],
152
+ };
153
+ return {
154
+ state,
155
+ };
156
+ },
157
+ async recall({ state }) {
158
+ if (state.pendingAsync.length === 0) {
159
+ return null;
160
+ }
161
+ const feedback = state.pendingAsync.map((p) => `[${p.ruleId}] ${p.guidance}`).join('\n');
162
+ state.pendingAsync = [];
163
+ const content = `<steering_feedback>\n${feedback}\n</steering_feedback>`;
164
+ return {
165
+ items: [
166
+ createMessage(content, 'developer'),
167
+ ],
168
+ tokenCount: estimateTokens(content),
169
+ state,
170
+ };
171
+ },
172
+ async store() {
173
+ // Model turn ledger entries are recorded in afterModelCall to avoid duplicates
174
+ return undefined;
175
+ },
176
+ async beforeToolCall({ toolName, toolArgs, ctx, state }) {
177
+ const decision = await evaluateRules({
178
+ rules: config.rules,
179
+ hookName: 'beforeToolCall',
180
+ params: {
181
+ toolName,
182
+ toolArgs,
183
+ ctx,
184
+ state,
185
+ },
186
+ state,
187
+ });
188
+ const entry = {
189
+ kind: LedgerEntryKind.ToolCall,
190
+ timestamp: Date.now(),
191
+ toolName,
192
+ toolArgs,
193
+ ruleId: decision.action !== SteeringAction.Allow ? 'steering' : undefined,
194
+ action: decision.action,
195
+ guidance: decision.guidance,
196
+ };
197
+ state.ledger.push(entry);
198
+ state.ledger = trimLedger(state.ledger, maxLedger);
199
+ return {
200
+ decision,
201
+ state,
202
+ };
203
+ },
204
+ async afterModelCall({ response, ctx, state }) {
205
+ const decision = await evaluateRules({
206
+ rules: config.rules,
207
+ hookName: 'afterModelCall',
208
+ params: {
209
+ response,
210
+ ctx,
211
+ state,
212
+ },
213
+ state,
214
+ });
215
+ const entry = {
216
+ kind: LedgerEntryKind.ModelTurn,
217
+ timestamp: Date.now(),
218
+ tokenUsage: {
219
+ input: response.usage.inputTokens,
220
+ output: response.usage.outputTokens,
221
+ },
222
+ ruleId: decision.action !== SteeringAction.Allow ? 'steering' : undefined,
223
+ action: decision.action,
224
+ guidance: decision.guidance,
225
+ };
226
+ state.ledger.push(entry);
227
+ state.ledger = trimLedger(state.ledger, maxLedger);
228
+ return {
229
+ decision,
230
+ state,
231
+ };
232
+ },
233
+ async onSpawn({ parentState }) {
234
+ return {
235
+ childState: {
236
+ ledger: structuredClone(parentState.ledger),
237
+ pendingAsync: [],
238
+ },
239
+ };
240
+ },
241
+ async onComplete({ state, outcome }) {
242
+ const entry = {
243
+ kind: LedgerEntryKind.Custom,
244
+ timestamp: Date.now(),
245
+ custom: {
246
+ outcome,
247
+ },
248
+ };
249
+ state.ledger.push(entry);
250
+ state.ledger = trimLedger(state.ledger, maxLedger);
251
+ return {
252
+ state,
253
+ };
254
+ },
255
+ },
256
+ };
257
+ }
258
+ //#endregion
@@ -0,0 +1,16 @@
1
+ import type { Tool } from '../../types/common';
2
+ import type { MemoryLayer } from '../../types/memory';
3
+ interface ToolMemoryLayerOpts {
4
+ slot?: number;
5
+ }
6
+ /**
7
+ * Generates one MemoryLayer per unique memory id among the provided tools.
8
+ * Tools sharing the same `memory.id` share a single layer (and thus state).
9
+ *
10
+ * @public
11
+ * @param tools - Array of tools to extract memory declarations from.
12
+ * @param opts - Optional slot override for the generated layers.
13
+ * @returns An array of `MemoryLayer` instances, one per unique tool memory id.
14
+ */
15
+ export declare function toolMemoryLayer(tools: ReadonlyArray<Tool>, opts?: ToolMemoryLayerOpts): MemoryLayer[];
16
+ export {};
@@ -0,0 +1,43 @@
1
+ import { Slot } from '../../types/memory';
2
+ const EXECUTION_SCOPE = 'execution';
3
+ /**
4
+ * Generates one MemoryLayer per unique memory id among the provided tools.
5
+ * Tools sharing the same `memory.id` share a single layer (and thus state).
6
+ *
7
+ * @public
8
+ * @param tools - Array of tools to extract memory declarations from.
9
+ * @param opts - Optional slot override for the generated layers.
10
+ * @returns An array of `MemoryLayer` instances, one per unique tool memory id.
11
+ */
12
+ export function toolMemoryLayer(tools, opts) {
13
+ const seen = new Map();
14
+ for (const t of tools) {
15
+ if (!t.memory) {
16
+ continue;
17
+ }
18
+ const memId = t.memory.id ?? t.name;
19
+ if (seen.has(memId)) {
20
+ continue;
21
+ }
22
+ seen.set(memId, t.memory);
23
+ }
24
+ const slot = opts?.slot ?? Slot.WORKING_MEMORY + 10;
25
+ return [
26
+ ...seen.entries(),
27
+ ].map(([memId, decl]) => ({
28
+ id: memId,
29
+ name: memId,
30
+ slot,
31
+ scope: EXECUTION_SCOPE,
32
+ hooks: {
33
+ async init() {
34
+ return {
35
+ state: decl.init(),
36
+ };
37
+ },
38
+ async recall({ state }) {
39
+ return decl.recall(state);
40
+ },
41
+ },
42
+ }));
43
+ }
@@ -0,0 +1,44 @@
1
+ import type { ZodType } from 'zod';
2
+ export type WorkingMemoryState = string | Record<string, unknown>;
3
+ export interface WorkingMemoryConfig {
4
+ scope?: 'thread' | 'resource';
5
+ schema?: ZodType;
6
+ template?: string;
7
+ readOnly?: boolean;
8
+ }
9
+ /**
10
+ * Creates a mutable working memory layer that the model can update via the `working-memory/update` tool.
11
+ *
12
+ * @public
13
+ * @param config - Optional configuration for scope, Zod schema, template, and read-only mode.
14
+ * @returns A `MemoryLayer` providing scratchpad state the model can read and write.
15
+ */
16
+ export declare function workingMemory(config?: WorkingMemoryConfig): {
17
+ id: "working-memory";
18
+ name: string;
19
+ slot: 100;
20
+ scope: "thread" | "resource";
21
+ budget: {
22
+ min: number;
23
+ max: number;
24
+ };
25
+ provides: {
26
+ snapshot: import("../..").LayerDataDecl<WorkingMemoryState, WorkingMemoryState>;
27
+ update: import("../..").LayerFunctionDecl<Record<string, unknown>, void, WorkingMemoryState>;
28
+ };
29
+ hooks: {
30
+ init({ storage }: import("../..").InitParams): Promise<{
31
+ state: WorkingMemoryState;
32
+ }>;
33
+ recall({ state }: import("../..").RecallParams<WorkingMemoryState>): Promise<{
34
+ items: import("../..").InputMessageItem[];
35
+ tokenCount: number;
36
+ } | null>;
37
+ store({ newItems, state }: import("../..").StoreParams<WorkingMemoryState>): Promise<{
38
+ state: WorkingMemoryState;
39
+ } | undefined>;
40
+ onSpawn({ parentState }: import("../..").SpawnParams<WorkingMemoryState>): Promise<{
41
+ childState: WorkingMemoryState;
42
+ } | null>;
43
+ };
44
+ };
@@ -0,0 +1,92 @@
1
+ import { z } from 'zod';
2
+ import { layerData, layerFn } from '../../builders/layer-provides-builders';
3
+ import { createMessage, estimateTokens } from '../../interpreter/message-helpers';
4
+ import { Slot } from '../../types/memory';
5
+ import { findFunctionCall } from '../function-call-utils';
6
+ function safeMerge(state, args) {
7
+ const { __proto__: _p, constructor: _c, ...safeArgs } = args;
8
+ if (typeof state === 'object' && state !== null) {
9
+ return {
10
+ ...state,
11
+ ...safeArgs,
12
+ };
13
+ }
14
+ return safeArgs;
15
+ }
16
+ /**
17
+ * Creates a mutable working memory layer that the model can update via the `working-memory/update` tool.
18
+ *
19
+ * @public
20
+ * @param config - Optional configuration for scope, Zod schema, template, and read-only mode.
21
+ * @returns A `MemoryLayer` providing scratchpad state the model can read and write.
22
+ */
23
+ export function workingMemory(config) {
24
+ const scope = config?.scope ?? 'thread';
25
+ return {
26
+ id: 'working-memory',
27
+ name: 'Working Memory',
28
+ slot: Slot.WORKING_MEMORY,
29
+ scope,
30
+ budget: {
31
+ min: 200,
32
+ max: 1_500,
33
+ },
34
+ provides: {
35
+ snapshot: layerData({
36
+ read: (state) => state,
37
+ }),
38
+ update: layerFn({
39
+ description: 'Update the agent working memory with new key-value pairs.',
40
+ input: z.record(z.string(), z.unknown()),
41
+ output: z.void(),
42
+ execute: async (args, state) => ({
43
+ result: undefined,
44
+ state: safeMerge(state, args),
45
+ }),
46
+ }),
47
+ },
48
+ hooks: {
49
+ async init({ storage }) {
50
+ const saved = await storage.get('state');
51
+ const state = saved ?? (config?.schema ? {} : '');
52
+ return {
53
+ state,
54
+ };
55
+ },
56
+ async recall({ state }) {
57
+ if (!state || (typeof state === 'object' && Object.keys(state).length === 0)) {
58
+ return null;
59
+ }
60
+ const text = typeof state === 'string' ? state : JSON.stringify(state, null, 2);
61
+ const content = `<working_memory>\n${text}\n</working_memory>`;
62
+ return {
63
+ items: [
64
+ createMessage(content, 'developer'),
65
+ ],
66
+ tokenCount: estimateTokens(content),
67
+ };
68
+ },
69
+ async store({ newItems, state }) {
70
+ if (config?.readOnly) {
71
+ return;
72
+ }
73
+ // Backward compat: detect implicit updateWorkingMemory calls from LLMs
74
+ const args = findFunctionCall(newItems, 'updateWorkingMemory');
75
+ if (!args) {
76
+ return;
77
+ }
78
+ return {
79
+ state: safeMerge(state, args),
80
+ };
81
+ },
82
+ async onSpawn({ parentState }) {
83
+ if (scope === 'resource') {
84
+ return {
85
+ childState: structuredClone(parentState),
86
+ };
87
+ }
88
+ return null;
89
+ },
90
+ },
91
+ };
92
+ }
@@ -0,0 +1,10 @@
1
+ import type { Item } from '../types/items';
2
+ import type { ProjectionPolicy } from '../types/memory';
3
+ interface AssembleViewParams {
4
+ systemPromptItems: Item[];
5
+ layerOutputItems: Item[];
6
+ historyItems: Item[];
7
+ policy?: ProjectionPolicy;
8
+ }
9
+ export declare function assembleView({ systemPromptItems, layerOutputItems, historyItems, policy, }: AssembleViewParams): Item[];
10
+ export {};
@@ -0,0 +1,19 @@
1
+ //#endregion
2
+ //#region Public API
3
+ export function assembleView({ systemPromptItems, layerOutputItems, historyItems, policy, }) {
4
+ const view = [];
5
+ // Add system prompt items
6
+ view.push(...systemPromptItems);
7
+ // Add layer output items (sorted by slot already from recall)
8
+ view.push(...layerOutputItems);
9
+ // Add history items (with overflow handling if policy specified)
10
+ if (policy?.overflow === 'sliding_window' && policy.windowSize) {
11
+ const windowItems = historyItems.slice(-policy.windowSize);
12
+ view.push(...windowItems);
13
+ }
14
+ else {
15
+ view.push(...historyItems);
16
+ }
17
+ return view;
18
+ }
19
+ //#endregion
@@ -0,0 +1,3 @@
1
+ import type { ExecutionContext, MemoryScope, ScopedStorage, StorageAdapter } from '../types/memory';
2
+ export declare function resolveScopeKey(scope: MemoryScope, ctx: ExecutionContext): string;
3
+ export declare function createScopedStorage(storage: StorageAdapter, layerId: string, scopeKey: string): ScopedStorage;
@@ -0,0 +1,31 @@
1
+ export function resolveScopeKey(scope, ctx) {
2
+ switch (scope) {
3
+ case 'thread':
4
+ return ctx.threadId;
5
+ case 'resource':
6
+ return ctx.resourceId ?? ctx.threadId;
7
+ case 'global':
8
+ return '__global__';
9
+ case 'execution':
10
+ return ctx.executionId;
11
+ }
12
+ }
13
+ export function createScopedStorage(storage, layerId, scopeKey) {
14
+ const prefix = `layers/${layerId}/${scopeKey}/`;
15
+ return {
16
+ async get(key) {
17
+ return storage.get(`${prefix}${key}`);
18
+ },
19
+ async set(key, value) {
20
+ return storage.set(`${prefix}${key}`, value);
21
+ },
22
+ async delete(key) {
23
+ return storage.delete(`${prefix}${key}`);
24
+ },
25
+ async list(keyPrefix) {
26
+ const fullPrefix = keyPrefix ? `${prefix}${keyPrefix}` : prefix;
27
+ const keys = await storage.list(fullPrefix);
28
+ return keys.map((k) => (k.startsWith(prefix) ? k.slice(prefix.length) : k));
29
+ },
30
+ };
31
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Drop dangling tool-call state from an item list. A `function_call` without a
3
+ * matching `function_call_output` (or vice-versa) wedges the model on the next
4
+ * round (it typically fabricates an output), so we filter both sides of any
5
+ * incomplete pair. Order-preserving and pure — safe to run after a slice.
6
+ *
7
+ * Mirrors Claude Code's `filterUnresolvedToolUses`. Used by the CLI's
8
+ * session-resume path to drop dangling pairs left by a crash, and by the
9
+ * `historyWindow` memory layer to clean orphans at the slice boundary.
10
+ */
11
+ import type { Item } from '../types/items';
12
+ /** @public */
13
+ export declare function stripUnresolvedToolCalls(items: ReadonlyArray<Item>): Item[];
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Drop dangling tool-call state from an item list. A `function_call` without a
3
+ * matching `function_call_output` (or vice-versa) wedges the model on the next
4
+ * round (it typically fabricates an output), so we filter both sides of any
5
+ * incomplete pair. Order-preserving and pure — safe to run after a slice.
6
+ *
7
+ * Mirrors Claude Code's `filterUnresolvedToolUses`. Used by the CLI's
8
+ * session-resume path to drop dangling pairs left by a crash, and by the
9
+ * `historyWindow` memory layer to clean orphans at the slice boundary.
10
+ */
11
+ /** @public */
12
+ export function stripUnresolvedToolCalls(items) {
13
+ const outputCallIds = new Set();
14
+ const callCallIds = new Set();
15
+ for (const item of items) {
16
+ if (item.type === 'function_call_output') {
17
+ outputCallIds.add(item.callId);
18
+ continue;
19
+ }
20
+ if (item.type === 'function_call') {
21
+ callCallIds.add(item.callId);
22
+ }
23
+ }
24
+ const keep = [];
25
+ for (const item of items) {
26
+ if (item.type === 'function_call' && !outputCallIds.has(item.callId)) {
27
+ continue;
28
+ }
29
+ if (item.type === 'function_call_output' && !callCallIds.has(item.callId)) {
30
+ continue;
31
+ }
32
+ keep.push(item);
33
+ }
34
+ return keep;
35
+ }
@@ -0,0 +1,11 @@
1
+ export declare const GenAI: {
2
+ readonly SYSTEM: "gen_ai.system";
3
+ readonly REQUEST_MODEL: "gen_ai.request.model";
4
+ readonly USAGE_INPUT_TOKENS: "gen_ai.usage.input_tokens";
5
+ readonly USAGE_OUTPUT_TOKENS: "gen_ai.usage.output_tokens";
6
+ readonly COST: "gen_ai.cost";
7
+ };
8
+ export declare const ToolAttr: {
9
+ readonly NAME: "tool.name";
10
+ readonly NEEDS_APPROVAL: "tool.needs_approval";
11
+ };
@@ -0,0 +1,12 @@
1
+ // GenAI Semantic Convention attributes
2
+ export const GenAI = {
3
+ SYSTEM: 'gen_ai.system',
4
+ REQUEST_MODEL: 'gen_ai.request.model',
5
+ USAGE_INPUT_TOKENS: 'gen_ai.usage.input_tokens',
6
+ USAGE_OUTPUT_TOKENS: 'gen_ai.usage.output_tokens',
7
+ COST: 'gen_ai.cost',
8
+ };
9
+ export const ToolAttr = {
10
+ NAME: 'tool.name',
11
+ NEEDS_APPROVAL: 'tool.needs_approval',
12
+ };
@@ -0,0 +1,20 @@
1
+ import type { Span } from '../types/observability';
2
+ export declare class SpanImpl implements Span {
3
+ readonly traceId: string;
4
+ readonly spanId: string;
5
+ readonly parentSpanId: string | null;
6
+ readonly name: string;
7
+ readonly startTime: number;
8
+ endTime?: number;
9
+ readonly attributes: Map<string, string | number | boolean>;
10
+ readonly events: Array<{
11
+ name: string;
12
+ timestamp: number;
13
+ attributes?: Record<string, string | number | boolean>;
14
+ }>;
15
+ constructor(name: string, parent: Span | null, traceId?: string);
16
+ setAttribute(key: string, value: string | number | boolean): void;
17
+ addEvent(name: string, attributes?: Record<string, string | number | boolean>): void;
18
+ end(): void;
19
+ get duration(): number;
20
+ }