@agentmemory/agentmemory 0.7.2 → 0.7.4

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 (217) hide show
  1. package/AGENTS.md +4 -4
  2. package/README.md +46 -73
  3. package/dist/cli.mjs +3 -3
  4. package/dist/index.mjs +484 -6
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/{src-1fTKFEtN.mjs → src-DP8JSXkx.mjs} +485 -7
  7. package/dist/src-DP8JSXkx.mjs.map +1 -0
  8. package/dist/standalone.mjs +42 -3
  9. package/dist/standalone.mjs.map +1 -1
  10. package/package.json +10 -1
  11. package/plugin/.claude-plugin/plugin.json +2 -2
  12. package/plugin/scripts/notification.d.mts +1 -0
  13. package/plugin/scripts/notification.mjs.map +1 -0
  14. package/plugin/scripts/post-tool-failure.d.mts +1 -0
  15. package/plugin/scripts/post-tool-failure.mjs.map +1 -0
  16. package/plugin/scripts/post-tool-use.d.mts +1 -0
  17. package/plugin/scripts/post-tool-use.mjs.map +1 -0
  18. package/plugin/scripts/pre-compact.d.mts +1 -0
  19. package/plugin/scripts/pre-compact.mjs.map +1 -0
  20. package/plugin/scripts/pre-tool-use.d.mts +1 -0
  21. package/plugin/scripts/pre-tool-use.mjs.map +1 -0
  22. package/plugin/scripts/prompt-submit.d.mts +1 -0
  23. package/plugin/scripts/prompt-submit.mjs.map +1 -0
  24. package/plugin/scripts/session-end.d.mts +1 -0
  25. package/plugin/scripts/session-end.mjs.map +1 -0
  26. package/plugin/scripts/session-start.d.mts +1 -0
  27. package/plugin/scripts/session-start.mjs.map +1 -0
  28. package/plugin/scripts/stop.d.mts +1 -0
  29. package/plugin/scripts/stop.mjs.map +1 -0
  30. package/plugin/scripts/subagent-start.d.mts +1 -0
  31. package/plugin/scripts/subagent-start.mjs.map +1 -0
  32. package/plugin/scripts/subagent-stop.d.mts +1 -0
  33. package/plugin/scripts/subagent-stop.mjs.map +1 -0
  34. package/plugin/scripts/task-completed.d.mts +1 -0
  35. package/plugin/scripts/task-completed.mjs.map +1 -0
  36. package/.claude-plugin/marketplace.json +0 -14
  37. package/.github/workflows/ci.yml +0 -22
  38. package/.github/workflows/publish.yml +0 -28
  39. package/assets/banner.png +0 -0
  40. package/assets/demo.gif +0 -0
  41. package/assets/demo.mp4 +0 -0
  42. package/benchmark/QUALITY.md +0 -73
  43. package/benchmark/REAL-EMBEDDINGS.md +0 -67
  44. package/benchmark/SCALE.md +0 -110
  45. package/benchmark/dataset.ts +0 -293
  46. package/benchmark/quality-eval.ts +0 -643
  47. package/benchmark/real-embeddings-eval.ts +0 -405
  48. package/benchmark/scale-eval.ts +0 -398
  49. package/dist/src-1fTKFEtN.mjs.map +0 -1
  50. package/src/auth.ts +0 -12
  51. package/src/cli.ts +0 -251
  52. package/src/config.ts +0 -221
  53. package/src/eval/metrics-store.ts +0 -65
  54. package/src/eval/quality.ts +0 -51
  55. package/src/eval/schemas.ts +0 -124
  56. package/src/eval/self-correct.ts +0 -28
  57. package/src/eval/validator.ts +0 -31
  58. package/src/functions/actions.ts +0 -288
  59. package/src/functions/audit.ts +0 -61
  60. package/src/functions/auto-forget.ts +0 -169
  61. package/src/functions/branch-aware.ts +0 -169
  62. package/src/functions/cascade.ts +0 -80
  63. package/src/functions/checkpoints.ts +0 -209
  64. package/src/functions/claude-bridge.ts +0 -161
  65. package/src/functions/compress.ts +0 -194
  66. package/src/functions/consolidate.ts +0 -212
  67. package/src/functions/consolidation-pipeline.ts +0 -258
  68. package/src/functions/context.ts +0 -169
  69. package/src/functions/crystallize.ts +0 -293
  70. package/src/functions/dedup.ts +0 -57
  71. package/src/functions/diagnostics.ts +0 -785
  72. package/src/functions/enrich.ts +0 -132
  73. package/src/functions/evict.ts +0 -163
  74. package/src/functions/export-import.ts +0 -508
  75. package/src/functions/facets.ts +0 -248
  76. package/src/functions/file-index.ts +0 -106
  77. package/src/functions/flow-compress.ts +0 -214
  78. package/src/functions/frontier.ts +0 -196
  79. package/src/functions/governance.ts +0 -131
  80. package/src/functions/graph-retrieval.ts +0 -277
  81. package/src/functions/graph.ts +0 -275
  82. package/src/functions/leases.ts +0 -216
  83. package/src/functions/lessons.ts +0 -253
  84. package/src/functions/mesh.ts +0 -434
  85. package/src/functions/migrate.ts +0 -165
  86. package/src/functions/observe.ts +0 -144
  87. package/src/functions/obsidian-export.ts +0 -310
  88. package/src/functions/patterns.ts +0 -138
  89. package/src/functions/privacy.ts +0 -39
  90. package/src/functions/profile.ts +0 -155
  91. package/src/functions/query-expansion.ts +0 -186
  92. package/src/functions/relations.ts +0 -237
  93. package/src/functions/remember.ts +0 -162
  94. package/src/functions/retention.ts +0 -235
  95. package/src/functions/routines.ts +0 -289
  96. package/src/functions/search.ts +0 -80
  97. package/src/functions/sentinels.ts +0 -417
  98. package/src/functions/signals.ts +0 -186
  99. package/src/functions/sketches.ts +0 -274
  100. package/src/functions/sliding-window.ts +0 -257
  101. package/src/functions/smart-search.ts +0 -115
  102. package/src/functions/snapshot.ts +0 -219
  103. package/src/functions/summarize.ts +0 -155
  104. package/src/functions/team.ts +0 -147
  105. package/src/functions/temporal-graph.ts +0 -476
  106. package/src/functions/timeline.ts +0 -138
  107. package/src/functions/verify.ts +0 -117
  108. package/src/health/monitor.ts +0 -110
  109. package/src/health/thresholds.ts +0 -73
  110. package/src/hooks/notification.ts +0 -52
  111. package/src/hooks/post-tool-failure.ts +0 -58
  112. package/src/hooks/post-tool-use.ts +0 -62
  113. package/src/hooks/pre-compact.ts +0 -60
  114. package/src/hooks/pre-tool-use.ts +0 -72
  115. package/src/hooks/prompt-submit.ts +0 -46
  116. package/src/hooks/session-end.ts +0 -71
  117. package/src/hooks/session-start.ts +0 -48
  118. package/src/hooks/stop.ts +0 -39
  119. package/src/hooks/subagent-start.ts +0 -49
  120. package/src/hooks/subagent-stop.ts +0 -54
  121. package/src/hooks/task-completed.ts +0 -54
  122. package/src/index.ts +0 -342
  123. package/src/mcp/in-memory-kv.ts +0 -61
  124. package/src/mcp/server.ts +0 -1455
  125. package/src/mcp/standalone.ts +0 -177
  126. package/src/mcp/tools-registry.ts +0 -769
  127. package/src/mcp/transport.ts +0 -91
  128. package/src/prompts/compression.ts +0 -67
  129. package/src/prompts/consolidation.ts +0 -48
  130. package/src/prompts/graph-extraction.ts +0 -35
  131. package/src/prompts/summary.ts +0 -38
  132. package/src/prompts/xml.ts +0 -26
  133. package/src/providers/agent-sdk.ts +0 -34
  134. package/src/providers/anthropic.ts +0 -35
  135. package/src/providers/circuit-breaker.ts +0 -82
  136. package/src/providers/embedding/cohere.ts +0 -46
  137. package/src/providers/embedding/gemini.ts +0 -54
  138. package/src/providers/embedding/index.ts +0 -39
  139. package/src/providers/embedding/local.ts +0 -52
  140. package/src/providers/embedding/openai.ts +0 -45
  141. package/src/providers/embedding/openrouter.ts +0 -51
  142. package/src/providers/embedding/voyage.ts +0 -46
  143. package/src/providers/fallback-chain.ts +0 -31
  144. package/src/providers/index.ts +0 -84
  145. package/src/providers/openrouter.ts +0 -71
  146. package/src/providers/resilient.ts +0 -37
  147. package/src/state/hybrid-search.ts +0 -295
  148. package/src/state/index-persistence.ts +0 -63
  149. package/src/state/keyed-mutex.ts +0 -18
  150. package/src/state/kv.ts +0 -33
  151. package/src/state/schema.ts +0 -71
  152. package/src/state/search-index.ts +0 -245
  153. package/src/state/stemmer.ts +0 -104
  154. package/src/state/synonyms.ts +0 -63
  155. package/src/state/vector-index.ts +0 -130
  156. package/src/telemetry/setup.ts +0 -116
  157. package/src/triggers/api.ts +0 -1904
  158. package/src/triggers/events.ts +0 -71
  159. package/src/types.ts +0 -769
  160. package/src/version.ts +0 -1
  161. package/src/viewer/index.html +0 -2556
  162. package/src/viewer/server.ts +0 -207
  163. package/src/xenova.d.ts +0 -3
  164. package/test/actions.test.ts +0 -490
  165. package/test/audit.test.ts +0 -108
  166. package/test/auto-forget.test.ts +0 -188
  167. package/test/cascade.test.ts +0 -277
  168. package/test/checkpoints.test.ts +0 -493
  169. package/test/circuit-breaker.test.ts +0 -107
  170. package/test/claude-bridge.test.ts +0 -178
  171. package/test/confidence.test.ts +0 -247
  172. package/test/consistency.test.ts +0 -61
  173. package/test/consolidation-pipeline.test.ts +0 -251
  174. package/test/crystallize.test.ts +0 -521
  175. package/test/diagnostics.test.ts +0 -638
  176. package/test/embedding-provider.test.ts +0 -49
  177. package/test/enrich.test.ts +0 -209
  178. package/test/eval.test.ts +0 -300
  179. package/test/export-import.test.ts +0 -251
  180. package/test/facets.test.ts +0 -448
  181. package/test/fallback-chain.test.ts +0 -93
  182. package/test/frontier.test.ts +0 -485
  183. package/test/governance.test.ts +0 -147
  184. package/test/graph-retrieval.test.ts +0 -186
  185. package/test/graph.test.ts +0 -160
  186. package/test/helpers/mocks.ts +0 -40
  187. package/test/hybrid-search.test.ts +0 -145
  188. package/test/index-persistence.test.ts +0 -124
  189. package/test/integration.test.ts +0 -265
  190. package/test/leases.test.ts +0 -399
  191. package/test/mcp-prompts.test.ts +0 -218
  192. package/test/mcp-resources.test.ts +0 -286
  193. package/test/mcp-standalone.test.ts +0 -113
  194. package/test/mesh.test.ts +0 -700
  195. package/test/privacy.test.ts +0 -87
  196. package/test/profile.test.ts +0 -161
  197. package/test/query-expansion.test.ts +0 -154
  198. package/test/relations.test.ts +0 -198
  199. package/test/retention.test.ts +0 -245
  200. package/test/routines.test.ts +0 -497
  201. package/test/schema-fingerprint.test.ts +0 -81
  202. package/test/schema.test.ts +0 -42
  203. package/test/search-index.test.ts +0 -128
  204. package/test/sentinels.test.ts +0 -626
  205. package/test/signals.test.ts +0 -410
  206. package/test/sketches.test.ts +0 -549
  207. package/test/sliding-window.test.ts +0 -199
  208. package/test/smart-search.test.ts +0 -169
  209. package/test/snapshot.test.ts +0 -165
  210. package/test/team.test.ts +0 -156
  211. package/test/temporal-graph.test.ts +0 -378
  212. package/test/timeline.test.ts +0 -148
  213. package/test/vector-index.test.ts +0 -79
  214. package/test/verify.test.ts +0 -209
  215. package/test/xml.test.ts +0 -65
  216. package/tsconfig.json +0 -22
  217. package/tsdown.config.ts +0 -62
