@mnemoai/core 1.1.0 → 1.1.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 (220) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +7 -0
  4. package/dist/cli.js.map +7 -0
  5. package/dist/index.d.ts +128 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/{index.ts → dist/index.js} +526 -1333
  8. package/dist/index.js.map +7 -0
  9. package/dist/src/access-tracker.d.ts +97 -0
  10. package/dist/src/access-tracker.d.ts.map +1 -0
  11. package/dist/src/access-tracker.js +184 -0
  12. package/dist/src/access-tracker.js.map +7 -0
  13. package/dist/src/adapters/chroma.d.ts +31 -0
  14. package/dist/src/adapters/chroma.d.ts.map +1 -0
  15. package/{src/adapters/chroma.ts → dist/src/adapters/chroma.js} +45 -107
  16. package/dist/src/adapters/chroma.js.map +7 -0
  17. package/dist/src/adapters/lancedb.d.ts +29 -0
  18. package/dist/src/adapters/lancedb.d.ts.map +1 -0
  19. package/{src/adapters/lancedb.ts → dist/src/adapters/lancedb.js} +41 -109
  20. package/dist/src/adapters/lancedb.js.map +7 -0
  21. package/dist/src/adapters/pgvector.d.ts +33 -0
  22. package/dist/src/adapters/pgvector.d.ts.map +1 -0
  23. package/{src/adapters/pgvector.ts → dist/src/adapters/pgvector.js} +42 -104
  24. package/dist/src/adapters/pgvector.js.map +7 -0
  25. package/dist/src/adapters/qdrant.d.ts +34 -0
  26. package/dist/src/adapters/qdrant.d.ts.map +1 -0
  27. package/dist/src/adapters/qdrant.js +132 -0
  28. package/dist/src/adapters/qdrant.js.map +7 -0
  29. package/dist/src/adaptive-retrieval.d.ts +14 -0
  30. package/dist/src/adaptive-retrieval.d.ts.map +1 -0
  31. package/dist/src/adaptive-retrieval.js +52 -0
  32. package/dist/src/adaptive-retrieval.js.map +7 -0
  33. package/dist/src/audit-log.d.ts +56 -0
  34. package/dist/src/audit-log.d.ts.map +1 -0
  35. package/dist/src/audit-log.js +139 -0
  36. package/dist/src/audit-log.js.map +7 -0
  37. package/dist/src/chunker.d.ts +45 -0
  38. package/dist/src/chunker.d.ts.map +1 -0
  39. package/dist/src/chunker.js +157 -0
  40. package/dist/src/chunker.js.map +7 -0
  41. package/dist/src/config.d.ts +70 -0
  42. package/dist/src/config.d.ts.map +1 -0
  43. package/dist/src/config.js +142 -0
  44. package/dist/src/config.js.map +7 -0
  45. package/dist/src/decay-engine.d.ts +73 -0
  46. package/dist/src/decay-engine.d.ts.map +1 -0
  47. package/dist/src/decay-engine.js +119 -0
  48. package/dist/src/decay-engine.js.map +7 -0
  49. package/dist/src/embedder.d.ts +94 -0
  50. package/dist/src/embedder.d.ts.map +1 -0
  51. package/{src/embedder.ts → dist/src/embedder.js} +119 -317
  52. package/dist/src/embedder.js.map +7 -0
  53. package/dist/src/extraction-prompts.d.ts +12 -0
  54. package/dist/src/extraction-prompts.d.ts.map +1 -0
  55. package/dist/src/extraction-prompts.js +311 -0
  56. package/dist/src/extraction-prompts.js.map +7 -0
  57. package/dist/src/license.d.ts +29 -0
  58. package/dist/src/license.d.ts.map +1 -0
  59. package/{src/license.ts → dist/src/license.js} +42 -113
  60. package/dist/src/license.js.map +7 -0
  61. package/dist/src/llm-client.d.ts +23 -0
  62. package/dist/src/llm-client.d.ts.map +1 -0
  63. package/{src/llm-client.ts → dist/src/llm-client.js} +22 -55
  64. package/dist/src/llm-client.js.map +7 -0
  65. package/dist/src/logger.d.ts +33 -0
  66. package/dist/src/logger.d.ts.map +1 -0
  67. package/dist/src/logger.js +35 -0
  68. package/dist/src/logger.js.map +7 -0
  69. package/dist/src/mcp-server.d.ts +16 -0
  70. package/dist/src/mcp-server.d.ts.map +1 -0
  71. package/{src/mcp-server.ts → dist/src/mcp-server.js} +81 -181
  72. package/dist/src/mcp-server.js.map +7 -0
  73. package/dist/src/memory-categories.d.ts +40 -0
  74. package/dist/src/memory-categories.d.ts.map +1 -0
  75. package/dist/src/memory-categories.js +33 -0
  76. package/dist/src/memory-categories.js.map +7 -0
  77. package/dist/src/memory-upgrader.d.ts +71 -0
  78. package/dist/src/memory-upgrader.d.ts.map +1 -0
  79. package/dist/src/memory-upgrader.js +238 -0
  80. package/dist/src/memory-upgrader.js.map +7 -0
  81. package/dist/src/migrate.d.ts +47 -0
  82. package/dist/src/migrate.d.ts.map +1 -0
  83. package/{src/migrate.ts → dist/src/migrate.js} +57 -165
  84. package/dist/src/migrate.js.map +7 -0
  85. package/dist/src/mnemo.d.ts +67 -0
  86. package/dist/src/mnemo.d.ts.map +1 -0
  87. package/dist/src/mnemo.js +66 -0
  88. package/dist/src/mnemo.js.map +7 -0
  89. package/dist/src/noise-filter.d.ts +23 -0
  90. package/dist/src/noise-filter.d.ts.map +1 -0
  91. package/dist/src/noise-filter.js +62 -0
  92. package/dist/src/noise-filter.js.map +7 -0
  93. package/dist/src/noise-prototypes.d.ts +40 -0
  94. package/dist/src/noise-prototypes.d.ts.map +1 -0
  95. package/dist/src/noise-prototypes.js +116 -0
  96. package/dist/src/noise-prototypes.js.map +7 -0
  97. package/dist/src/observability.d.ts +16 -0
  98. package/dist/src/observability.d.ts.map +1 -0
  99. package/dist/src/observability.js +53 -0
  100. package/dist/src/observability.js.map +7 -0
  101. package/dist/src/query-tracker.d.ts +27 -0
  102. package/dist/src/query-tracker.d.ts.map +1 -0
  103. package/dist/src/query-tracker.js +32 -0
  104. package/dist/src/query-tracker.js.map +7 -0
  105. package/dist/src/reflection-event-store.d.ts +44 -0
  106. package/dist/src/reflection-event-store.d.ts.map +1 -0
  107. package/dist/src/reflection-event-store.js +50 -0
  108. package/dist/src/reflection-event-store.js.map +7 -0
  109. package/dist/src/reflection-item-store.d.ts +58 -0
  110. package/dist/src/reflection-item-store.d.ts.map +1 -0
  111. package/dist/src/reflection-item-store.js +69 -0
  112. package/dist/src/reflection-item-store.js.map +7 -0
  113. package/dist/src/reflection-mapped-metadata.d.ts +47 -0
  114. package/dist/src/reflection-mapped-metadata.d.ts.map +1 -0
  115. package/dist/src/reflection-mapped-metadata.js +40 -0
  116. package/dist/src/reflection-mapped-metadata.js.map +7 -0
  117. package/dist/src/reflection-metadata.d.ts +11 -0
  118. package/dist/src/reflection-metadata.d.ts.map +1 -0
  119. package/dist/src/reflection-metadata.js +24 -0
  120. package/dist/src/reflection-metadata.js.map +7 -0
  121. package/dist/src/reflection-ranking.d.ts +13 -0
  122. package/dist/src/reflection-ranking.d.ts.map +1 -0
  123. package/{src/reflection-ranking.ts → dist/src/reflection-ranking.js} +12 -21
  124. package/dist/src/reflection-ranking.js.map +7 -0
  125. package/dist/src/reflection-retry.d.ts +30 -0
  126. package/dist/src/reflection-retry.d.ts.map +1 -0
  127. package/{src/reflection-retry.ts → dist/src/reflection-retry.js} +24 -64
  128. package/dist/src/reflection-retry.js.map +7 -0
  129. package/dist/src/reflection-slices.d.ts +42 -0
  130. package/dist/src/reflection-slices.d.ts.map +1 -0
  131. package/{src/reflection-slices.ts → dist/src/reflection-slices.js} +60 -136
  132. package/dist/src/reflection-slices.js.map +7 -0
  133. package/dist/src/reflection-store.d.ts +85 -0
  134. package/dist/src/reflection-store.d.ts.map +1 -0
  135. package/dist/src/reflection-store.js +407 -0
  136. package/dist/src/reflection-store.js.map +7 -0
  137. package/dist/src/resonance-state.d.ts +19 -0
  138. package/dist/src/resonance-state.d.ts.map +1 -0
  139. package/{src/resonance-state.ts → dist/src/resonance-state.js} +13 -42
  140. package/dist/src/resonance-state.js.map +7 -0
  141. package/dist/src/retriever.d.ts +228 -0
  142. package/dist/src/retriever.d.ts.map +1 -0
  143. package/dist/src/retriever.js +1006 -0
  144. package/dist/src/retriever.js.map +7 -0
  145. package/dist/src/scopes.d.ts +58 -0
  146. package/dist/src/scopes.d.ts.map +1 -0
  147. package/dist/src/scopes.js +252 -0
  148. package/dist/src/scopes.js.map +7 -0
  149. package/dist/src/self-improvement-files.d.ts +20 -0
  150. package/dist/src/self-improvement-files.d.ts.map +1 -0
  151. package/{src/self-improvement-files.ts → dist/src/self-improvement-files.js} +24 -49
  152. package/dist/src/self-improvement-files.js.map +7 -0
  153. package/dist/src/semantic-gate.d.ts +24 -0
  154. package/dist/src/semantic-gate.d.ts.map +1 -0
  155. package/dist/src/semantic-gate.js +86 -0
  156. package/dist/src/semantic-gate.js.map +7 -0
  157. package/dist/src/session-recovery.d.ts +9 -0
  158. package/dist/src/session-recovery.d.ts.map +1 -0
  159. package/{src/session-recovery.ts → dist/src/session-recovery.js} +40 -57
  160. package/dist/src/session-recovery.js.map +7 -0
  161. package/dist/src/smart-extractor.d.ts +107 -0
  162. package/dist/src/smart-extractor.d.ts.map +1 -0
  163. package/{src/smart-extractor.ts → dist/src/smart-extractor.js} +130 -383
  164. package/dist/src/smart-extractor.js.map +7 -0
  165. package/dist/src/smart-metadata.d.ts +103 -0
  166. package/dist/src/smart-metadata.d.ts.map +1 -0
  167. package/dist/src/smart-metadata.js +361 -0
  168. package/dist/src/smart-metadata.js.map +7 -0
  169. package/dist/src/storage-adapter.d.ts +102 -0
  170. package/dist/src/storage-adapter.d.ts.map +1 -0
  171. package/dist/src/storage-adapter.js +22 -0
  172. package/dist/src/storage-adapter.js.map +7 -0
  173. package/dist/src/store.d.ts +108 -0
  174. package/dist/src/store.d.ts.map +1 -0
  175. package/dist/src/store.js +939 -0
  176. package/dist/src/store.js.map +7 -0
  177. package/dist/src/tier-manager.d.ts +57 -0
  178. package/dist/src/tier-manager.d.ts.map +1 -0
  179. package/dist/src/tier-manager.js +80 -0
  180. package/dist/src/tier-manager.js.map +7 -0
  181. package/dist/src/tools.d.ts +43 -0
  182. package/dist/src/tools.d.ts.map +1 -0
  183. package/dist/src/tools.js +1075 -0
  184. package/dist/src/tools.js.map +7 -0
  185. package/dist/src/wal-recovery.d.ts +30 -0
  186. package/dist/src/wal-recovery.d.ts.map +1 -0
  187. package/{src/wal-recovery.ts → dist/src/wal-recovery.js} +26 -79
  188. package/dist/src/wal-recovery.js.map +7 -0
  189. package/package.json +21 -2
  190. package/openclaw.plugin.json +0 -815
  191. package/src/access-tracker.ts +0 -341
  192. package/src/adapters/README.md +0 -78
  193. package/src/adapters/qdrant.ts +0 -191
  194. package/src/adaptive-retrieval.ts +0 -90
  195. package/src/audit-log.ts +0 -238
  196. package/src/chunker.ts +0 -254
  197. package/src/config.ts +0 -271
  198. package/src/decay-engine.ts +0 -238
  199. package/src/extraction-prompts.ts +0 -339
  200. package/src/memory-categories.ts +0 -71
  201. package/src/memory-upgrader.ts +0 -388
  202. package/src/mnemo.ts +0 -142
  203. package/src/noise-filter.ts +0 -97
  204. package/src/noise-prototypes.ts +0 -164
  205. package/src/observability.ts +0 -81
  206. package/src/query-tracker.ts +0 -57
  207. package/src/reflection-event-store.ts +0 -98
  208. package/src/reflection-item-store.ts +0 -112
  209. package/src/reflection-mapped-metadata.ts +0 -84
  210. package/src/reflection-metadata.ts +0 -23
  211. package/src/reflection-store.ts +0 -602
  212. package/src/retriever.ts +0 -1510
  213. package/src/scopes.ts +0 -375
  214. package/src/semantic-gate.ts +0 -121
  215. package/src/smart-metadata.ts +0 -561
  216. package/src/storage-adapter.ts +0 -153
  217. package/src/store.ts +0 -1330
  218. package/src/tier-manager.ts +0 -189
  219. package/src/tools.ts +0 -1292
  220. package/test/core.test.mjs +0 -301
