@agentmemory/agentmemory 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.github/workflows/ci.yml +22 -0
  3. package/.github/workflows/publish.yml +28 -0
  4. package/AGENTS.md +113 -0
  5. package/LICENSE +190 -0
  6. package/README.md +828 -0
  7. package/assets/banner.png +0 -0
  8. package/assets/demo.gif +0 -0
  9. package/assets/demo.mp4 +0 -0
  10. package/benchmark/QUALITY.md +73 -0
  11. package/benchmark/REAL-EMBEDDINGS.md +67 -0
  12. package/benchmark/SCALE.md +110 -0
  13. package/benchmark/dataset.ts +293 -0
  14. package/benchmark/quality-eval.ts +643 -0
  15. package/benchmark/real-embeddings-eval.ts +405 -0
  16. package/benchmark/scale-eval.ts +398 -0
  17. package/dist/cli.d.mts +1 -0
  18. package/dist/cli.mjs +137 -0
  19. package/dist/cli.mjs.map +1 -0
  20. package/dist/docker-compose.yml +14 -0
  21. package/dist/hooks/notification.d.mts +1 -0
  22. package/dist/hooks/notification.mjs +45 -0
  23. package/dist/hooks/notification.mjs.map +1 -0
  24. package/dist/hooks/post-tool-failure.d.mts +1 -0
  25. package/dist/hooks/post-tool-failure.mjs +45 -0
  26. package/dist/hooks/post-tool-failure.mjs.map +1 -0
  27. package/dist/hooks/post-tool-use.d.mts +1 -0
  28. package/dist/hooks/post-tool-use.mjs +53 -0
  29. package/dist/hooks/post-tool-use.mjs.map +1 -0
  30. package/dist/hooks/pre-compact.d.mts +1 -0
  31. package/dist/hooks/pre-compact.mjs +50 -0
  32. package/dist/hooks/pre-compact.mjs.map +1 -0
  33. package/dist/hooks/pre-tool-use.d.mts +1 -0
  34. package/dist/hooks/pre-tool-use.mjs +69 -0
  35. package/dist/hooks/pre-tool-use.mjs.map +1 -0
  36. package/dist/hooks/prompt-submit.d.mts +1 -0
  37. package/dist/hooks/prompt-submit.mjs +40 -0
  38. package/dist/hooks/prompt-submit.mjs.map +1 -0
  39. package/dist/hooks/session-end.d.mts +1 -0
  40. package/dist/hooks/session-end.mjs +61 -0
  41. package/dist/hooks/session-end.mjs.map +1 -0
  42. package/dist/hooks/session-start.d.mts +1 -0
  43. package/dist/hooks/session-start.mjs +42 -0
  44. package/dist/hooks/session-start.mjs.map +1 -0
  45. package/dist/hooks/stop.d.mts +1 -0
  46. package/dist/hooks/stop.mjs +33 -0
  47. package/dist/hooks/stop.mjs.map +1 -0
  48. package/dist/hooks/subagent-start.d.mts +1 -0
  49. package/dist/hooks/subagent-start.mjs +43 -0
  50. package/dist/hooks/subagent-start.mjs.map +1 -0
  51. package/dist/hooks/subagent-stop.d.mts +1 -0
  52. package/dist/hooks/subagent-stop.mjs +45 -0
  53. package/dist/hooks/subagent-stop.mjs.map +1 -0
  54. package/dist/hooks/task-completed.d.mts +1 -0
  55. package/dist/hooks/task-completed.mjs +46 -0
  56. package/dist/hooks/task-completed.mjs.map +1 -0
  57. package/dist/iii-config.yaml +51 -0
  58. package/dist/index.d.mts +2 -0
  59. package/dist/index.mjs +13776 -0
  60. package/dist/index.mjs.map +1 -0
  61. package/dist/src-QxitMPfJ.mjs +13775 -0
  62. package/dist/src-QxitMPfJ.mjs.map +1 -0
  63. package/dist/standalone.d.mts +1 -0
  64. package/dist/standalone.mjs +1155 -0
  65. package/dist/standalone.mjs.map +1 -0
  66. package/dist/transformers-BX_tgxdO.mjs +38684 -0
  67. package/dist/transformers-BX_tgxdO.mjs.map +1 -0
  68. package/dist/transformers-KMm1i9no.mjs +38683 -0
  69. package/dist/transformers-KMm1i9no.mjs.map +1 -0
  70. package/docker-compose.yml +14 -0
  71. package/iii-config.yaml +51 -0
  72. package/package.json +59 -0
  73. package/plugin/.claude-plugin/plugin.json +10 -0
  74. package/plugin/hooks/hooks.json +77 -0
  75. package/plugin/scripts/diagnostics.mjs +551 -0
  76. package/plugin/scripts/notification.mjs +45 -0
  77. package/plugin/scripts/post-tool-failure.mjs +45 -0
  78. package/plugin/scripts/post-tool-use.mjs +53 -0
  79. package/plugin/scripts/pre-compact.mjs +50 -0
  80. package/plugin/scripts/pre-tool-use.mjs +69 -0
  81. package/plugin/scripts/prompt-submit.mjs +40 -0
  82. package/plugin/scripts/session-end.mjs +61 -0
  83. package/plugin/scripts/session-start.mjs +42 -0
  84. package/plugin/scripts/stop.mjs +33 -0
  85. package/plugin/scripts/subagent-start.mjs +43 -0
  86. package/plugin/scripts/subagent-stop.mjs +45 -0
  87. package/plugin/scripts/task-completed.mjs +46 -0
  88. package/plugin/skills/forget/SKILL.md +32 -0
  89. package/plugin/skills/recall/SKILL.md +18 -0
  90. package/plugin/skills/remember/SKILL.md +25 -0
  91. package/plugin/skills/session-history/SKILL.md +17 -0
  92. package/src/auth.ts +12 -0
  93. package/src/cli.ts +159 -0
  94. package/src/config.ts +221 -0
  95. package/src/eval/metrics-store.ts +65 -0
  96. package/src/eval/quality.ts +51 -0
  97. package/src/eval/schemas.ts +124 -0
  98. package/src/eval/self-correct.ts +28 -0
  99. package/src/eval/validator.ts +31 -0
  100. package/src/functions/actions.ts +288 -0
  101. package/src/functions/audit.ts +61 -0
  102. package/src/functions/auto-forget.ts +169 -0
  103. package/src/functions/branch-aware.ts +169 -0
  104. package/src/functions/cascade.ts +80 -0
  105. package/src/functions/checkpoints.ts +209 -0
  106. package/src/functions/claude-bridge.ts +161 -0
  107. package/src/functions/compress.ts +194 -0
  108. package/src/functions/consolidate.ts +212 -0
  109. package/src/functions/consolidation-pipeline.ts +258 -0
  110. package/src/functions/context.ts +169 -0
  111. package/src/functions/crystallize.ts +293 -0
  112. package/src/functions/dedup.ts +57 -0
  113. package/src/functions/diagnostics.ts +785 -0
  114. package/src/functions/enrich.ts +132 -0
  115. package/src/functions/evict.ts +163 -0
  116. package/src/functions/export-import.ts +508 -0
  117. package/src/functions/facets.ts +248 -0
  118. package/src/functions/file-index.ts +106 -0
  119. package/src/functions/flow-compress.ts +214 -0
  120. package/src/functions/frontier.ts +196 -0
  121. package/src/functions/governance.ts +131 -0
  122. package/src/functions/graph-retrieval.ts +277 -0
  123. package/src/functions/graph.ts +275 -0
  124. package/src/functions/leases.ts +216 -0
  125. package/src/functions/lessons.ts +253 -0
  126. package/src/functions/mesh.ts +434 -0
  127. package/src/functions/migrate.ts +165 -0
  128. package/src/functions/observe.ts +144 -0
  129. package/src/functions/obsidian-export.ts +310 -0
  130. package/src/functions/patterns.ts +138 -0
  131. package/src/functions/privacy.ts +39 -0
  132. package/src/functions/profile.ts +155 -0
  133. package/src/functions/query-expansion.ts +186 -0
  134. package/src/functions/relations.ts +237 -0
  135. package/src/functions/remember.ts +162 -0
  136. package/src/functions/retention.ts +235 -0
  137. package/src/functions/routines.ts +289 -0
  138. package/src/functions/search.ts +80 -0
  139. package/src/functions/sentinels.ts +417 -0
  140. package/src/functions/signals.ts +186 -0
  141. package/src/functions/sketches.ts +274 -0
  142. package/src/functions/sliding-window.ts +257 -0
  143. package/src/functions/smart-search.ts +115 -0
  144. package/src/functions/snapshot.ts +219 -0
  145. package/src/functions/summarize.ts +155 -0
  146. package/src/functions/team.ts +147 -0
  147. package/src/functions/temporal-graph.ts +476 -0
  148. package/src/functions/timeline.ts +138 -0
  149. package/src/functions/verify.ts +117 -0
  150. package/src/health/monitor.ts +110 -0
  151. package/src/health/thresholds.ts +73 -0
  152. package/src/hooks/notification.ts +52 -0
  153. package/src/hooks/post-tool-failure.ts +58 -0
  154. package/src/hooks/post-tool-use.ts +62 -0
  155. package/src/hooks/pre-compact.ts +60 -0
  156. package/src/hooks/pre-tool-use.ts +72 -0
  157. package/src/hooks/prompt-submit.ts +46 -0
  158. package/src/hooks/session-end.ts +71 -0
  159. package/src/hooks/session-start.ts +48 -0
  160. package/src/hooks/stop.ts +39 -0
  161. package/src/hooks/subagent-start.ts +49 -0
  162. package/src/hooks/subagent-stop.ts +54 -0
  163. package/src/hooks/task-completed.ts +54 -0
  164. package/src/index.ts +342 -0
  165. package/src/mcp/in-memory-kv.ts +61 -0
  166. package/src/mcp/server.ts +1455 -0
  167. package/src/mcp/standalone.ts +177 -0
  168. package/src/mcp/tools-registry.ts +769 -0
  169. package/src/mcp/transport.ts +91 -0
  170. package/src/prompts/compression.ts +67 -0
  171. package/src/prompts/consolidation.ts +48 -0
  172. package/src/prompts/graph-extraction.ts +35 -0
  173. package/src/prompts/summary.ts +38 -0
  174. package/src/prompts/xml.ts +26 -0
  175. package/src/providers/agent-sdk.ts +34 -0
  176. package/src/providers/anthropic.ts +35 -0
  177. package/src/providers/circuit-breaker.ts +82 -0
  178. package/src/providers/embedding/cohere.ts +46 -0
  179. package/src/providers/embedding/gemini.ts +54 -0
  180. package/src/providers/embedding/index.ts +39 -0
  181. package/src/providers/embedding/local.ts +52 -0
  182. package/src/providers/embedding/openai.ts +45 -0
  183. package/src/providers/embedding/openrouter.ts +51 -0
  184. package/src/providers/embedding/voyage.ts +46 -0
  185. package/src/providers/fallback-chain.ts +31 -0
  186. package/src/providers/index.ts +84 -0
  187. package/src/providers/openrouter.ts +71 -0
  188. package/src/providers/resilient.ts +37 -0
  189. package/src/state/hybrid-search.ts +295 -0
  190. package/src/state/index-persistence.ts +63 -0
  191. package/src/state/keyed-mutex.ts +18 -0
  192. package/src/state/kv.ts +33 -0
  193. package/src/state/schema.ts +71 -0
  194. package/src/state/search-index.ts +245 -0
  195. package/src/state/stemmer.ts +104 -0
  196. package/src/state/synonyms.ts +63 -0
  197. package/src/state/vector-index.ts +130 -0
  198. package/src/telemetry/setup.ts +116 -0
  199. package/src/triggers/api.ts +1904 -0
  200. package/src/triggers/events.ts +71 -0
  201. package/src/types.ts +769 -0
  202. package/src/version.ts +1 -0
  203. package/src/viewer/index.html +2497 -0
  204. package/src/viewer/server.ts +207 -0
  205. package/src/xenova.d.ts +3 -0
  206. package/test/actions.test.ts +490 -0
  207. package/test/audit.test.ts +108 -0
  208. package/test/auto-forget.test.ts +188 -0
  209. package/test/cascade.test.ts +277 -0
  210. package/test/checkpoints.test.ts +493 -0
  211. package/test/circuit-breaker.test.ts +107 -0
  212. package/test/claude-bridge.test.ts +178 -0
  213. package/test/confidence.test.ts +247 -0
  214. package/test/consistency.test.ts +61 -0
  215. package/test/consolidation-pipeline.test.ts +251 -0
  216. package/test/crystallize.test.ts +521 -0
  217. package/test/diagnostics.test.ts +638 -0
  218. package/test/embedding-provider.test.ts +49 -0
  219. package/test/enrich.test.ts +209 -0
  220. package/test/eval.test.ts +300 -0
  221. package/test/export-import.test.ts +251 -0
  222. package/test/facets.test.ts +448 -0
  223. package/test/fallback-chain.test.ts +93 -0
  224. package/test/frontier.test.ts +485 -0
  225. package/test/governance.test.ts +147 -0
  226. package/test/graph-retrieval.test.ts +186 -0
  227. package/test/graph.test.ts +160 -0
  228. package/test/helpers/mocks.ts +40 -0
  229. package/test/hybrid-search.test.ts +145 -0
  230. package/test/index-persistence.test.ts +124 -0
  231. package/test/integration.test.ts +265 -0
  232. package/test/leases.test.ts +399 -0
  233. package/test/mcp-prompts.test.ts +218 -0
  234. package/test/mcp-resources.test.ts +286 -0
  235. package/test/mcp-standalone.test.ts +113 -0
  236. package/test/mesh.test.ts +700 -0
  237. package/test/privacy.test.ts +87 -0
  238. package/test/profile.test.ts +161 -0
  239. package/test/query-expansion.test.ts +154 -0
  240. package/test/relations.test.ts +198 -0
  241. package/test/retention.test.ts +245 -0
  242. package/test/routines.test.ts +497 -0
  243. package/test/schema-fingerprint.test.ts +81 -0
  244. package/test/schema.test.ts +42 -0
  245. package/test/search-index.test.ts +128 -0
  246. package/test/sentinels.test.ts +626 -0
  247. package/test/signals.test.ts +410 -0
  248. package/test/sketches.test.ts +549 -0
  249. package/test/sliding-window.test.ts +199 -0
  250. package/test/smart-search.test.ts +169 -0
  251. package/test/snapshot.test.ts +165 -0
  252. package/test/team.test.ts +156 -0
  253. package/test/temporal-graph.test.ts +378 -0
  254. package/test/timeline.test.ts +148 -0
  255. package/test/vector-index.test.ts +79 -0
  256. package/test/verify.test.ts +209 -0
  257. package/test/xml.test.ts +65 -0
  258. package/tsconfig.json +22 -0
  259. package/tsdown.config.ts +62 -0