@@ -825,6 +825,7 @@ const KV = {
825
825
  sentinels: "mem:sentinels",
826
826
  crystals: "mem:crystals",
827
827
  lessons: "mem:lessons",
828
+ insights: "mem:insights",
828
829
  graphEdgeHistory: "mem:graph:edge-history",
829
830
  enrichedChunks: (sessionId) => `mem:enriched:${sessionId}`,
830
831
  latentEmbeddings: (obsId) => `mem:latent:${obsId}`,
@@ -3738,7 +3739,7 @@ function registerAutoForgetFunction(sdk, kv) {
3738
3739
 
3739
3740
  //#endregion
3740
3741
  //#region src/version.ts
3741
- const VERSION = "0.7.2";
3742
+ const VERSION = "0.7.4";
3742
3743
 
3743
3744
  //#endregion
3744
3745
  //#region src/functions/export-import.ts
@@ -3766,7 +3767,7 @@ function registerExportImportFunction(sdk, kv) {
3766
3767
  const uniqueProjects = [...new Set(paginatedSessions.map((s) => s.project))];
3767
3768
  const profileResults = await Promise.all(uniqueProjects.map((project) => kv.get(KV.profiles, project).catch(() => null)));
3768
3769
  for (const profile of profileResults) if (profile) profiles.push(profile);
3769
- const [graphNodes, graphEdges, semanticMemories, proceduralMemories, actions, actionEdges, sentinels, sketches, crystals, facets, lessons, routines, signals, checkpoints] = await Promise.all([
3770
+ const [graphNodes, graphEdges, semanticMemories, proceduralMemories, actions, actionEdges, sentinels, sketches, crystals, facets, lessons, insights, routines, signals, checkpoints] = await Promise.all([
3770
3771
  kv.list(KV.graphNodes).catch(() => []),
3771
3772
  kv.list(KV.graphEdges).catch(() => []),
3772
3773
  kv.list(KV.semantic).catch(() => []),
@@ -3778,6 +3779,7 @@ function registerExportImportFunction(sdk, kv) {
3778
3779
  kv.list(KV.crystals).catch(() => []),
3779
3780
  kv.list(KV.facets).catch(() => []),
3780
3781
  kv.list(KV.lessons).catch(() => []),
3782
+ kv.list(KV.insights).catch(() => []),
3781
3783
  kv.list(KV.routines).catch(() => []),
3782
3784
  kv.list(KV.signals).catch(() => []),
3783
3785
  kv.list(KV.checkpoints).catch(() => [])
@@ -3801,6 +3803,7 @@ function registerExportImportFunction(sdk, kv) {
3801
3803
  crystals: crystals.length > 0 ? crystals : void 0,
3802
3804
  facets: facets.length > 0 ? facets : void 0,
3803
3805
  lessons: lessons.length > 0 ? lessons : void 0,
3806
+ insights: insights.length > 0 ? insights : void 0,
3804
3807
  routines: routines.length > 0 ? routines : void 0,
3805
3808
  signals: signals.length > 0 ? signals : void 0,
3806
3809
  checkpoints: checkpoints.length > 0 ? checkpoints : void 0
@@ -3835,7 +3838,9 @@ function registerExportImportFunction(sdk, kv) {
3835
3838
  "0.6.0",
3836
3839
  "0.6.1",
3837
3840
  "0.7.0",
3838
- "0.7.2"
3841
+ "0.7.2",
3842
+ "0.7.3",
3843
+ "0.7.4"
3839
3844
  ]).has(importData.version)) return {
3840
3845
  success: false,
3841
3846
  error: `Unsupported export version: ${importData.version}`
@@ -3922,6 +3927,7 @@ function registerExportImportFunction(sdk, kv) {
3922
3927
  for (const c of await kv.list(KV.crystals).catch(() => [])) await kv.delete(KV.crystals, c.id);
3923
3928
  for (const f of await kv.list(KV.facets).catch(() => [])) await kv.delete(KV.facets, f.id);
3924
3929
  for (const l of await kv.list(KV.lessons).catch(() => [])) await kv.delete(KV.lessons, l.id);
3930
+ for (const i of await kv.list(KV.insights).catch(() => [])) await kv.delete(KV.insights, i.id);
3925
3931
  for (const n of await kv.list(KV.graphNodes).catch(() => [])) await kv.delete(KV.graphNodes, n.id);
3926
3932
  for (const e of await kv.list(KV.graphEdges).catch(() => [])) await kv.delete(KV.graphEdges, e.id);
3927
3933
  for (const s of await kv.list(KV.semantic).catch(() => [])) await kv.delete(KV.semantic, s.id);
@@ -4093,6 +4099,15 @@ function registerExportImportFunction(sdk, kv) {
4093
4099
  }
4094
4100
  await kv.set(KV.lessons, lesson.id, lesson);
4095
4101
  }
4102
+ if (importData.insights) for (const insight of importData.insights) {
4103
+ if (strategy === "skip") {
4104
+ if (await kv.get(KV.insights, insight.id).catch(() => null)) {
4105
+ stats.skipped++;
4106
+ continue;
4107
+ }
4108
+ }
4109
+ await kv.set(KV.insights, insight.id, insight);
4110
+ }
4096
4111
  ctx.logger.info("Import complete", {
4097
4112
  strategy,
4098
4113
  ...stats
@@ -4647,6 +4662,16 @@ function registerConsolidationPipelineFunction(sdk, kv, provider) {
4647
4662
  reason: "fewer than 5 summaries"
4648
4663
  };
4649
4664
  }
4665
+ if (tier === "all" || tier === "reflect") try {
4666
+ results.reflect = await sdk.trigger("mem::reflect", {
4667
+ maxClusters: 10,
4668
+ project: data?.project
4669
+ });
4670
+ } catch (err) {
4671
+ const msg = err instanceof Error ? err.message : String(err);
4672
+ ctx.logger.warn("Reflect tier failed", { error: msg });
4673
+ results.reflect = { error: msg };
4674
+ }
4650
4675
  if (tier === "all" || tier === "procedural") {
4651
4676
  const patterns = (await kv.list(KV.memories)).filter((m) => m.isLatest && m.type === "pattern").map((m) => ({
4652
4677
  content: m.content,
@@ -8403,6 +8428,320 @@ function registerObsidianExportFunction(sdk, kv) {
8403
8428
  });
8404
8429
  }
8405
8430
 
8431
+ //#endregion
8432
+ //#region src/prompts/reflect.ts
8433
+ const REFLECT_SYSTEM = `You are a higher-order reasoning engine. Given a cluster of related concepts, facts, lessons, and action outcomes, synthesize cross-cutting insights that span multiple individual memories.
8434
+
8435
+ Output format (XML):
8436
+ <insights>
8437
+ <insight confidence="0.0-1.0" title="Short descriptive title">
8438
+ The higher-order observation or principle. Should be actionable and non-obvious — something that only becomes visible when viewing multiple memories together.
8439
+ </insight>
8440
+ </insights>
8441
+
8442
+ Rules:
8443
+ - Identify patterns, principles, or strategies that span 2+ source items
8444
+ - Confidence reflects how well-supported the insight is across sources
8445
+ - Title should be a concise label (under 60 chars)
8446
+ - Content should be the actual observation (1-3 sentences)
8447
+ - Prefer actionable insights over abstract summaries
8448
+ - Skip insights that merely restate a single source item
8449
+ - Always emit confidence attribute before title attribute`;
8450
+ function buildReflectPrompt(cluster) {
8451
+ const sections = [];
8452
+ sections.push(`## Concept Cluster: ${cluster.concepts.join(", ")}`);
8453
+ if (cluster.facts.length > 0) sections.push("\n## Known Facts", ...cluster.facts.map((f) => `- [confidence=${f.confidence}] ${f.fact}`));
8454
+ if (cluster.lessons.length > 0) sections.push("\n## Lessons Learned", ...cluster.lessons.map((l) => `- [confidence=${l.confidence}] ${l.content}`));
8455
+ if (cluster.crystalNarratives.length > 0) sections.push("\n## Completed Work Summaries", ...cluster.crystalNarratives.map((n) => `- ${n}`));
8456
+ return `Synthesize higher-order insights from this cluster of related memories:\n\n${sections.join("\n")}`;
8457
+ }
8458
+
8459
+ //#endregion
8460
+ //#region src/functions/reflect.ts
8461
+ function reinforceInsight(insight) {
8462
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8463
+ insight.reinforcements++;
8464
+ insight.confidence = Math.min(1, insight.confidence + .1 * (1 - insight.confidence));
8465
+ insight.lastReinforcedAt = now;
8466
+ insight.updatedAt = now;
8467
+ }
8468
+ function buildGraphClusters(nodes, edges, maxClusters) {
8469
+ const conceptNodes = nodes.filter((n) => n.type === "concept" && !n.stale);
8470
+ if (conceptNodes.length === 0) return [];
8471
+ const edgeMap = /* @__PURE__ */ new Map();
8472
+ for (const edge of edges) {
8473
+ if (edge.stale) continue;
8474
+ if (!edgeMap.has(edge.sourceNodeId)) edgeMap.set(edge.sourceNodeId, /* @__PURE__ */ new Set());
8475
+ if (!edgeMap.has(edge.targetNodeId)) edgeMap.set(edge.targetNodeId, /* @__PURE__ */ new Set());
8476
+ edgeMap.get(edge.sourceNodeId).add(edge.targetNodeId);
8477
+ edgeMap.get(edge.targetNodeId).add(edge.sourceNodeId);
8478
+ }
8479
+ const degree = /* @__PURE__ */ new Map();
8480
+ for (const node of conceptNodes) degree.set(node.id, edgeMap.get(node.id)?.size || 0);
8481
+ const sorted = [...conceptNodes].sort((a, b) => (degree.get(b.id) || 0) - (degree.get(a.id) || 0));
8482
+ const visited = /* @__PURE__ */ new Set();
8483
+ const clusters = [];
8484
+ const conceptNodeIds = new Set(conceptNodes.map((n) => n.id));
8485
+ for (const seed of sorted) {
8486
+ if (visited.has(seed.id) || clusters.length >= maxClusters) break;
8487
+ const cluster = [];
8488
+ const queue = [seed.id];
8489
+ const seen = /* @__PURE__ */ new Set();
8490
+ let depth = 0;
8491
+ while (queue.length > 0 && depth <= 2) {
8492
+ const levelCount = queue.length;
8493
+ for (let i = 0; i < levelCount; i++) {
8494
+ const current = queue.shift();
8495
+ if (seen.has(current)) continue;
8496
+ seen.add(current);
8497
+ if (conceptNodeIds.has(current)) {
8498
+ const node = conceptNodes.find((n) => n.id === current);
8499
+ if (node) cluster.push(node.name);
8500
+ visited.add(current);
8501
+ }
8502
+ const neighbors = edgeMap.get(current) || /* @__PURE__ */ new Set();
8503
+ for (const neighbor of neighbors) if (!seen.has(neighbor)) queue.push(neighbor);
8504
+ }
8505
+ depth++;
8506
+ }
8507
+ if (cluster.length >= 2) clusters.push(cluster);
8508
+ }
8509
+ return clusters;
8510
+ }
8511
+ function buildJaccardClusters(semanticMemories, lessons, maxClusters) {
8512
+ const allConcepts = /* @__PURE__ */ new Map();
8513
+ for (const sem of semanticMemories) {
8514
+ const terms = sem.fact.toLowerCase().split(/\s+/).filter((t) => t.length > 3);
8515
+ for (const term of terms) {
8516
+ if (!allConcepts.has(term)) allConcepts.set(term, /* @__PURE__ */ new Set());
8517
+ allConcepts.get(term).add(sem.id);
8518
+ }
8519
+ }
8520
+ for (const lesson of lessons) for (const tag of lesson.tags) {
8521
+ const key = tag.toLowerCase();
8522
+ if (!allConcepts.has(key)) allConcepts.set(key, /* @__PURE__ */ new Set());
8523
+ allConcepts.get(key).add(lesson.id);
8524
+ }
8525
+ const conceptList = [...allConcepts.keys()].filter((k) => (allConcepts.get(k)?.size || 0) >= 2);
8526
+ const visited = /* @__PURE__ */ new Set();
8527
+ const clusters = [];
8528
+ for (const concept of conceptList) {
8529
+ if (visited.has(concept) || clusters.length >= maxClusters) break;
8530
+ const cluster = [concept];
8531
+ visited.add(concept);
8532
+ const docsA = allConcepts.get(concept) || /* @__PURE__ */ new Set();
8533
+ for (const other of conceptList) {
8534
+ if (visited.has(other)) continue;
8535
+ const docsB = allConcepts.get(other) || /* @__PURE__ */ new Set();
8536
+ let intersection = 0;
8537
+ for (const d of docsA) if (docsB.has(d)) intersection++;
8538
+ const union = docsA.size + docsB.size - intersection;
8539
+ if ((union > 0 ? intersection / union : 0) > .3) {
8540
+ cluster.push(other);
8541
+ visited.add(other);
8542
+ }
8543
+ }
8544
+ if (cluster.length >= 2) clusters.push(cluster);
8545
+ }
8546
+ return clusters;
8547
+ }
8548
+ function registerReflectFunctions(sdk, kv, provider) {
8549
+ sdk.registerFunction({ id: "mem::reflect" }, async (data) => {
8550
+ const maxClusters = Math.min(data?.maxClusters ?? 10, 20);
8551
+ const maxInsightsPerCluster = 5;
8552
+ const maxTotal = 50;
8553
+ const [graphNodes, graphEdges, semanticMemories, lessons, crystals] = await Promise.all([
8554
+ kv.list(KV.graphNodes).catch(() => []),
8555
+ kv.list(KV.graphEdges).catch(() => []),
8556
+ kv.list(KV.semantic).catch(() => []),
8557
+ kv.list(KV.lessons).catch(() => []),
8558
+ kv.list(KV.crystals).catch(() => [])
8559
+ ]);
8560
+ let activeLessons = lessons.filter((l) => !l.deleted);
8561
+ if (data?.project) activeLessons = activeLessons.filter((l) => l.project === data.project);
8562
+ let conceptClusters = buildGraphClusters(graphNodes, graphEdges, maxClusters);
8563
+ const usedFallback = conceptClusters.length === 0;
8564
+ if (usedFallback) conceptClusters = buildJaccardClusters(semanticMemories, activeLessons, maxClusters);
8565
+ let newInsights = 0;
8566
+ let reinforced = 0;
8567
+ let clustersSkipped = 0;
8568
+ let totalInsights = 0;
8569
+ for (const conceptNames of conceptClusters) {
8570
+ if (totalInsights >= maxTotal) break;
8571
+ const conceptSet = new Set(conceptNames.map((c) => c.toLowerCase()));
8572
+ const clusterFacts = semanticMemories.filter((s) => {
8573
+ return s.fact.toLowerCase().split(/\s+/).some((t) => conceptSet.has(t));
8574
+ });
8575
+ const clusterLessons = activeLessons.filter((l) => l.tags.some((t) => conceptSet.has(t.toLowerCase())) || conceptNames.some((c) => l.content.toLowerCase().includes(c.toLowerCase())));
8576
+ const clusterCrystals = crystals.filter((c) => (c.lessons || []).some((l) => conceptNames.some((cn) => l.toLowerCase().includes(cn.toLowerCase()))));
8577
+ if (clusterFacts.length + clusterLessons.length + clusterCrystals.length < 3) {
8578
+ clustersSkipped++;
8579
+ continue;
8580
+ }
8581
+ const cluster = {
8582
+ concepts: conceptNames,
8583
+ facts: clusterFacts.map((f) => ({
8584
+ fact: f.fact,
8585
+ confidence: f.confidence
8586
+ })),
8587
+ lessons: clusterLessons.map((l) => ({
8588
+ content: l.content,
8589
+ confidence: l.confidence
8590
+ })),
8591
+ crystalNarratives: clusterCrystals.map((c) => c.narrative),
8592
+ factIds: clusterFacts.map((f) => f.id),
8593
+ lessonIds: clusterLessons.map((l) => l.id),
8594
+ crystalIds: clusterCrystals.map((c) => c.id)
8595
+ };
8596
+ try {
8597
+ const prompt = buildReflectPrompt(cluster);
8598
+ const response = await provider.summarize(REFLECT_SYSTEM, prompt);
8599
+ const insightRegex = /<insight\s+confidence="([^"]+)"\s+title="([^"]+)">([\s\S]*?)<\/insight>/g;
8600
+ let match;
8601
+ let clusterCount = 0;
8602
+ while ((match = insightRegex.exec(response)) !== null && clusterCount < maxInsightsPerCluster && totalInsights < maxTotal) {
8603
+ const parsedConf = parseFloat(match[1]);
8604
+ const confidence = Number.isNaN(parsedConf) ? .5 : Math.max(0, Math.min(1, parsedConf));
8605
+ const title = match[2].trim();
8606
+ const content = match[3].trim();
8607
+ if (!content) continue;
8608
+ const fp = fingerprintId("ins", content.trim().toLowerCase());
8609
+ const existing = await kv.get(KV.insights, fp);
8610
+ if (existing && !existing.deleted) {
8611
+ reinforceInsight(existing);
8612
+ await kv.set(KV.insights, existing.id, existing);
8613
+ reinforced++;
8614
+ } else {
8615
+ const now = (/* @__PURE__ */ new Date()).toISOString();
8616
+ const insight = {
8617
+ id: fp,
8618
+ title,
8619
+ content,
8620
+ confidence,
8621
+ reinforcements: 0,
8622
+ sourceConceptCluster: conceptNames,
8623
+ sourceMemoryIds: cluster.factIds,
8624
+ sourceLessonIds: cluster.lessonIds,
8625
+ sourceCrystalIds: cluster.crystalIds,
8626
+ project: data?.project,
8627
+ tags: conceptNames,
8628
+ createdAt: now,
8629
+ updatedAt: now,
8630
+ decayRate: .05
8631
+ };
8632
+ await kv.set(KV.insights, insight.id, insight);
8633
+ newInsights++;
8634
+ }
8635
+ clusterCount++;
8636
+ totalInsights++;
8637
+ }
8638
+ } catch {
8639
+ continue;
8640
+ }
8641
+ }
8642
+ try {
8643
+ await recordAudit(kv, "reflect", "mem::reflect", [], {
8644
+ newInsights,
8645
+ reinforced,
8646
+ clustersProcessed: conceptClusters.length - clustersSkipped,
8647
+ clustersSkipped,
8648
+ usedFallback
8649
+ });
8650
+ } catch {}
8651
+ return {
8652
+ success: true,
8653
+ newInsights,
8654
+ reinforced,
8655
+ clustersProcessed: conceptClusters.length - clustersSkipped,
8656
+ clustersSkipped,
8657
+ usedFallback
8658
+ };
8659
+ });
8660
+ sdk.registerFunction({ id: "mem::insight-list" }, async (data) => {
8661
+ const limit = data?.limit ?? 50;
8662
+ const minConfidence = data?.minConfidence ?? 0;
8663
+ let items = await kv.list(KV.insights);
8664
+ items = items.filter((i) => !i.deleted && i.confidence >= minConfidence);
8665
+ if (data?.project) items = items.filter((i) => i.project === data.project);
8666
+ items.sort((a, b) => b.confidence - a.confidence);
8667
+ return {
8668
+ success: true,
8669
+ insights: items.slice(0, limit)
8670
+ };
8671
+ });
8672
+ sdk.registerFunction({ id: "mem::insight-search" }, async (data) => {
8673
+ if (!data?.query?.trim()) return {
8674
+ success: false,
8675
+ error: "query is required"
8676
+ };
8677
+ const query = data.query.toLowerCase();
8678
+ const minConfidence = data.minConfidence ?? .1;
8679
+ const limit = data.limit ?? 10;
8680
+ let items = await kv.list(KV.insights);
8681
+ items = items.filter((i) => !i.deleted && i.confidence >= minConfidence);
8682
+ if (data.project) items = items.filter((i) => i.project === data.project);
8683
+ const terms = query.split(/\s+/).filter((t) => t.length > 1);
8684
+ const scored = items.map((i) => {
8685
+ const text = `${i.title} ${i.content} ${i.tags.join(" ")}`.toLowerCase();
8686
+ const matchCount = terms.filter((t) => text.includes(t)).length;
8687
+ if (matchCount === 0) return null;
8688
+ const relevance = matchCount / terms.length;
8689
+ const recencyBoost = 1 / (1 + (i.lastReinforcedAt ? (Date.now() - new Date(i.lastReinforcedAt).getTime()) / (1e3 * 60 * 60 * 24) : (Date.now() - new Date(i.createdAt).getTime()) / (1e3 * 60 * 60 * 24)) * .01);
8690
+ return {
8691
+ insight: i,
8692
+ score: i.confidence * relevance * recencyBoost
8693
+ };
8694
+ }).filter(Boolean);
8695
+ scored.sort((a, b) => b.score - a.score);
8696
+ try {
8697
+ await recordAudit(kv, "insight_search", "mem::insight-search", [], {
8698
+ query: data.query,
8699
+ resultCount: scored.length
8700
+ });
8701
+ } catch {}
8702
+ return {
8703
+ success: true,
8704
+ insights: scored.slice(0, limit).map((s) => ({
8705
+ ...s.insight,
8706
+ score: Math.round(s.score * 1e3) / 1e3
8707
+ }))
8708
+ };
8709
+ });
8710
+ sdk.registerFunction({ id: "mem::insight-decay-sweep" }, async () => {
8711
+ const items = await kv.list(KV.insights);
8712
+ let decayed = 0;
8713
+ let softDeleted = 0;
8714
+ const now = Date.now();
8715
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
8716
+ const dirty = [];
8717
+ for (const insight of items) {
8718
+ if (insight.deleted) continue;
8719
+ const baseline = insight.lastDecayedAt || insight.lastReinforcedAt || insight.createdAt;
8720
+ const weeksSince = (now - new Date(baseline).getTime()) / (1e3 * 60 * 60 * 24 * 7);
8721
+ if (weeksSince < 1) continue;
8722
+ const decay = insight.decayRate * weeksSince;
8723
+ const newConfidence = Math.max(.05, insight.confidence - decay);
8724
+ if (newConfidence !== insight.confidence) {
8725
+ insight.confidence = Math.round(newConfidence * 1e3) / 1e3;
8726
+ insight.lastDecayedAt = timestamp;
8727
+ insight.updatedAt = timestamp;
8728
+ if (insight.confidence <= .1 && insight.reinforcements === 0) {
8729
+ insight.deleted = true;
8730
+ softDeleted++;
8731
+ } else decayed++;
8732
+ dirty.push(insight);
8733
+ }
8734
+ }
8735
+ await Promise.all(dirty.map((i) => kv.set(KV.insights, i.id, i)));
8736
+ return {
8737
+ success: true,
8738
+ decayed,
8739
+ softDeleted,
8740
+ total: items.length
8741
+ };
8742
+ });
8743
+ }
8744
+
8406
8745
  //#endregion
8407
8746
  //#region src/functions/sliding-window.ts
8408
8747
  const SLIDING_WINDOW_SYSTEM = `You are a contextual enrichment engine. Given a primary observation and its surrounding context window (previous and next observations from the same session), produce an enriched version.
@@ -11191,6 +11530,73 @@ function registerApiTriggers(sdk, kv, secret, metricsStore, provider) {
11191
11530
  http_method: "POST"
11192
11531
  }
11193
11532
  });
11533
+ sdk.registerFunction({ id: "api::reflect" }, async (req) => {
11534
+ const denied = checkAuth$1(req, secret);
11535
+ if (denied) return denied;
11536
+ const body = req.body || {};
11537
+ return {
11538
+ status_code: 200,
11539
+ body: await sdk.trigger("mem::reflect", {
11540
+ project: typeof body.project === "string" ? body.project : void 0,
11541
+ maxClusters: typeof body.maxClusters === "number" ? body.maxClusters : void 0
11542
+ })
11543
+ };
11544
+ });
11545
+ sdk.registerTrigger({
11546
+ type: "http",
11547
+ function_id: "api::reflect",
11548
+ config: {
11549
+ api_path: "/agentmemory/reflect",
11550
+ http_method: "POST"
11551
+ }
11552
+ });
11553
+ sdk.registerFunction({ id: "api::insight-list" }, async (req) => {
11554
+ const denied = checkAuth$1(req, secret);
11555
+ if (denied) return denied;
11556
+ const params = req.query_params || {};
11557
+ return {
11558
+ status_code: 200,
11559
+ body: await sdk.trigger("mem::insight-list", {
11560
+ project: params.project,
11561
+ minConfidence: params.minConfidence ? parseFloat(params.minConfidence) : void 0,
11562
+ limit: params.limit ? parseInt(params.limit, 10) : void 0
11563
+ })
11564
+ };
11565
+ });
11566
+ sdk.registerTrigger({
11567
+ type: "http",
11568
+ function_id: "api::insight-list",
11569
+ config: {
11570
+ api_path: "/agentmemory/insights",
11571
+ http_method: "GET"
11572
+ }
11573
+ });
11574
+ sdk.registerFunction({ id: "api::insight-search" }, async (req) => {
11575
+ const denied = checkAuth$1(req, secret);
11576
+ if (denied) return denied;
11577
+ const body = req.body;
11578
+ if (!body?.query || typeof body.query !== "string") return {
11579
+ status_code: 400,
11580
+ body: { error: "query is required" }
11581
+ };
11582
+ return {
11583
+ status_code: 200,
11584
+ body: await sdk.trigger("mem::insight-search", {
11585
+ query: body.query,
11586
+ project: typeof body.project === "string" ? body.project : void 0,
11587
+ minConfidence: typeof body.minConfidence === "number" ? body.minConfidence : void 0,
11588
+ limit: typeof body.limit === "number" ? body.limit : void 0
11589
+ })
11590
+ };
11591
+ });
11592
+ sdk.registerTrigger({
11593
+ type: "http",
11594
+ function_id: "api::insight-search",
11595
+ config: {
11596
+ api_path: "/agentmemory/insights/search",
11597
+ http_method: "POST"
11598
+ }
11599
+ });
11194
11600
  }
11195
11601
 
11196
11602
  //#endregion
@@ -12103,6 +12509,43 @@ const V070_TOOLS = [
12103
12509
  }
12104
12510
  }
12105
12511
  ];
12512
+ const V073_TOOLS = [{
12513
+ name: "memory_reflect",
12514
+ description: "Traverse the knowledge graph, group related memories by concept clusters, and synthesize higher-order insights via LLM. Returns new and reinforced insights.",
12515
+ inputSchema: {
12516
+ type: "object",
12517
+ properties: {
12518
+ project: {
12519
+ type: "string",
12520
+ description: "Filter by project"
12521
+ },
12522
+ maxClusters: {
12523
+ type: "number",
12524
+ description: "Max concept clusters to process (default 10, max 20)"
12525
+ }
12526
+ }
12527
+ }
12528
+ }, {
12529
+ name: "memory_insight_list",
12530
+ description: "List synthesized insights — higher-order observations derived from patterns across memories, lessons, and crystals.",
12531
+ inputSchema: {
12532
+ type: "object",
12533
+ properties: {
12534
+ project: {
12535
+ type: "string",
12536
+ description: "Filter by project"
12537
+ },
12538
+ minConfidence: {
12539
+ type: "number",
12540
+ description: "Minimum confidence threshold (default 0)"
12541
+ },
12542
+ limit: {
12543
+ type: "number",
12544
+ description: "Max results (default 50)"
12545
+ }
12546
+ }
12547
+ }
12548
+ }];
12106
12549
  const ESSENTIAL_TOOLS = new Set([
12107
12550
  "memory_save",
12108
12551
  "memory_recall",
@@ -12110,7 +12553,8 @@ const ESSENTIAL_TOOLS = new Set([
12110
12553
  "memory_smart_search",
12111
12554
  "memory_sessions",
12112
12555
  "memory_diagnose",
12113
- "memory_lesson_save"
12556
+ "memory_lesson_save",
12557
+ "memory_reflect"
12114
12558
  ]);
12115
12559
  function getAllTools() {
12116
12560
  return [
@@ -12119,7 +12563,8 @@ function getAllTools() {
12119
12563
  ...V050_TOOLS,
12120
12564
  ...V051_TOOLS,
12121
12565
  ...V061_TOOLS,
12122
- ...V070_TOOLS
12566
+ ...V070_TOOLS,
12567
+ ...V073_TOOLS
12123
12568
  ];
12124
12569
  }
12125
12570
  function getVisibleTools() {
@@ -12939,6 +13384,33 @@ function registerMcpEndpoints(sdk, kv, secret) {
12939
13384
  }] }
12940
13385
  };
12941
13386
  }
13387
+ case "memory_reflect": {
13388
+ const reflectResult = await sdk.trigger("mem::reflect", {
13389
+ project: args.project,
13390
+ maxClusters: args.maxClusters
13391
+ });
13392
+ return {
13393
+ status_code: 200,
13394
+ body: { content: [{
13395
+ type: "text",
13396
+ text: JSON.stringify(reflectResult, null, 2)
13397
+ }] }
13398
+ };
13399
+ }
13400
+ case "memory_insight_list": {
13401
+ const insightListResult = await sdk.trigger("mem::insight-list", {
13402
+ project: args.project,
13403
+ minConfidence: args.minConfidence,
13404
+ limit: args.limit
13405
+ });
13406
+ return {
13407
+ status_code: 200,
13408
+ body: { content: [{
13409
+ type: "text",
13410
+ text: JSON.stringify(insightListResult, null, 2)
13411
+ }] }
13412
+ };
13413
+ }
12942
13414
  case "memory_obsidian_export": {
12943
13415
  const exportTypes = typeof args.types === "string" && args.types.trim() ? args.types.split(",").map((t) => t.trim()).filter(Boolean) : void 0;
12944
13416
  const obsidianResult = await sdk.trigger("mem::obsidian-export", {
@@ -13679,6 +14151,7 @@ async function main() {
13679
14151
  registerVerifyFunction(sdk, kv);
13680
14152
  registerLessonsFunctions(sdk, kv);
13681
14153
  registerObsidianExportFunction(sdk, kv);
14154
+ registerReflectFunctions(sdk, kv, provider);
13682
14155
  registerCascadeFunction(sdk, kv);
13683
14156
  registerSlidingWindowFunction(sdk, kv, provider);
13684
14157
  registerQueryExpansionFunction(sdk, provider);
@@ -13723,7 +14196,7 @@ async function main() {
13723
14196
  }
13724
14197
  }
13725
14198
  console.log(`[agentmemory] Ready. ${embeddingProvider ? "Triple-stream (BM25+Vector+Graph)" : "BM25+Graph"} search active.`);
13726
- console.log(`[agentmemory] Endpoints: 100 REST + 41 MCP tools + 6 MCP resources + 3 MCP prompts`);
14199
+ console.log(`[agentmemory] Endpoints: 103 REST + 43 MCP tools + 6 MCP resources + 3 MCP prompts`);
13727
14200
  const viewerServer = startViewerServer(config.restPort + 2, kv, sdk, secret, config.restPort);
13728
14201
  const autoForgetIntervalMs = parseInt(process.env.AUTO_FORGET_INTERVAL_MS || "3600000", 10);
13729
14202
  const consolidationIntervalMs = parseInt(process.env.CONSOLIDATION_INTERVAL_MS || "7200000", 10);
@@ -13743,6 +14216,11 @@ async function main() {
13743
14216
  }, 864e5).unref();
13744
14217
  console.log(`[agentmemory] Lesson decay sweep: enabled (every 24h)`);
13745
14218
  }
14219
+ if (process.env.INSIGHT_DECAY_ENABLED !== "false") setInterval(async () => {
14220
+ try {
14221
+ await sdk.trigger("mem::insight-decay-sweep", {});
14222
+ } catch {}
14223
+ }, 864e5).unref();
13746
14224
  if (isConsolidationEnabled()) {
13747
14225
  setInterval(async () => {
13748
14226
  try {
@@ -13773,4 +14251,4 @@ main().catch((err) => {
13773
14251
 
13774
14252
  //#endregion
13775
14253
  export { };
13776
- //# sourceMappingURL=src-1fTKFEtN.mjs.map
14254
+ //# sourceMappingURL=src-DP8JSXkx.mjs.map