@@ -0,0 +1,1075 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { isNoise } from "./noise-filter.js";
6
+ import {
7
+ buildSmartMetadata,
8
+ parseSmartMetadata,
9
+ stringifySmartMetadata
10
+ } from "./smart-metadata.js";
11
+ import { appendSelfImprovementEntry, ensureSelfImprovementLearningFiles } from "./self-improvement-files.js";
12
+ import { getDisplayCategoryTag } from "./reflection-metadata.js";
13
+ import { log } from "./logger.js";
14
+ const MEMORY_CATEGORIES = [
15
+ "preference",
16
+ "fact",
17
+ "decision",
18
+ "entity",
19
+ "reflection",
20
+ "other"
21
+ ];
22
+ function stringEnum(values) {
23
+ return Type.Unsafe({
24
+ type: "string",
25
+ enum: [...values]
26
+ });
27
+ }
28
+ function resolveAgentId(runtimeAgentId, fallback) {
29
+ if (typeof runtimeAgentId === "string" && runtimeAgentId.trim().length > 0) return runtimeAgentId;
30
+ if (typeof fallback === "string" && fallback.trim().length > 0) return fallback;
31
+ return void 0;
32
+ }
33
+ function clampInt(value, min, max) {
34
+ if (!Number.isFinite(value)) return min;
35
+ return Math.min(max, Math.max(min, Math.floor(value)));
36
+ }
37
+ function clamp01(value, fallback = 0.7) {
38
+ if (!Number.isFinite(value)) return fallback;
39
+ return Math.min(1, Math.max(0, value));
40
+ }
41
+ function sanitizeMemoryForSerialization(results) {
42
+ return results.map((r) => ({
43
+ id: r.entry.id,
44
+ text: r.entry.text,
45
+ category: getDisplayCategoryTag(r.entry),
46
+ rawCategory: r.entry.category,
47
+ scope: r.entry.scope,
48
+ importance: r.entry.importance,
49
+ score: r.score,
50
+ sources: r.sources
51
+ }));
52
+ }
53
+ function parseAgentIdFromSessionKey(sessionKey) {
54
+ if (!sessionKey) return void 0;
55
+ const m = /^agent:([^:]+):/.exec(sessionKey);
56
+ return m?.[1];
57
+ }
58
+ function resolveRuntimeAgentId(staticAgentId, runtimeCtx) {
59
+ if (!runtimeCtx || typeof runtimeCtx !== "object") return staticAgentId;
60
+ const ctx = runtimeCtx;
61
+ const ctxAgentId = typeof ctx.agentId === "string" ? ctx.agentId : void 0;
62
+ const ctxSessionKey = typeof ctx.sessionKey === "string" ? ctx.sessionKey : void 0;
63
+ return ctxAgentId || parseAgentIdFromSessionKey(ctxSessionKey) || staticAgentId;
64
+ }
65
+ function resolveToolContext(base, runtimeCtx) {
66
+ return {
67
+ ...base,
68
+ agentId: resolveRuntimeAgentId(base.agentId, runtimeCtx)
69
+ };
70
+ }
71
+ async function sleep(ms) {
72
+ await new Promise((resolve) => setTimeout(resolve, ms));
73
+ }
74
+ async function retrieveWithRetry(retriever, params) {
75
+ let results = await retriever.retrieve(params);
76
+ if (results.length === 0) {
77
+ await sleep(75);
78
+ results = await retriever.retrieve(params);
79
+ }
80
+ return results;
81
+ }
82
+ function resolveWorkspaceDir(toolCtx, fallback) {
83
+ const runtime = toolCtx;
84
+ const runtimePath = typeof runtime?.workspaceDir === "string" ? runtime.workspaceDir.trim() : "";
85
+ if (runtimePath) return runtimePath;
86
+ if (fallback && fallback.trim()) return fallback;
87
+ return join(homedir(), ".openclaw", "workspace");
88
+ }
89
+ function escapeRegExp(input) {
90
+ return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
91
+ }
92
+ function registerSelfImprovementLogTool(api, context) {
93
+ api.registerTool(
94
+ (toolCtx) => ({
95
+ name: "self_improvement_log",
96
+ label: "Self-Improvement Log",
97
+ description: "Log structured learning/error entries into .learnings for governance and later distillation.",
98
+ parameters: Type.Object({
99
+ type: stringEnum(["learning", "error"]),
100
+ summary: Type.String({ description: "One-line summary" }),
101
+ details: Type.Optional(Type.String({ description: "Detailed context or error output" })),
102
+ suggestedAction: Type.Optional(Type.String({ description: "Concrete action to prevent recurrence" })),
103
+ category: Type.Optional(Type.String({ description: "learning category (correction/best_practice/knowledge_gap) when type=learning" })),
104
+ area: Type.Optional(Type.String({ description: "frontend|backend|infra|tests|docs|config or custom area" })),
105
+ priority: Type.Optional(Type.String({ description: "low|medium|high|critical" }))
106
+ }),
107
+ async execute(_toolCallId, params) {
108
+ const {
109
+ type,
110
+ summary,
111
+ details = "",
112
+ suggestedAction = "",
113
+ category = "best_practice",
114
+ area = "config",
115
+ priority = "medium"
116
+ } = params;
117
+ try {
118
+ const workspaceDir = resolveWorkspaceDir(toolCtx, context.workspaceDir);
119
+ const { id: entryId, filePath } = await appendSelfImprovementEntry({
120
+ baseDir: workspaceDir,
121
+ type,
122
+ summary,
123
+ details,
124
+ suggestedAction,
125
+ category,
126
+ area,
127
+ priority,
128
+ source: "mnemo/self_improvement_log"
129
+ });
130
+ const fileName = type === "learning" ? "LEARNINGS.md" : "ERRORS.md";
131
+ return {
132
+ content: [{ type: "text", text: `Logged ${type} entry ${entryId} to .learnings/${fileName}` }],
133
+ details: { action: "logged", type, id: entryId, filePath }
134
+ };
135
+ } catch (error) {
136
+ return {
137
+ content: [{ type: "text", text: `Failed to log self-improvement entry: ${error instanceof Error ? error.message : String(error)}` }],
138
+ details: { error: "self_improvement_log_failed", message: String(error) }
139
+ };
140
+ }
141
+ }
142
+ }),
143
+ { name: "self_improvement_log" }
144
+ );
145
+ }
146
+ function registerSelfImprovementExtractSkillTool(api, context) {
147
+ api.registerTool(
148
+ (toolCtx) => ({
149
+ name: "self_improvement_extract_skill",
150
+ label: "Extract Skill From Learning",
151
+ description: "Create a new skill scaffold from a learning entry and mark the source learning as promoted_to_skill.",
152
+ parameters: Type.Object({
153
+ learningId: Type.String({ description: "Learning ID like LRN-YYYYMMDD-001" }),
154
+ skillName: Type.String({ description: "Skill folder name, lowercase with hyphens" }),
155
+ sourceFile: Type.Optional(stringEnum(["LEARNINGS.md", "ERRORS.md"])),
156
+ outputDir: Type.Optional(Type.String({ description: "Relative output dir under workspace (default: skills)" }))
157
+ }),
158
+ async execute(_toolCallId, params) {
159
+ const { learningId, skillName, sourceFile = "LEARNINGS.md", outputDir = "skills" } = params;
160
+ try {
161
+ if (!/^(LRN|ERR)-\d{8}-\d{3}$/.test(learningId)) {
162
+ return {
163
+ content: [{ type: "text", text: "Invalid learningId format. Use LRN-YYYYMMDD-001 / ERR-..." }],
164
+ details: { error: "invalid_learning_id" }
165
+ };
166
+ }
167
+ if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(skillName)) {
168
+ return {
169
+ content: [{ type: "text", text: "Invalid skillName. Use lowercase letters, numbers, and hyphens only." }],
170
+ details: { error: "invalid_skill_name" }
171
+ };
172
+ }
173
+ const workspaceDir = resolveWorkspaceDir(toolCtx, context.workspaceDir);
174
+ await ensureSelfImprovementLearningFiles(workspaceDir);
175
+ const learningsPath = join(workspaceDir, ".learnings", sourceFile);
176
+ const learningBody = await readFile(learningsPath, "utf-8");
177
+ const escapedLearningId = escapeRegExp(learningId.trim());
178
+ const entryRegex = new RegExp(`## \\[${escapedLearningId}\\][\\s\\S]*?(?=\\n## \\[|$)`, "m");
179
+ const match = learningBody.match(entryRegex);
180
+ if (!match) {
181
+ return {
182
+ content: [{ type: "text", text: `Learning entry ${learningId} not found in .learnings/${sourceFile}` }],
183
+ details: { error: "learning_not_found", learningId, sourceFile }
184
+ };
185
+ }
186
+ const summaryMatch = match[0].match(/### Summary\n([\s\S]*?)\n###/m);
187
+ const summary = (summaryMatch?.[1] ?? "Summarize the source learning here.").trim();
188
+ const safeOutputDir = outputDir.replace(/\\/g, "/").split("/").filter((segment) => segment && segment !== "." && segment !== "..").join("/");
189
+ const skillDir = join(workspaceDir, safeOutputDir || "skills", skillName);
190
+ await mkdir(skillDir, { recursive: true });
191
+ const skillPath = join(skillDir, "SKILL.md");
192
+ const skillTitle = skillName.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
193
+ const skillContent = [
194
+ "---",
195
+ `name: ${skillName}`,
196
+ `description: "Extracted from learning ${learningId}. Replace with a concise description."`,
197
+ "---",
198
+ "",
199
+ `# ${skillTitle}`,
200
+ "",
201
+ "## Why",
202
+ summary,
203
+ "",
204
+ "## When To Use",
205
+ "- [TODO] Define trigger conditions",
206
+ "",
207
+ "## Steps",
208
+ "1. [TODO] Add repeatable workflow steps",
209
+ "2. [TODO] Add verification steps",
210
+ "",
211
+ "## Source Learning",
212
+ `- Learning ID: ${learningId}`,
213
+ `- Source File: .learnings/${sourceFile}`,
214
+ ""
215
+ ].join("\n");
216
+ await writeFile(skillPath, skillContent, "utf-8");
217
+ const promotedMarker = `**Status**: promoted_to_skill`;
218
+ const skillPathMarker = `- Skill-Path: ${safeOutputDir || "skills"}/${skillName}`;
219
+ let updatedEntry = match[0];
220
+ updatedEntry = updatedEntry.includes("**Status**:") ? updatedEntry.replace(/\*\*Status\*\*:\s*.+/m, promotedMarker) : `${updatedEntry.trimEnd()}
221
+ ${promotedMarker}
222
+ `;
223
+ if (!updatedEntry.includes("Skill-Path:")) {
224
+ updatedEntry = `${updatedEntry.trimEnd()}
225
+ ${skillPathMarker}
226
+ `;
227
+ }
228
+ const updatedLearningBody = learningBody.replace(match[0], updatedEntry);
229
+ await writeFile(learningsPath, updatedLearningBody, "utf-8");
230
+ return {
231
+ content: [{ type: "text", text: `Extracted skill scaffold to ${safeOutputDir || "skills"}/${skillName}/SKILL.md and updated ${learningId}.` }],
232
+ details: {
233
+ action: "skill_extracted",
234
+ learningId,
235
+ sourceFile,
236
+ skillPath: `${safeOutputDir || "skills"}/${skillName}/SKILL.md`
237
+ }
238
+ };
239
+ } catch (error) {
240
+ return {
241
+ content: [{ type: "text", text: `Failed to extract skill: ${error instanceof Error ? error.message : String(error)}` }],
242
+ details: { error: "self_improvement_extract_skill_failed", message: String(error) }
243
+ };
244
+ }
245
+ }
246
+ }),
247
+ { name: "self_improvement_extract_skill" }
248
+ );
249
+ }
250
+ function registerSelfImprovementReviewTool(api, context) {
251
+ api.registerTool(
252
+ (toolCtx) => ({
253
+ name: "self_improvement_review",
254
+ label: "Self-Improvement Review",
255
+ description: "Summarize governance backlog from .learnings files (pending/high-priority/promoted counts).",
256
+ parameters: Type.Object({}),
257
+ async execute() {
258
+ try {
259
+ const workspaceDir = resolveWorkspaceDir(toolCtx, context.workspaceDir);
260
+ await ensureSelfImprovementLearningFiles(workspaceDir);
261
+ const learningsDir = join(workspaceDir, ".learnings");
262
+ const files = ["LEARNINGS.md", "ERRORS.md"];
263
+ const stats = { pending: 0, high: 0, promoted: 0, total: 0 };
264
+ for (const f of files) {
265
+ const content = await readFile(join(learningsDir, f), "utf-8").catch(() => "");
266
+ stats.total += (content.match(/^## \[/gm) || []).length;
267
+ stats.pending += (content.match(/\*\*Status\*\*:\s*pending/gi) || []).length;
268
+ stats.high += (content.match(/\*\*Priority\*\*:\s*(high|critical)/gi) || []).length;
269
+ stats.promoted += (content.match(/\*\*Status\*\*:\s*promoted(_to_skill)?/gi) || []).length;
270
+ }
271
+ const text = [
272
+ "Self-Improvement Governance Snapshot:",
273
+ `- Total entries: ${stats.total}`,
274
+ `- Pending: ${stats.pending}`,
275
+ `- High/Critical: ${stats.high}`,
276
+ `- Promoted: ${stats.promoted}`,
277
+ "",
278
+ "Recommended loop:",
279
+ "1) Resolve high-priority pending entries",
280
+ "2) Distill reusable rules into AGENTS.md / SOUL.md / TOOLS.md",
281
+ "3) Extract repeatable patterns as skills"
282
+ ].join("\n");
283
+ return {
284
+ content: [{ type: "text", text }],
285
+ details: { action: "review", stats }
286
+ };
287
+ } catch (error) {
288
+ return {
289
+ content: [{ type: "text", text: `Failed to review self-improvement backlog: ${error instanceof Error ? error.message : String(error)}` }],
290
+ details: { error: "self_improvement_review_failed", message: String(error) }
291
+ };
292
+ }
293
+ }
294
+ }),
295
+ { name: "self_improvement_review" }
296
+ );
297
+ }
298
+ function registerMemoryRecallTool(api, context) {
299
+ api.registerTool(
300
+ (toolCtx) => {
301
+ const runtimeContext = resolveToolContext(context, toolCtx);
302
+ return {
303
+ name: "memory_recall",
304
+ label: "Memory Recall",
305
+ description: "Search through long-term memories using hybrid retrieval (vector + keyword search). Use when you need context about user preferences, past decisions, or previously discussed topics.",
306
+ parameters: Type.Object({
307
+ query: Type.String({
308
+ description: "Search query for finding relevant memories"
309
+ }),
310
+ limit: Type.Optional(
311
+ Type.Number({
312
+ description: "Max results to return (default: 5, max: 20)"
313
+ })
314
+ ),
315
+ scope: Type.Optional(
316
+ Type.String({
317
+ description: "Specific memory scope to search in (optional)"
318
+ })
319
+ ),
320
+ category: Type.Optional(stringEnum(MEMORY_CATEGORIES))
321
+ }),
322
+ async execute(_toolCallId, params) {
323
+ const {
324
+ query,
325
+ limit = 5,
326
+ scope,
327
+ category
328
+ } = params;
329
+ try {
330
+ const safeLimit = clampInt(limit, 1, 20);
331
+ const agentId = runtimeContext.agentId;
332
+ let scopeFilter = runtimeContext.scopeManager.getAccessibleScopes(agentId);
333
+ if (scope) {
334
+ if (runtimeContext.scopeManager.isAccessible(scope, agentId)) {
335
+ scopeFilter = [scope];
336
+ } else {
337
+ return {
338
+ content: [
339
+ { type: "text", text: `Access denied to scope: ${scope}` }
340
+ ],
341
+ details: {
342
+ error: "scope_access_denied",
343
+ requestedScope: scope
344
+ }
345
+ };
346
+ }
347
+ }
348
+ const results = await retrieveWithRetry(runtimeContext.retriever, {
349
+ query,
350
+ limit: safeLimit,
351
+ scopeFilter,
352
+ category,
353
+ source: "manual"
354
+ });
355
+ if (results.length === 0) {
356
+ return {
357
+ content: [{ type: "text", text: "No relevant memories found." }],
358
+ details: { count: 0, query, scopes: scopeFilter }
359
+ };
360
+ }
361
+ const now = Date.now();
362
+ await Promise.allSettled(
363
+ results.map((result) => {
364
+ const meta = parseSmartMetadata(result.entry.metadata, result.entry);
365
+ return runtimeContext.store.patchMetadata(
366
+ result.entry.id,
367
+ {
368
+ access_count: meta.access_count + 1,
369
+ last_accessed_at: now
370
+ },
371
+ scopeFilter
372
+ );
373
+ })
374
+ );
375
+ const text = results.map((r, i) => {
376
+ const categoryTag = getDisplayCategoryTag(r.entry);
377
+ return `${i + 1}. [${r.entry.id}] [${categoryTag}] ${r.entry.text}`;
378
+ }).join("\n");
379
+ return {
380
+ content: [
381
+ {
382
+ type: "text",
383
+ text: `Found ${results.length} memories:
384
+
385
+ ${text}`
386
+ }
387
+ ],
388
+ details: {
389
+ count: results.length,
390
+ memories: sanitizeMemoryForSerialization(results),
391
+ query,
392
+ scopes: scopeFilter,
393
+ retrievalMode: runtimeContext.retriever.getConfig().mode
394
+ }
395
+ };
396
+ } catch (error) {
397
+ return {
398
+ content: [
399
+ {
400
+ type: "text",
401
+ text: `Memory recall failed: ${error instanceof Error ? error.message : String(error)}`
402
+ }
403
+ ],
404
+ details: { error: "recall_failed", message: String(error) }
405
+ };
406
+ }
407
+ }
408
+ };
409
+ },
410
+ { name: "memory_recall" }
411
+ );
412
+ }
413
+ function registerMemoryStoreTool(api, context) {
414
+ api.registerTool(
415
+ (toolCtx) => {
416
+ const runtimeContext = resolveToolContext(context, toolCtx);
417
+ return {
418
+ name: "memory_store",
419
+ label: "Memory Store",
420
+ description: "Save important information in long-term memory. Use for preferences, facts, decisions, and other notable information.",
421
+ parameters: Type.Object({
422
+ text: Type.String({ description: "Information to remember" }),
423
+ importance: Type.Optional(
424
+ Type.Number({ description: "Importance score 0-1 (default: 0.7)" })
425
+ ),
426
+ category: Type.Optional(stringEnum(MEMORY_CATEGORIES)),
427
+ scope: Type.Optional(
428
+ Type.String({
429
+ description: "Memory scope (optional, defaults to agent scope)"
430
+ })
431
+ )
432
+ }),
433
+ async execute(_toolCallId, params) {
434
+ const {
435
+ text,
436
+ importance = 0.7,
437
+ category = "other",
438
+ scope
439
+ } = params;
440
+ try {
441
+ const agentId = runtimeContext.agentId;
442
+ let targetScope = scope || runtimeContext.scopeManager.getDefaultScope(agentId);
443
+ if (!runtimeContext.scopeManager.isAccessible(targetScope, agentId)) {
444
+ return {
445
+ content: [
446
+ {
447
+ type: "text",
448
+ text: `Access denied to scope: ${targetScope}`
449
+ }
450
+ ],
451
+ details: {
452
+ error: "scope_access_denied",
453
+ requestedScope: targetScope
454
+ }
455
+ };
456
+ }
457
+ if (isNoise(text)) {
458
+ return {
459
+ content: [
460
+ {
461
+ type: "text",
462
+ text: `Skipped: text detected as noise (greeting, boilerplate, or meta-question)`
463
+ }
464
+ ],
465
+ details: { action: "noise_filtered", text: text.slice(0, 60) }
466
+ };
467
+ }
468
+ const safeImportance = clamp01(importance, 0.7);
469
+ const vector = await runtimeContext.embedder.embedPassage(text);
470
+ let existing = [];
471
+ try {
472
+ existing = await runtimeContext.store.vectorSearch(vector, 1, 0.1, [
473
+ targetScope
474
+ ]);
475
+ } catch (err) {
476
+ log.warn(
477
+ `duplicate pre-check failed, continue store: ${String(err)}`
478
+ );
479
+ }
480
+ if (existing.length > 0 && existing[0].score > 0.98) {
481
+ return {
482
+ content: [
483
+ {
484
+ type: "text",
485
+ text: `Similar memory already exists: "${existing[0].entry.text}"`
486
+ }
487
+ ],
488
+ details: {
489
+ action: "duplicate",
490
+ existingId: existing[0].entry.id,
491
+ existingText: existing[0].entry.text,
492
+ existingScope: existing[0].entry.scope,
493
+ similarity: existing[0].score
494
+ }
495
+ };
496
+ }
497
+ const entry = await runtimeContext.store.store({
498
+ text,
499
+ vector,
500
+ importance: safeImportance,
501
+ category,
502
+ scope: targetScope,
503
+ metadata: stringifySmartMetadata(
504
+ buildSmartMetadata(
505
+ {
506
+ text,
507
+ category,
508
+ importance: safeImportance
509
+ },
510
+ {
511
+ l0_abstract: text,
512
+ l1_overview: `- ${text}`,
513
+ l2_content: text
514
+ }
515
+ )
516
+ )
517
+ });
518
+ if (context.mdMirror) {
519
+ await context.mdMirror(
520
+ { text, category, scope: targetScope, timestamp: entry.timestamp },
521
+ { source: "memory_store", agentId }
522
+ );
523
+ }
524
+ return {
525
+ content: [
526
+ {
527
+ type: "text",
528
+ text: `Stored: "${text.slice(0, 100)}${text.length > 100 ? "..." : ""}" in scope '${targetScope}'`
529
+ }
530
+ ],
531
+ details: {
532
+ action: "created",
533
+ id: entry.id,
534
+ scope: entry.scope,
535
+ category: entry.category,
536
+ importance: entry.importance
537
+ }
538
+ };
539
+ } catch (error) {
540
+ return {
541
+ content: [
542
+ {
543
+ type: "text",
544
+ text: `Memory storage failed: ${error instanceof Error ? error.message : String(error)}`
545
+ }
546
+ ],
547
+ details: { error: "store_failed", message: String(error) }
548
+ };
549
+ }
550
+ }
551
+ };
552
+ },
553
+ { name: "memory_store" }
554
+ );
555
+ }
556
+ function registerMemoryForgetTool(api, context) {
557
+ api.registerTool(
558
+ (toolCtx) => {
559
+ const agentId = resolveAgentId(toolCtx?.agentId, context.agentId) ?? "main";
560
+ return {
561
+ name: "memory_forget",
562
+ label: "Memory Forget",
563
+ description: "Delete specific memories. Supports both search-based and direct ID-based deletion.",
564
+ parameters: Type.Object({
565
+ query: Type.Optional(
566
+ Type.String({ description: "Search query to find memory to delete" })
567
+ ),
568
+ memoryId: Type.Optional(
569
+ Type.String({ description: "Specific memory ID to delete" })
570
+ ),
571
+ scope: Type.Optional(
572
+ Type.String({
573
+ description: "Scope to search/delete from (optional)"
574
+ })
575
+ )
576
+ }),
577
+ async execute(_toolCallId, params, _signal, _onUpdate, runtimeCtx) {
578
+ const { query, memoryId, scope } = params;
579
+ try {
580
+ const agentId2 = resolveRuntimeAgentId(context.agentId, runtimeCtx);
581
+ let scopeFilter = context.scopeManager.getAccessibleScopes(agentId2);
582
+ if (scope) {
583
+ if (context.scopeManager.isAccessible(scope, agentId2)) {
584
+ scopeFilter = [scope];
585
+ } else {
586
+ return {
587
+ content: [
588
+ { type: "text", text: `Access denied to scope: ${scope}` }
589
+ ],
590
+ details: {
591
+ error: "scope_access_denied",
592
+ requestedScope: scope
593
+ }
594
+ };
595
+ }
596
+ }
597
+ if (memoryId) {
598
+ const deleted = await context.store.delete(memoryId, scopeFilter);
599
+ if (deleted) {
600
+ return {
601
+ content: [
602
+ { type: "text", text: `Memory ${memoryId} forgotten.` }
603
+ ],
604
+ details: { action: "deleted", id: memoryId }
605
+ };
606
+ } else {
607
+ return {
608
+ content: [
609
+ {
610
+ type: "text",
611
+ text: `Memory ${memoryId} not found or access denied.`
612
+ }
613
+ ],
614
+ details: { error: "not_found", id: memoryId }
615
+ };
616
+ }
617
+ }
618
+ if (query) {
619
+ const results = await retrieveWithRetry(context.retriever, {
620
+ query,
621
+ limit: 5,
622
+ scopeFilter
623
+ });
624
+ if (results.length === 0) {
625
+ return {
626
+ content: [
627
+ { type: "text", text: "No matching memories found." }
628
+ ],
629
+ details: { found: 0, query }
630
+ };
631
+ }
632
+ if (results.length === 1 && results[0].score > 0.9) {
633
+ const deleted = await context.store.delete(
634
+ results[0].entry.id,
635
+ scopeFilter
636
+ );
637
+ if (deleted) {
638
+ return {
639
+ content: [
640
+ {
641
+ type: "text",
642
+ text: `Forgotten: "${results[0].entry.text}"`
643
+ }
644
+ ],
645
+ details: { action: "deleted", id: results[0].entry.id }
646
+ };
647
+ }
648
+ }
649
+ const list = results.map(
650
+ (r) => `- [${r.entry.id.slice(0, 8)}] ${r.entry.text.slice(0, 60)}${r.entry.text.length > 60 ? "..." : ""}`
651
+ ).join("\n");
652
+ return {
653
+ content: [
654
+ {
655
+ type: "text",
656
+ text: `Found ${results.length} candidates. Specify memoryId to delete:
657
+ ${list}`
658
+ }
659
+ ],
660
+ details: {
661
+ action: "candidates",
662
+ candidates: sanitizeMemoryForSerialization(results)
663
+ }
664
+ };
665
+ }
666
+ return {
667
+ content: [
668
+ {
669
+ type: "text",
670
+ text: "Provide either 'query' to search for memories or 'memoryId' to delete specific memory."
671
+ }
672
+ ],
673
+ details: { error: "missing_param" }
674
+ };
675
+ } catch (error) {
676
+ return {
677
+ content: [
678
+ {
679
+ type: "text",
680
+ text: `Memory deletion failed: ${error instanceof Error ? error.message : String(error)}`
681
+ }
682
+ ],
683
+ details: { error: "delete_failed", message: String(error) }
684
+ };
685
+ }
686
+ }
687
+ };
688
+ },
689
+ { name: "memory_forget" }
690
+ );
691
+ }
692
+ function registerMemoryUpdateTool(api, context) {
693
+ api.registerTool(
694
+ (toolCtx) => {
695
+ const agentId = resolveAgentId(toolCtx?.agentId, context.agentId) ?? "main";
696
+ return {
697
+ name: "memory_update",
698
+ label: "Memory Update",
699
+ description: "Update an existing memory in-place. Preserves original timestamp. Use when correcting outdated info or adjusting importance/category without losing creation date.",
700
+ parameters: Type.Object({
701
+ memoryId: Type.String({
702
+ description: "ID of the memory to update (full UUID or 8+ char prefix)"
703
+ }),
704
+ text: Type.Optional(
705
+ Type.String({
706
+ description: "New text content (triggers re-embedding)"
707
+ })
708
+ ),
709
+ importance: Type.Optional(
710
+ Type.Number({ description: "New importance score 0-1" })
711
+ ),
712
+ category: Type.Optional(stringEnum(MEMORY_CATEGORIES))
713
+ }),
714
+ async execute(_toolCallId, params, _signal, _onUpdate, runtimeCtx) {
715
+ const { memoryId, text, importance, category } = params;
716
+ try {
717
+ if (!text && importance === void 0 && !category) {
718
+ return {
719
+ content: [
720
+ {
721
+ type: "text",
722
+ text: "Nothing to update. Provide at least one of: text, importance, category."
723
+ }
724
+ ],
725
+ details: { error: "no_updates" }
726
+ };
727
+ }
728
+ const agentId2 = resolveRuntimeAgentId(context.agentId, runtimeCtx);
729
+ const scopeFilter = context.scopeManager.getAccessibleScopes(agentId2);
730
+ let resolvedId = memoryId;
731
+ const uuidLike = /^[0-9a-f]{8}(-[0-9a-f]{4}){0,4}/i.test(memoryId);
732
+ if (!uuidLike) {
733
+ const results = await retrieveWithRetry(context.retriever, {
734
+ query: memoryId,
735
+ limit: 3,
736
+ scopeFilter
737
+ });
738
+ if (results.length === 0) {
739
+ return {
740
+ content: [
741
+ {
742
+ type: "text",
743
+ text: `No memory found matching "${memoryId}".`
744
+ }
745
+ ],
746
+ details: { error: "not_found", query: memoryId }
747
+ };
748
+ }
749
+ if (results.length === 1 || results[0].score > 0.85) {
750
+ resolvedId = results[0].entry.id;
751
+ } else {
752
+ const list = results.map(
753
+ (r) => `- [${r.entry.id.slice(0, 8)}] ${r.entry.text.slice(0, 60)}${r.entry.text.length > 60 ? "..." : ""}`
754
+ ).join("\n");
755
+ return {
756
+ content: [
757
+ {
758
+ type: "text",
759
+ text: `Multiple matches. Specify memoryId:
760
+ ${list}`
761
+ }
762
+ ],
763
+ details: {
764
+ action: "candidates",
765
+ candidates: sanitizeMemoryForSerialization(results)
766
+ }
767
+ };
768
+ }
769
+ }
770
+ let newVector;
771
+ if (text) {
772
+ if (isNoise(text)) {
773
+ return {
774
+ content: [
775
+ {
776
+ type: "text",
777
+ text: "Skipped: updated text detected as noise"
778
+ }
779
+ ],
780
+ details: { action: "noise_filtered" }
781
+ };
782
+ }
783
+ newVector = await context.embedder.embedPassage(text);
784
+ }
785
+ const updates = {};
786
+ if (text) updates.text = text;
787
+ if (newVector) updates.vector = newVector;
788
+ if (importance !== void 0)
789
+ updates.importance = clamp01(importance, 0.7);
790
+ if (category) updates.category = category;
791
+ const updated = await context.store.update(
792
+ resolvedId,
793
+ updates,
794
+ scopeFilter
795
+ );
796
+ if (!updated) {
797
+ return {
798
+ content: [
799
+ {
800
+ type: "text",
801
+ text: `Memory ${resolvedId.slice(0, 8)}... not found or access denied.`
802
+ }
803
+ ],
804
+ details: { error: "not_found", id: resolvedId }
805
+ };
806
+ }
807
+ return {
808
+ content: [
809
+ {
810
+ type: "text",
811
+ text: `Updated memory ${updated.id.slice(0, 8)}...: "${updated.text.slice(0, 80)}${updated.text.length > 80 ? "..." : ""}"`
812
+ }
813
+ ],
814
+ details: {
815
+ action: "updated",
816
+ id: updated.id,
817
+ scope: updated.scope,
818
+ category: updated.category,
819
+ importance: updated.importance,
820
+ fieldsUpdated: Object.keys(updates)
821
+ }
822
+ };
823
+ } catch (error) {
824
+ return {
825
+ content: [
826
+ {
827
+ type: "text",
828
+ text: `Memory update failed: ${error instanceof Error ? error.message : String(error)}`
829
+ }
830
+ ],
831
+ details: { error: "update_failed", message: String(error) }
832
+ };
833
+ }
834
+ }
835
+ };
836
+ },
837
+ { name: "memory_update" }
838
+ );
839
+ }
840
+ function registerMemoryStatsTool(api, context) {
841
+ api.registerTool(
842
+ (toolCtx) => {
843
+ const agentId = resolveAgentId(toolCtx?.agentId, context.agentId) ?? "main";
844
+ return {
845
+ name: "memory_stats",
846
+ label: "Memory Statistics",
847
+ description: "Get statistics about memory usage, scopes, and categories.",
848
+ parameters: Type.Object({
849
+ scope: Type.Optional(
850
+ Type.String({
851
+ description: "Specific scope to get stats for (optional)"
852
+ })
853
+ )
854
+ }),
855
+ async execute(_toolCallId, params, _signal, _onUpdate, runtimeCtx) {
856
+ const { scope } = params;
857
+ try {
858
+ const agentId2 = resolveRuntimeAgentId(context.agentId, runtimeCtx);
859
+ let scopeFilter = context.scopeManager.getAccessibleScopes(agentId2);
860
+ if (scope) {
861
+ if (context.scopeManager.isAccessible(scope, agentId2)) {
862
+ scopeFilter = [scope];
863
+ } else {
864
+ return {
865
+ content: [
866
+ { type: "text", text: `Access denied to scope: ${scope}` }
867
+ ],
868
+ details: {
869
+ error: "scope_access_denied",
870
+ requestedScope: scope
871
+ }
872
+ };
873
+ }
874
+ }
875
+ const stats = await context.store.stats(scopeFilter);
876
+ const scopeManagerStats = context.scopeManager.getStats();
877
+ const retrievalConfig = context.retriever.getConfig();
878
+ const text = [
879
+ `Memory Statistics:`,
880
+ `\u2022 Total memories: ${stats.totalCount}`,
881
+ `\u2022 Available scopes: ${scopeManagerStats.totalScopes}`,
882
+ `\u2022 Retrieval mode: ${retrievalConfig.mode}`,
883
+ `\u2022 FTS support: ${context.store.hasFtsSupport ? "Yes" : "No"}`,
884
+ ``,
885
+ `Memories by scope:`,
886
+ ...Object.entries(stats.scopeCounts).map(
887
+ ([s, count]) => ` \u2022 ${s}: ${count}`
888
+ ),
889
+ ``,
890
+ `Memories by category:`,
891
+ ...Object.entries(stats.categoryCounts).map(
892
+ ([c, count]) => ` \u2022 ${c}: ${count}`
893
+ )
894
+ ].join("\n");
895
+ return {
896
+ content: [{ type: "text", text }],
897
+ details: {
898
+ stats,
899
+ scopeManagerStats,
900
+ retrievalConfig: {
901
+ ...retrievalConfig,
902
+ rerankApiKey: retrievalConfig.rerankApiKey ? "***" : void 0
903
+ },
904
+ hasFtsSupport: context.store.hasFtsSupport
905
+ }
906
+ };
907
+ } catch (error) {
908
+ return {
909
+ content: [
910
+ {
911
+ type: "text",
912
+ text: `Failed to get memory stats: ${error instanceof Error ? error.message : String(error)}`
913
+ }
914
+ ],
915
+ details: { error: "stats_failed", message: String(error) }
916
+ };
917
+ }
918
+ }
919
+ };
920
+ },
921
+ { name: "memory_stats" }
922
+ );
923
+ }
924
+ function registerMemoryListTool(api, context) {
925
+ api.registerTool(
926
+ (toolCtx) => {
927
+ const agentId = resolveAgentId(toolCtx?.agentId, context.agentId) ?? "main";
928
+ return {
929
+ name: "memory_list",
930
+ label: "Memory List",
931
+ description: "List recent memories with optional filtering by scope and category.",
932
+ parameters: Type.Object({
933
+ limit: Type.Optional(
934
+ Type.Number({
935
+ description: "Max memories to list (default: 10, max: 50)"
936
+ })
937
+ ),
938
+ scope: Type.Optional(
939
+ Type.String({ description: "Filter by specific scope (optional)" })
940
+ ),
941
+ category: Type.Optional(stringEnum(MEMORY_CATEGORIES)),
942
+ offset: Type.Optional(
943
+ Type.Number({
944
+ description: "Number of memories to skip (default: 0)"
945
+ })
946
+ )
947
+ }),
948
+ async execute(_toolCallId, params, _signal, _onUpdate, runtimeCtx) {
949
+ const {
950
+ limit = 10,
951
+ scope,
952
+ category,
953
+ offset = 0
954
+ } = params;
955
+ try {
956
+ const safeLimit = clampInt(limit, 1, 50);
957
+ const safeOffset = clampInt(offset, 0, 1e3);
958
+ const agentId2 = resolveRuntimeAgentId(context.agentId, runtimeCtx);
959
+ let scopeFilter = context.scopeManager.getAccessibleScopes(agentId2);
960
+ if (scope) {
961
+ if (context.scopeManager.isAccessible(scope, agentId2)) {
962
+ scopeFilter = [scope];
963
+ } else {
964
+ return {
965
+ content: [
966
+ { type: "text", text: `Access denied to scope: ${scope}` }
967
+ ],
968
+ details: {
969
+ error: "scope_access_denied",
970
+ requestedScope: scope
971
+ }
972
+ };
973
+ }
974
+ }
975
+ const entries = await context.store.list(
976
+ scopeFilter,
977
+ category,
978
+ safeLimit,
979
+ safeOffset
980
+ );
981
+ if (entries.length === 0) {
982
+ return {
983
+ content: [{ type: "text", text: "No memories found." }],
984
+ details: {
985
+ count: 0,
986
+ filters: {
987
+ scope,
988
+ category,
989
+ limit: safeLimit,
990
+ offset: safeOffset
991
+ }
992
+ }
993
+ };
994
+ }
995
+ const text = entries.map((entry, i) => {
996
+ const date = new Date(entry.timestamp).toISOString().split("T")[0];
997
+ const categoryTag = getDisplayCategoryTag(entry);
998
+ return `${safeOffset + i + 1}. [${entry.id}] [${categoryTag}] ${entry.text.slice(0, 100)}${entry.text.length > 100 ? "..." : ""} (${date})`;
999
+ }).join("\n");
1000
+ return {
1001
+ content: [
1002
+ {
1003
+ type: "text",
1004
+ text: `Recent memories (showing ${entries.length}):
1005
+
1006
+ ${text}`
1007
+ }
1008
+ ],
1009
+ details: {
1010
+ count: entries.length,
1011
+ memories: entries.map((e) => ({
1012
+ id: e.id,
1013
+ text: e.text,
1014
+ category: getDisplayCategoryTag(e),
1015
+ rawCategory: e.category,
1016
+ scope: e.scope,
1017
+ importance: e.importance,
1018
+ timestamp: e.timestamp
1019
+ })),
1020
+ filters: {
1021
+ scope,
1022
+ category,
1023
+ limit: safeLimit,
1024
+ offset: safeOffset
1025
+ }
1026
+ }
1027
+ };
1028
+ } catch (error) {
1029
+ return {
1030
+ content: [
1031
+ {
1032
+ type: "text",
1033
+ text: `Failed to list memories: ${error instanceof Error ? error.message : String(error)}`
1034
+ }
1035
+ ],
1036
+ details: { error: "list_failed", message: String(error) }
1037
+ };
1038
+ }
1039
+ }
1040
+ };
1041
+ },
1042
+ { name: "memory_list" }
1043
+ );
1044
+ }
1045
+ function registerAllMemoryTools(api, context, options = {}) {
1046
+ registerMemoryRecallTool(api, context);
1047
+ registerMemoryStoreTool(api, context);
1048
+ registerMemoryForgetTool(api, context);
1049
+ registerMemoryUpdateTool(api, context);
1050
+ if (options.enableManagementTools) {
1051
+ registerMemoryStatsTool(api, context);
1052
+ registerMemoryListTool(api, context);
1053
+ }
1054
+ if (options.enableSelfImprovementTools !== false) {
1055
+ registerSelfImprovementLogTool(api, context);
1056
+ if (options.enableManagementTools) {
1057
+ registerSelfImprovementExtractSkillTool(api, context);
1058
+ registerSelfImprovementReviewTool(api, context);
1059
+ }
1060
+ }
1061
+ }
1062
+ export {
1063
+ MEMORY_CATEGORIES,
1064
+ registerAllMemoryTools,
1065
+ registerMemoryForgetTool,
1066
+ registerMemoryListTool,
1067
+ registerMemoryRecallTool,
1068
+ registerMemoryStatsTool,
1069
+ registerMemoryStoreTool,
1070
+ registerMemoryUpdateTool,
1071
+ registerSelfImprovementExtractSkillTool,
1072
+ registerSelfImprovementLogTool,
1073
+ registerSelfImprovementReviewTool
1074
+ };
1075
+ //# sourceMappingURL=tools.js.map