@@ -0,0 +1,398 @@
1
+ import { SearchIndex } from "../src/state/search-index.js";
2
+ import { VectorIndex } from "../src/state/vector-index.js";
3
+ import { HybridSearch } from "../src/state/hybrid-search.js";
4
+ import type { CompressedObservation } from "../src/types.js";
5
+ import { generateScaleDataset, generateDataset } from "./dataset.js";
6
+ import { writeFileSync } from "node:fs";
7
+
8
+ function mockKV() {
9
+ const store = new Map<string, Map<string, unknown>>();
10
+ return {
11
+ get: async <T>(scope: string, key: string): Promise<T | null> =>
12
+ (store.get(scope)?.get(key) as T) ?? null,
13
+ set: async <T>(scope: string, key: string, data: T): Promise<T> => {
14
+ if (!store.has(scope)) store.set(scope, new Map());
15
+ store.get(scope)!.set(key, data);
16
+ return data;
17
+ },
18
+ delete: async (scope: string, key: string): Promise<void> => {
19
+ store.get(scope)?.delete(key);
20
+ },
21
+ list: async <T>(scope: string): Promise<T[]> => {
22
+ const entries = store.get(scope);
23
+ return entries ? (Array.from(entries.values()) as T[]) : [];
24
+ },
25
+ };
26
+ }
27
+
28
+ function deterministicEmbedding(text: string, dims = 384): Float32Array {
29
+ const arr = new Float32Array(dims);
30
+ const words = text.toLowerCase().split(/\W+/).filter(w => w.length > 2);
31
+ for (const word of words) {
32
+ for (let i = 0; i < word.length; i++) {
33
+ const idx = (word.charCodeAt(i) * 31 + i * 17) % dims;
34
+ arr[idx] += 1;
35
+ const idx2 = (word.charCodeAt(i) * 37 + i * 13 + word.length * 7) % dims;
36
+ arr[idx2] += 0.5;
37
+ }
38
+ }
39
+ const norm = Math.sqrt(arr.reduce((s, v) => s + v * v, 0));
40
+ if (norm > 0) for (let i = 0; i < dims; i++) arr[i] /= norm;
41
+ return arr;
42
+ }
43
+
44
+ function estimateTokens(text: string): number {
45
+ return Math.ceil(text.length / 4);
46
+ }
47
+
48
+ interface ScaleResult {
49
+ scale: number;
50
+ sessions: number;
51
+ index_build_ms: number;
52
+ index_build_per_doc_ms: number;
53
+ bm25_search_ms: number;
54
+ hybrid_search_ms: number;
55
+ index_size_kb: number;
56
+ vector_size_kb: number;
57
+ heap_mb: number;
58
+ builtin_tokens: number;
59
+ builtin_200line_tokens: number;
60
+ agentmemory_tokens: number;
61
+ token_savings_pct: number;
62
+ builtin_unreachable_pct: number;
63
+ }
64
+
65
+ interface CrossSessionResult {
66
+ query: string;
67
+ target_session: string;
68
+ current_session: string;
69
+ sessions_apart: number;
70
+ bm25_found: boolean;
71
+ bm25_rank: number;
72
+ hybrid_found: boolean;
73
+ hybrid_rank: number;
74
+ builtin_found: boolean;
75
+ latency_ms: number;
76
+ }
77
+
78
+ const SEARCH_QUERIES = [
79
+ "authentication middleware JWT",
80
+ "PostgreSQL connection pooling",
81
+ "Kubernetes pod crash",
82
+ "rate limiting API",
83
+ "Playwright E2E tests",
84
+ "Docker multi-stage build",
85
+ "Redis caching layer",
86
+ "CI/CD GitHub Actions",
87
+ "Prisma migration drift",
88
+ "monitoring Datadog alerts",
89
+ ];
90
+
91
+ async function benchmarkScale(counts: number[]): Promise<ScaleResult[]> {
92
+ const results: ScaleResult[] = [];
93
+
94
+ for (const count of counts) {
95
+ console.log(` Scale: ${count.toLocaleString()} observations...`);
96
+ const observations = generateScaleDataset(count);
97
+ const sessionCount = new Set(observations.map(o => o.sessionId)).size;
98
+
99
+ const heapBefore = process.memoryUsage().heapUsed;
100
+
101
+ const buildStart = performance.now();
102
+ const bm25 = new SearchIndex();
103
+ const vector = new VectorIndex();
104
+ const kv = mockKV();
105
+ const dims = 384;
106
+
107
+ for (const obs of observations) {
108
+ bm25.add(obs);
109
+ const text = [obs.title, obs.narrative, ...obs.concepts].join(" ");
110
+ vector.add(obs.id, obs.sessionId, deterministicEmbedding(text, dims));
111
+ await kv.set(`mem:obs:${obs.sessionId}`, obs.id, obs);
112
+ }
113
+ const buildMs = performance.now() - buildStart;
114
+
115
+ const heapAfter = process.memoryUsage().heapUsed;
116
+
117
+ const mockEmbed: any = {
118
+ name: "deterministic", dimensions: dims,
119
+ embed: async (t: string) => deterministicEmbedding(t, dims),
120
+ embedBatch: async (ts: string[]) => ts.map(t => deterministicEmbedding(t, dims)),
121
+ };
122
+ const hybrid = new HybridSearch(bm25, vector, mockEmbed, kv as never, 0.4, 0.6, 0);
123
+
124
+ let bm25Total = 0;
125
+ let hybridTotal = 0;
126
+ const iters = 20;
127
+
128
+ for (let i = 0; i < iters; i++) {
129
+ const q = SEARCH_QUERIES[i % SEARCH_QUERIES.length];
130
+ const s1 = performance.now();
131
+ bm25.search(q, 10);
132
+ bm25Total += performance.now() - s1;
133
+
134
+ const s2 = performance.now();
135
+ await hybrid.search(q, 10);
136
+ hybridTotal += performance.now() - s2;
137
+ }
138
+
139
+ const bm25Ser = bm25.serialize();
140
+ const vecSer = vector.serialize();
141
+
142
+ const allText = observations.map(o =>
143
+ `- ${o.title}: ${o.narrative.slice(0, 80)}... [${o.concepts.slice(0, 3).join(", ")}]`
144
+ ).join("\n");
145
+ const builtinTokens = estimateTokens(allText);
146
+
147
+ const truncatedText = observations.slice(0, 200).map(o =>
148
+ `- ${o.title}: ${o.narrative.slice(0, 60)}... [${o.concepts.slice(0, 3).join(", ")}]`
149
+ ).join("\n");
150
+ const builtin200Tokens = estimateTokens(truncatedText);
151
+
152
+ let totalResultTokens = 0;
153
+ for (let i = 0; i < iters; i++) {
154
+ const q = SEARCH_QUERIES[i % SEARCH_QUERIES.length];
155
+ const results = await hybrid.search(q, 10);
156
+ totalResultTokens += estimateTokens(JSON.stringify(results.map(r => r.observation)));
157
+ }
158
+ const agentmemoryTokens = Math.round(totalResultTokens / iters);
159
+
160
+ results.push({
161
+ scale: count,
162
+ sessions: sessionCount,
163
+ index_build_ms: Math.round(buildMs),
164
+ index_build_per_doc_ms: +(buildMs / count).toFixed(3),
165
+ bm25_search_ms: +(bm25Total / iters).toFixed(3),
166
+ hybrid_search_ms: +(hybridTotal / iters).toFixed(3),
167
+ index_size_kb: Math.round(Buffer.byteLength(bm25Ser, "utf-8") / 1024),
168
+ vector_size_kb: Math.round(Buffer.byteLength(vecSer, "utf-8") / 1024),
169
+ heap_mb: Math.round((heapAfter - heapBefore) / 1024 / 1024),
170
+ builtin_tokens: builtinTokens,
171
+ builtin_200line_tokens: builtin200Tokens,
172
+ agentmemory_tokens: agentmemoryTokens,
173
+ token_savings_pct: Math.round((1 - agentmemoryTokens / builtinTokens) * 100),
174
+ builtin_unreachable_pct: count <= 200 ? 0 : Math.round((1 - 200 / count) * 100),
175
+ });
176
+ }
177
+
178
+ return results;
179
+ }
180
+
181
+ async function benchmarkCrossSession(): Promise<CrossSessionResult[]> {
182
+ const { observations } = generateDataset();
183
+ const results: CrossSessionResult[] = [];
184
+
185
+ const bm25 = new SearchIndex();
186
+ const kv = mockKV();
187
+ const vector = new VectorIndex();
188
+ const dims = 384;
189
+
190
+ for (const obs of observations) {
191
+ bm25.add(obs);
192
+ const text = [obs.title, obs.narrative, ...obs.concepts].join(" ");
193
+ vector.add(obs.id, obs.sessionId, deterministicEmbedding(text, dims));
194
+ await kv.set(`mem:obs:${obs.sessionId}`, obs.id, obs);
195
+ }
196
+
197
+ const mockEmbed: any = {
198
+ name: "deterministic", dimensions: dims,
199
+ embed: async (t: string) => deterministicEmbedding(t, dims),
200
+ embedBatch: async (ts: string[]) => ts.map(t => deterministicEmbedding(t, dims)),
201
+ };
202
+ const hybrid = new HybridSearch(bm25, vector, mockEmbed, kv as never, 0.4, 0.6, 0);
203
+
204
+ const crossQueries: Array<{
205
+ query: string;
206
+ targetConcepts: string[];
207
+ targetSessionRange: [number, number];
208
+ currentSession: number;
209
+ }> = [
210
+ { query: "How did we set up OAuth providers?", targetConcepts: ["oauth", "nextauth"], targetSessionRange: [5, 9], currentSession: 29 },
211
+ { query: "What was the N+1 query fix?", targetConcepts: ["n+1", "eager-loading"], targetSessionRange: [10, 14], currentSession: 28 },
212
+ { query: "PostgreSQL full-text search setup", targetConcepts: ["full-text-search", "tsvector"], targetSessionRange: [10, 14], currentSession: 27 },
213
+ { query: "bcrypt password hashing configuration", targetConcepts: ["bcrypt", "password-hashing"], targetSessionRange: [5, 9], currentSession: 25 },
214
+ { query: "Vitest unit testing setup", targetConcepts: ["vitest", "unit-testing"], targetSessionRange: [20, 24], currentSession: 29 },
215
+ { query: "webhook retry exponential backoff", targetConcepts: ["webhooks", "exponential-backoff"], targetSessionRange: [15, 19], currentSession: 29 },
216
+ { query: "ESLint flat config migration", targetConcepts: ["eslint", "linting"], targetSessionRange: [0, 4], currentSession: 29 },
217
+ { query: "Kubernetes HPA autoscaling configuration", targetConcepts: ["hpa", "autoscaling", "kubernetes"], targetSessionRange: [25, 29], currentSession: 29 },
218
+ { query: "Prisma database seed script", targetConcepts: ["seeding", "faker", "prisma"], targetSessionRange: [10, 14], currentSession: 26 },
219
+ { query: "API cursor-based pagination", targetConcepts: ["cursor-based", "pagination"], targetSessionRange: [15, 19], currentSession: 29 },
220
+ { query: "CSRF protection double-submit cookie", targetConcepts: ["csrf", "cookies"], targetSessionRange: [5, 9], currentSession: 29 },
221
+ { query: "blue-green deployment rollback", targetConcepts: ["blue-green", "rollback", "zero-downtime"], targetSessionRange: [25, 29], currentSession: 29 },
222
+ ];
223
+
224
+ for (const cq of crossQueries) {
225
+ const targetObs = observations.filter(o =>
226
+ o.concepts.some(c => cq.targetConcepts.includes(c))
227
+ );
228
+ const targetIds = new Set(targetObs.map(o => o.id));
229
+
230
+ const start = performance.now();
231
+ const bm25Results = bm25.search(cq.query, 20);
232
+ const hybridResults = await hybrid.search(cq.query, 20);
233
+ const latency = performance.now() - start;
234
+
235
+ const bm25Rank = bm25Results.findIndex(r => targetIds.has(r.obsId));
236
+ const hybridRank = hybridResults.findIndex(r => targetIds.has(r.observation.id));
237
+
238
+ const builtinLines = 200;
239
+ const visibleObs = observations.slice(0, builtinLines);
240
+ const builtinFound = visibleObs.some(o => targetIds.has(o.id));
241
+
242
+ const sessionsApart = cq.currentSession - cq.targetSessionRange[0];
243
+
244
+ results.push({
245
+ query: cq.query,
246
+ target_session: `ses_${cq.targetSessionRange[0].toString().padStart(3, "0")}-${cq.targetSessionRange[1].toString().padStart(3, "0")}`,
247
+ current_session: `ses_${cq.currentSession.toString().padStart(3, "0")}`,
248
+ sessions_apart: sessionsApart,
249
+ bm25_found: bm25Rank >= 0,
250
+ bm25_rank: bm25Rank >= 0 ? bm25Rank + 1 : -1,
251
+ hybrid_found: hybridRank >= 0,
252
+ hybrid_rank: hybridRank >= 0 ? hybridRank + 1 : -1,
253
+ builtin_found: builtinFound,
254
+ latency_ms: latency,
255
+ });
256
+ }
257
+
258
+ return results;
259
+ }
260
+
261
+ function generateReport(scale: ScaleResult[], cross: CrossSessionResult[]): string {
262
+ const lines: string[] = [];
263
+ const w = (s: string) => lines.push(s);
264
+
265
+ w("# agentmemory v0.6.0 — Scale & Cross-Session Evaluation");
266
+ w("");
267
+ w(`**Date:** ${new Date().toISOString()}`);
268
+ w(`**Platform:** ${process.platform} ${process.arch}, Node ${process.version}`);
269
+ w("");
270
+
271
+ w("## 1. Scale: agentmemory vs Built-in Memory");
272
+ w("");
273
+ w("Every built-in agent memory (CLAUDE.md, .cursorrules, Cline's memory-bank) loads ALL memory into context every session. agentmemory searches and returns only relevant results.");
274
+ w("");
275
+ w("| Observations | Sessions | Index Build | BM25 Search | Hybrid Search | Heap | Context Tokens (built-in) | Context Tokens (agentmemory) | Savings | Built-in Unreachable |");
276
+ w("|-------------|----------|------------|-------------|---------------|------|--------------------------|-----------------------------|---------|--------------------|");
277
+
278
+ for (const r of scale) {
279
+ w(`| ${r.scale.toLocaleString()} | ${r.sessions} | ${r.index_build_ms}ms | ${r.bm25_search_ms}ms | ${r.hybrid_search_ms}ms | ${r.heap_mb}MB | ${r.builtin_tokens.toLocaleString()} | ${r.agentmemory_tokens.toLocaleString()} | ${r.token_savings_pct}% | ${r.builtin_unreachable_pct}% |`);
280
+ }
281
+
282
+ w("");
283
+ w("### What the numbers mean");
284
+ w("");
285
+ w("**Context Tokens (built-in):** How many tokens Claude Code/Cursor/Cline would consume loading ALL memory into the context window. At 5,000 observations, this is ~250K tokens — exceeding most context windows entirely.");
286
+ w("");
287
+ w("**Context Tokens (agentmemory):** How many tokens the top-10 search results consume. Stays constant regardless of corpus size.");
288
+ w("");
289
+ w("**Built-in Unreachable:** Percentage of memories that built-in systems CANNOT access because they exceed the 200-line MEMORY.md cap or context window limits. At 1,000 observations, 80% of your project history is invisible.");
290
+ w("");
291
+
292
+ w("### Storage Costs");
293
+ w("");
294
+ w("| Observations | BM25 Index | Vector Index (d=384) | Total Storage |");
295
+ w("|-------------|-----------|---------------------|---------------|");
296
+ for (const r of scale) {
297
+ const total = r.index_size_kb + r.vector_size_kb;
298
+ w(`| ${r.scale.toLocaleString()} | ${r.index_size_kb.toLocaleString()} KB | ${r.vector_size_kb.toLocaleString()} KB | ${(total / 1024).toFixed(1)} MB |`);
299
+ }
300
+
301
+ w("");
302
+ w("## 2. Cross-Session Retrieval");
303
+ w("");
304
+ w("Can the system find relevant information from past sessions? This is impossible for built-in memory once observations exceed the line/context cap.");
305
+ w("");
306
+ w("| Query | Target Session | Gap | BM25 Found | BM25 Rank | Hybrid Found | Hybrid Rank | Built-in Visible |");
307
+ w("|-------|---------------|-----|-----------|-----------|-------------|-------------|-----------------|");
308
+
309
+ for (const r of cross) {
310
+ w(`| ${r.query.slice(0, 40)}${r.query.length > 40 ? "..." : ""} | ${r.target_session} | ${r.sessions_apart} | ${r.bm25_found ? "Yes" : "No"} | ${r.bm25_rank > 0 ? `#${r.bm25_rank}` : "-"} | ${r.hybrid_found ? "Yes" : "No"} | ${r.hybrid_rank > 0 ? `#${r.hybrid_rank}` : "-"} | ${r.builtin_found ? "Yes" : "No"} |`);
311
+ }
312
+
313
+ const bm25Found = cross.filter(r => r.bm25_found).length;
314
+ const hybridFound = cross.filter(r => r.hybrid_found).length;
315
+ const builtinFound = cross.filter(r => r.builtin_found).length;
316
+
317
+ w("");
318
+ w(`**Summary:** agentmemory BM25 found ${bm25Found}/${cross.length} cross-session queries. Hybrid found ${hybridFound}/${cross.length}. Built-in memory (200-line cap) could only reach ${builtinFound}/${cross.length}.`);
319
+
320
+ w("");
321
+ w("## 3. The Context Window Problem");
322
+ w("");
323
+ w("```");
324
+ w("Agent context window: ~200K tokens");
325
+ w("System prompt + tools: ~20K tokens");
326
+ w("User conversation: ~30K tokens");
327
+ w("Available for memory: ~150K tokens");
328
+ w("");
329
+ w("At 50 tokens/observation:");
330
+ w(" 200 observations = 10,000 tokens (fits, but 200-line cap hits first)");
331
+ w(" 1,000 observations = 50,000 tokens (33% of available budget)");
332
+ w(" 5,000 observations = 250,000 tokens (EXCEEDS total context window)");
333
+ w("");
334
+ w("agentmemory top-10 results:");
335
+ w(` Any corpus size = ~${scale[0]?.agentmemory_tokens.toLocaleString() || "500"} tokens (0.3% of budget)`);
336
+ w("```");
337
+ w("");
338
+
339
+ w("## 4. What Built-in Memory Cannot Do");
340
+ w("");
341
+ w("| Capability | Built-in (CLAUDE.md) | agentmemory |");
342
+ w("|-----------|---------------------|-------------|");
343
+ w("| Semantic search | No (keyword grep only) | BM25 + vector + graph |");
344
+ w("| Scale beyond 200 lines | No (hard cap) | Unlimited |");
345
+ w("| Cross-session recall | Only if in 200-line window | Full corpus search |");
346
+ w("| Cross-agent sharing | No (per-agent files) | MCP + REST API |");
347
+ w("| Multi-agent coordination | No | Leases, signals, actions |");
348
+ w("| Temporal queries | No | Point-in-time graph |");
349
+ w("| Memory lifecycle | No (manual pruning) | Ebbinghaus decay + eviction |");
350
+ w("| Knowledge graph | No | Entity extraction + traversal |");
351
+ w("| Query expansion | No | LLM-generated reformulations |");
352
+ w("| Retention scoring | No | Time-frequency decay model |");
353
+ w("| Real-time dashboard | No (read files manually) | Viewer on :3113 |");
354
+ w("| Concurrent access | No (file lock) | Keyed mutex + KV store |");
355
+ w("");
356
+
357
+ w("## 5. When to Use What");
358
+ w("");
359
+ w("**Use built-in memory (CLAUDE.md) when:**");
360
+ w("- You have < 200 items to remember");
361
+ w("- Single agent, single project");
362
+ w("- Preferences and quick facts only");
363
+ w("- Zero setup is the priority");
364
+ w("");
365
+ w("**Use agentmemory when:**");
366
+ w("- Project history exceeds 200 observations");
367
+ w("- You need to recall specific incidents from weeks ago");
368
+ w("- Multiple agents work on the same codebase");
369
+ w("- You want semantic search (\"how does auth work?\") not just keyword matching");
370
+ w("- You need to track memory quality, decay, and lifecycle");
371
+ w("- You want a shared memory layer across Claude Code, Cursor, Windsurf, etc.");
372
+ w("");
373
+ w("Built-in memory is your sticky notes. agentmemory is the searchable database behind them.");
374
+ w("");
375
+
376
+ w("---");
377
+ w(`*Scale tests: ${scale.length} corpus sizes. Cross-session tests: ${cross.length} queries targeting specific past sessions.*`);
378
+
379
+ return lines.join("\n");
380
+ }
381
+
382
+ async function main() {
383
+ console.log("=== agentmemory Scale & Cross-Session Evaluation ===\n");
384
+
385
+ console.log("1. Scale benchmarks...");
386
+ const scaleResults = await benchmarkScale([240, 1_000, 5_000, 10_000, 50_000]);
387
+
388
+ console.log("\n2. Cross-session retrieval...");
389
+ const crossResults = await benchmarkCrossSession();
390
+
391
+ console.log("");
392
+ const report = generateReport(scaleResults, crossResults);
393
+ writeFileSync("benchmark/SCALE.md", report);
394
+ console.log(report);
395
+ console.log(`\nReport written to benchmark/SCALE.md`);
396
+ }
397
+
398
+ main().catch(console.error);
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.mjs ADDED
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync, spawn } from "node:child_process";
3
+ import { existsSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ //#region src/cli.ts
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const args = process.argv.slice(2);
10
+ if (args.includes("--help") || args.includes("-h")) {
11
+ console.log(`
12
+ agentmemory — persistent memory for AI coding agents
13
+
14
+ Usage: agentmemory [options]
15
+
16
+ Options:
17
+ --help, -h Show this help
18
+ --tools all|core Tool visibility (default: core = 7 tools)
19
+ --no-engine Skip auto-starting iii-engine
20
+ --port <N> Override REST port (default: 3111)
21
+
22
+ Environment:
23
+ AGENTMEMORY_TOOLS=all Expose all 41 MCP tools
24
+ AGENTMEMORY_SECRET=xxx Auth secret for REST/MCP
25
+ CONSOLIDATION_ENABLED=true Enable auto-consolidation (off by default)
26
+ OBSIDIAN_AUTO_EXPORT=true Auto-export on consolidation
27
+
28
+ Quick start:
29
+ npx agentmemory # auto-starts iii-engine, runs worker
30
+ npx agentmemory-mcp # standalone MCP server (no engine needed)
31
+ `);
32
+ process.exit(0);
33
+ }
34
+ const toolsIdx = args.indexOf("--tools");
35
+ if (toolsIdx !== -1 && args[toolsIdx + 1]) process.env["AGENTMEMORY_TOOLS"] = args[toolsIdx + 1];
36
+ const portIdx = args.indexOf("--port");
37
+ if (portIdx !== -1 && args[portIdx + 1]) process.env["III_REST_PORT"] = args[portIdx + 1];
38
+ const skipEngine = args.includes("--no-engine");
39
+ function getRestPort() {
40
+ return parseInt(process.env["III_REST_PORT"] || "3111", 10) || 3111;
41
+ }
42
+ async function isEngineRunning() {
43
+ try {
44
+ return (await fetch(`http://localhost:${getRestPort()}/agentmemory/livez`, { signal: AbortSignal.timeout(2e3) })).ok;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+ function findIiiConfig() {
50
+ const candidates = [
51
+ join(__dirname, "iii-config.yaml"),
52
+ join(__dirname, "..", "iii-config.yaml"),
53
+ join(process.cwd(), "iii-config.yaml")
54
+ ];
55
+ for (const c of candidates) if (existsSync(c)) return c;
56
+ return "";
57
+ }
58
+ function whichBinary(name) {
59
+ const cmd = process.platform === "win32" ? "where" : "which";
60
+ try {
61
+ return execFileSync(cmd, [name], { encoding: "utf-8" }).trim().split("\n")[0];
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+ async function startEngine() {
67
+ const configPath = findIiiConfig();
68
+ const iiiBin = whichBinary("iii");
69
+ if (iiiBin && configPath) {
70
+ console.log(`[agentmemory] Starting iii-engine: ${iiiBin} --config ${configPath}`);
71
+ spawn(iiiBin, ["--config", configPath], {
72
+ detached: true,
73
+ stdio: "ignore"
74
+ }).unref();
75
+ return true;
76
+ }
77
+ const dockerCompose = join(__dirname, "..", "docker-compose.yml");
78
+ const dcExists = existsSync(dockerCompose) || existsSync(join(process.cwd(), "docker-compose.yml"));
79
+ const dockerBin = whichBinary("docker");
80
+ if (dockerBin && dcExists) {
81
+ const composeFile = existsSync(dockerCompose) ? dockerCompose : join(process.cwd(), "docker-compose.yml");
82
+ console.log(`[agentmemory] Starting iii-engine via Docker...`);
83
+ spawn(dockerBin, [
84
+ "compose",
85
+ "-f",
86
+ composeFile,
87
+ "up",
88
+ "-d"
89
+ ], {
90
+ detached: true,
91
+ stdio: "ignore"
92
+ }).unref();
93
+ return true;
94
+ }
95
+ return false;
96
+ }
97
+ async function waitForEngine(timeoutMs) {
98
+ const start = Date.now();
99
+ while (Date.now() - start < timeoutMs) {
100
+ if (await isEngineRunning()) return true;
101
+ await new Promise((r) => setTimeout(r, 500));
102
+ }
103
+ return false;
104
+ }
105
+ async function main() {
106
+ if (!skipEngine && !await isEngineRunning()) {
107
+ if (!await startEngine()) {
108
+ console.error(`
109
+ [agentmemory] iii-engine is not running and could not be auto-started.
110
+
111
+ Install one of:
112
+ 1. iii CLI: npm install -g iii-engine
113
+ 2. Docker: docker compose up -d (with docker-compose.yml)
114
+
115
+ Or run with --no-engine to connect to a remote engine.
116
+ `);
117
+ process.exit(1);
118
+ }
119
+ console.log(`[agentmemory] Waiting for iii-engine to start...`);
120
+ if (!await waitForEngine(15e3)) {
121
+ const port = getRestPort();
122
+ console.error(`[agentmemory] iii-engine did not become ready within 15s.`);
123
+ console.error(`[agentmemory] Check that ports ${port}, ${port + 1}, 49134 are available.`);
124
+ process.exit(1);
125
+ }
126
+ console.log(`[agentmemory] iii-engine is ready.`);
127
+ }
128
+ await import("./src-QxitMPfJ.mjs");
129
+ }
130
+ main().catch((err) => {
131
+ console.error(`[agentmemory] Fatal:`, err);
132
+ process.exit(1);
133
+ });
134
+
135
+ //#endregion
136
+ export { };
137
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { spawn, execFileSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nconst args = process.argv.slice(2);\n\nif (args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(`\nagentmemory — persistent memory for AI coding agents\n\nUsage: agentmemory [options]\n\nOptions:\n --help, -h Show this help\n --tools all|core Tool visibility (default: core = 7 tools)\n --no-engine Skip auto-starting iii-engine\n --port <N> Override REST port (default: 3111)\n\nEnvironment:\n AGENTMEMORY_TOOLS=all Expose all 41 MCP tools\n AGENTMEMORY_SECRET=xxx Auth secret for REST/MCP\n CONSOLIDATION_ENABLED=true Enable auto-consolidation (off by default)\n OBSIDIAN_AUTO_EXPORT=true Auto-export on consolidation\n\nQuick start:\n npx agentmemory # auto-starts iii-engine, runs worker\n npx agentmemory-mcp # standalone MCP server (no engine needed)\n`);\n process.exit(0);\n}\n\nconst toolsIdx = args.indexOf(\"--tools\");\nif (toolsIdx !== -1 && args[toolsIdx + 1]) {\n process.env[\"AGENTMEMORY_TOOLS\"] = args[toolsIdx + 1];\n}\n\nconst portIdx = args.indexOf(\"--port\");\nif (portIdx !== -1 && args[portIdx + 1]) {\n process.env[\"III_REST_PORT\"] = args[portIdx + 1];\n}\n\nconst skipEngine = args.includes(\"--no-engine\");\n\nfunction getRestPort(): number {\n return parseInt(process.env[\"III_REST_PORT\"] || \"3111\", 10) || 3111;\n}\n\nasync function isEngineRunning(): Promise<boolean> {\n try {\n const res = await fetch(`http://localhost:${getRestPort()}/agentmemory/livez`, {\n signal: AbortSignal.timeout(2000),\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\nfunction findIiiConfig(): string {\n const candidates = [\n join(__dirname, \"iii-config.yaml\"),\n join(__dirname, \"..\", \"iii-config.yaml\"),\n join(process.cwd(), \"iii-config.yaml\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n return \"\";\n}\n\nfunction whichBinary(name: string): string | null {\n const cmd = process.platform === \"win32\" ? \"where\" : \"which\";\n try {\n return execFileSync(cmd, [name], { encoding: \"utf-8\" }).trim().split(\"\\n\")[0];\n } catch {\n return null;\n }\n}\n\nasync function startEngine(): Promise<boolean> {\n const configPath = findIiiConfig();\n\n const iiiBin = whichBinary(\"iii\");\n if (iiiBin && configPath) {\n console.log(`[agentmemory] Starting iii-engine: ${iiiBin} --config ${configPath}`);\n const child = spawn(iiiBin, [\"--config\", configPath], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n return true;\n }\n\n const dockerCompose = join(__dirname, \"..\", \"docker-compose.yml\");\n const dcExists = existsSync(dockerCompose) || existsSync(join(process.cwd(), \"docker-compose.yml\"));\n const dockerBin = whichBinary(\"docker\");\n\n if (dockerBin && dcExists) {\n const composeFile = existsSync(dockerCompose) ? dockerCompose : join(process.cwd(), \"docker-compose.yml\");\n console.log(`[agentmemory] Starting iii-engine via Docker...`);\n const child = spawn(dockerBin, [\"compose\", \"-f\", composeFile, \"up\", \"-d\"], {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n return true;\n }\n\n return false;\n}\n\nasync function waitForEngine(timeoutMs: number): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await isEngineRunning()) return true;\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n\nasync function main() {\n if (!skipEngine && !(await isEngineRunning())) {\n const started = await startEngine();\n if (!started) {\n console.error(`\n[agentmemory] iii-engine is not running and could not be auto-started.\n\nInstall one of:\n 1. iii CLI: npm install -g iii-engine\n 2. Docker: docker compose up -d (with docker-compose.yml)\n\nOr run with --no-engine to connect to a remote engine.\n`);\n process.exit(1);\n }\n\n console.log(`[agentmemory] Waiting for iii-engine to start...`);\n const ready = await waitForEngine(15000);\n if (!ready) {\n const port = getRestPort();\n console.error(`[agentmemory] iii-engine did not become ready within 15s.`);\n console.error(`[agentmemory] Check that ports ${port}, ${port + 1}, 49134 are available.`);\n process.exit(1);\n }\n console.log(`[agentmemory] iii-engine is ready.`);\n }\n\n await import(\"./index.js\");\n}\n\nmain().catch((err) => {\n console.error(`[agentmemory] Fatal:`, err);\n process.exit(1);\n});\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ services:
2
+ iii-engine:
3
+ image: iiidev/iii:latest
4
+ ports:
5
+ - "49134:49134"
6
+ - "3111:3111"
7
+ - "3112:3112"
8
+ - "9464:9464"
9
+ volumes:
10
+ - iii-data:/data
11
+ restart: unless-stopped
12
+
13
+ volumes:
14
+ iii-data:
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ //#region src/hooks/notification.ts
3
+ const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
4
+ const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
5
+ function authHeaders() {
6
+ const h = { "Content-Type": "application/json" };
7
+ if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
8
+ return h;
9
+ }
10
+ async function main() {
11
+ let input = "";
12
+ for await (const chunk of process.stdin) input += chunk;
13
+ let data;
14
+ try {
15
+ data = JSON.parse(input);
16
+ } catch {
17
+ return;
18
+ }
19
+ if (data.notification_type !== "permission_prompt") return;
20
+ const sessionId = data.session_id || "unknown";
21
+ try {
22
+ await fetch(`${REST_URL}/agentmemory/observe`, {
23
+ method: "POST",
24
+ headers: authHeaders(),
25
+ body: JSON.stringify({
26
+ hookType: "notification",
27
+ sessionId,
28
+ project: data.cwd || process.cwd(),
29
+ cwd: data.cwd || process.cwd(),
30
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
31
+ data: {
32
+ notification_type: data.notification_type,
33
+ title: data.title,
34
+ message: data.message
35
+ }
36
+ }),
37
+ signal: AbortSignal.timeout(2e3)
38
+ });
39
+ } catch {}
40
+ }
41
+ main();
42
+
43
+ //#endregion
44
+ export { };
45
+ //# sourceMappingURL=notification.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification.mjs","names":[],"sources":["../../src/hooks/notification.ts"],"sourcesContent":["#!/usr/bin/env node\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (data.notification_type !== \"permission_prompt\") return;\n\n const sessionId = (data.session_id as string) || \"unknown\";\n\n try {\n await fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"notification\",\n sessionId,\n project: data.cwd || process.cwd(),\n cwd: data.cwd || process.cwd(),\n timestamp: new Date().toISOString(),\n data: {\n notification_type: data.notification_type,\n title: data.title,\n message: data.message,\n },\n }),\n signal: AbortSignal.timeout(2000),\n });\n } catch {\n // fire and forget\n }\n}\n\nmain();\n"],"mappings":";;AAEA,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,KAAK,sBAAsB,oBAAqB;CAEpD,MAAM,YAAa,KAAK,cAAyB;AAEjD,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,uBAAuB;GAC7C,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU;IACnB,UAAU;IACV;IACA,SAAS,KAAK,OAAO,QAAQ,KAAK;IAClC,KAAK,KAAK,OAAO,QAAQ,KAAK;IAC9B,4BAAW,IAAI,MAAM,EAAC,aAAa;IACnC,MAAM;KACJ,mBAAmB,KAAK;KACxB,OAAO,KAAK;KACZ,SAAS,KAAK;KACf;IACF,CAAC;GACF,QAAQ,YAAY,QAAQ,IAAK;GAClC,CAAC;SACI;;AAKV,MAAM"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ //#region src/hooks/post-tool-failure.ts
3
+ const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
4
+ const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
5
+ function authHeaders() {
6
+ const h = { "Content-Type": "application/json" };
7
+ if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
8
+ return h;
9
+ }
10
+ async function main() {
11
+ let input = "";
12
+ for await (const chunk of process.stdin) input += chunk;
13
+ let data;
14
+ try {
15
+ data = JSON.parse(input);
16
+ } catch {
17
+ return;
18
+ }
19
+ if (data.is_interrupt) return;
20
+ const sessionId = data.session_id || "unknown";
21
+ try {
22
+ await fetch(`${REST_URL}/agentmemory/observe`, {
23
+ method: "POST",
24
+ headers: authHeaders(),
25
+ body: JSON.stringify({
26
+ hookType: "post_tool_failure",
27
+ sessionId,
28
+ project: data.cwd || process.cwd(),
29
+ cwd: data.cwd || process.cwd(),
30
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
31
+ data: {
32
+ tool_name: data.tool_name,
33
+ tool_input: typeof data.tool_input === "string" ? data.tool_input.slice(0, 4e3) : JSON.stringify(data.tool_input ?? "").slice(0, 4e3),
34
+ error: typeof data.error === "string" ? data.error.slice(0, 4e3) : JSON.stringify(data.error ?? "").slice(0, 4e3)
35
+ }
36
+ }),
37
+ signal: AbortSignal.timeout(3e3)
38
+ });
39
+ } catch {}
40
+ }
41
+ main();
42
+
43
+ //#endregion
44
+ export { };
45
+ //# sourceMappingURL=post-tool-failure.mjs.map