@stackmemoryai/stackmemory 0.5.61 → 0.5.62

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.
@@ -0,0 +1,377 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { logger } from "../core/monitoring/logger.js";
6
+ const DEFAULT_CONFIG = {
7
+ enabled: !!process.env.DIFFMEM_ENDPOINT,
8
+ endpoint: process.env.DIFFMEM_ENDPOINT || "http://localhost:3100",
9
+ autoFetchCategories: ["preference", "expertise", "pattern"],
10
+ autoLearnEnabled: true,
11
+ learningConfidenceThreshold: 0.7,
12
+ maxMemoriesPerSession: 50
13
+ };
14
+ class DiffMemHooks {
15
+ config;
16
+ fetchedMemories = [];
17
+ learningBuffer = [];
18
+ isConnected = false;
19
+ frameManager;
20
+ sessionStartTime = 0;
21
+ constructor(config = {}) {
22
+ this.config = { ...DEFAULT_CONFIG, ...config };
23
+ }
24
+ /**
25
+ * Register session hooks with the event emitter
26
+ */
27
+ register(emitter, frameManager) {
28
+ this.frameManager = frameManager;
29
+ if (!this.config.enabled) {
30
+ logger.debug("DiffMem hooks disabled - skipping registration");
31
+ return;
32
+ }
33
+ emitter.registerHandler("session_start", this.onSessionStart.bind(this));
34
+ emitter.registerHandler("session_end", this.onSessionEnd.bind(this));
35
+ logger.info("DiffMem hooks registered", {
36
+ endpoint: this.config.endpoint,
37
+ autoFetchCategories: this.config.autoFetchCategories,
38
+ autoLearnEnabled: this.config.autoLearnEnabled
39
+ });
40
+ }
41
+ /**
42
+ * Handle session start - fetch user knowledge
43
+ */
44
+ async onSessionStart(event) {
45
+ const sessionEvent = event;
46
+ this.sessionStartTime = Date.now();
47
+ try {
48
+ const status = await this.checkStatus();
49
+ this.isConnected = status.connected;
50
+ if (!this.isConnected) {
51
+ logger.debug("DiffMem not available - skipping memory fetch");
52
+ return;
53
+ }
54
+ const query = {
55
+ categories: this.config.autoFetchCategories,
56
+ limit: this.config.maxMemoriesPerSession,
57
+ minConfidence: this.config.learningConfidenceThreshold
58
+ };
59
+ this.fetchedMemories = await this.fetchMemories(query);
60
+ if (this.frameManager && this.fetchedMemories.length > 0) {
61
+ await this.injectAsAnchors(sessionEvent.data.sessionId);
62
+ }
63
+ logger.info("DiffMem session start completed", {
64
+ memoriesFetched: this.fetchedMemories.length,
65
+ sessionId: sessionEvent.data.sessionId
66
+ });
67
+ } catch (error) {
68
+ logger.warn("DiffMem session start failed", {
69
+ error: error instanceof Error ? error.message : String(error)
70
+ });
71
+ this.isConnected = false;
72
+ }
73
+ }
74
+ /**
75
+ * Handle session end - sync buffered learnings
76
+ */
77
+ async onSessionEnd(event) {
78
+ const sessionEvent = event;
79
+ if (!this.config.autoLearnEnabled || this.learningBuffer.length === 0) {
80
+ logger.debug("No learnings to sync on session end");
81
+ return;
82
+ }
83
+ if (!this.isConnected) {
84
+ const status = await this.checkStatus();
85
+ if (!status.connected) {
86
+ logger.warn("DiffMem not available - learnings not synced", {
87
+ bufferedCount: this.learningBuffer.length
88
+ });
89
+ return;
90
+ }
91
+ this.isConnected = true;
92
+ }
93
+ try {
94
+ await this.syncLearnings();
95
+ const sessionDuration = Date.now() - this.sessionStartTime;
96
+ logger.info("DiffMem session end completed", {
97
+ learningSynced: this.learningBuffer.length,
98
+ sessionId: sessionEvent.data.sessionId,
99
+ sessionDurationMs: sessionDuration
100
+ });
101
+ this.learningBuffer = [];
102
+ } catch (error) {
103
+ logger.warn("DiffMem session end sync failed", {
104
+ error: error instanceof Error ? error.message : String(error),
105
+ bufferedCount: this.learningBuffer.length
106
+ });
107
+ }
108
+ }
109
+ /**
110
+ * Record a learning during session
111
+ */
112
+ recordLearning(insight) {
113
+ if (!this.config.autoLearnEnabled) {
114
+ return;
115
+ }
116
+ if (insight.confidence < this.config.learningConfidenceThreshold) {
117
+ logger.debug("Learning below confidence threshold", {
118
+ confidence: insight.confidence,
119
+ threshold: this.config.learningConfidenceThreshold
120
+ });
121
+ return;
122
+ }
123
+ const learning = {
124
+ ...insight,
125
+ timestamp: Date.now()
126
+ };
127
+ this.learningBuffer.push(learning);
128
+ logger.debug("Learning recorded", {
129
+ category: learning.category,
130
+ confidence: learning.confidence,
131
+ bufferSize: this.learningBuffer.length
132
+ });
133
+ }
134
+ /**
135
+ * Get fetched memories
136
+ */
137
+ getUserKnowledge() {
138
+ return [...this.fetchedMemories];
139
+ }
140
+ /**
141
+ * Format memories for LLM context with token budget
142
+ */
143
+ formatForContext(maxTokens = 2e3) {
144
+ if (this.fetchedMemories.length === 0) {
145
+ return "";
146
+ }
147
+ const sortedMemories = [...this.fetchedMemories].sort((a, b) => {
148
+ if (b.confidence !== a.confidence) {
149
+ return b.confidence - a.confidence;
150
+ }
151
+ return b.timestamp - a.timestamp;
152
+ });
153
+ const sections = /* @__PURE__ */ new Map();
154
+ for (const memory of sortedMemories) {
155
+ const category = memory.category;
156
+ if (!sections.has(category)) {
157
+ sections.set(category, []);
158
+ }
159
+ const categoryMemories = sections.get(category);
160
+ if (categoryMemories) {
161
+ categoryMemories.push(memory.content);
162
+ }
163
+ }
164
+ const lines = ["## User Knowledge"];
165
+ let estimatedTokens = 5;
166
+ const categoryLabels = {
167
+ preference: "Preferences",
168
+ expertise: "Expertise",
169
+ project_knowledge: "Project Knowledge",
170
+ pattern: "Patterns",
171
+ correction: "Corrections"
172
+ };
173
+ for (const [category, contents] of sections) {
174
+ const label = categoryLabels[category] || category;
175
+ const categoryHeader = `
176
+ ### ${label}`;
177
+ const headerTokens = Math.ceil(categoryHeader.length / 4);
178
+ if (estimatedTokens + headerTokens > maxTokens) {
179
+ break;
180
+ }
181
+ lines.push(categoryHeader);
182
+ estimatedTokens += headerTokens;
183
+ for (const content of contents) {
184
+ const contentLine = `- ${content}`;
185
+ const contentTokens = Math.ceil(contentLine.length / 4);
186
+ if (estimatedTokens + contentTokens > maxTokens) {
187
+ lines.push("- (additional items truncated for token budget)");
188
+ break;
189
+ }
190
+ lines.push(contentLine);
191
+ estimatedTokens += contentTokens;
192
+ }
193
+ }
194
+ return lines.join("\n");
195
+ }
196
+ /**
197
+ * Get current connection status
198
+ */
199
+ getStatus() {
200
+ return {
201
+ connected: this.isConnected,
202
+ memoriesLoaded: this.fetchedMemories.length,
203
+ learningsBuffered: this.learningBuffer.length
204
+ };
205
+ }
206
+ /**
207
+ * Update configuration
208
+ */
209
+ updateConfig(config) {
210
+ this.config = { ...this.config, ...config };
211
+ logger.debug("DiffMem config updated", { config: this.config });
212
+ }
213
+ // Private methods
214
+ /**
215
+ * Check DiffMem service status
216
+ */
217
+ async checkStatus() {
218
+ try {
219
+ const controller = new AbortController();
220
+ const timeoutId = setTimeout(() => controller.abort(), 3e3);
221
+ const response = await fetch(`${this.config.endpoint}/status`, {
222
+ method: "GET",
223
+ signal: controller.signal
224
+ });
225
+ clearTimeout(timeoutId);
226
+ if (!response.ok) {
227
+ return { connected: false, memoryCount: 0, lastSync: null };
228
+ }
229
+ const status = await response.json();
230
+ return {
231
+ connected: true,
232
+ memoryCount: status.memoryCount || 0,
233
+ lastSync: status.lastSync || null,
234
+ version: status.version
235
+ };
236
+ } catch {
237
+ return { connected: false, memoryCount: 0, lastSync: null };
238
+ }
239
+ }
240
+ /**
241
+ * Fetch memories from DiffMem
242
+ */
243
+ async fetchMemories(query) {
244
+ try {
245
+ const controller = new AbortController();
246
+ const timeoutId = setTimeout(() => controller.abort(), 5e3);
247
+ const response = await fetch(`${this.config.endpoint}/memories/query`, {
248
+ method: "POST",
249
+ headers: { "Content-Type": "application/json" },
250
+ body: JSON.stringify(query),
251
+ signal: controller.signal
252
+ });
253
+ clearTimeout(timeoutId);
254
+ if (!response.ok) {
255
+ logger.warn("DiffMem query failed", { status: response.status });
256
+ return [];
257
+ }
258
+ const data = await response.json();
259
+ return data.memories || [];
260
+ } catch (error) {
261
+ logger.debug("DiffMem fetch failed", {
262
+ error: error instanceof Error ? error.message : String(error)
263
+ });
264
+ return [];
265
+ }
266
+ }
267
+ /**
268
+ * Sync buffered learnings to DiffMem
269
+ */
270
+ async syncLearnings() {
271
+ if (this.learningBuffer.length === 0) {
272
+ return;
273
+ }
274
+ const controller = new AbortController();
275
+ const timeoutId = setTimeout(() => controller.abort(), 1e4);
276
+ try {
277
+ const response = await fetch(`${this.config.endpoint}/memories/learn`, {
278
+ method: "POST",
279
+ headers: { "Content-Type": "application/json" },
280
+ body: JSON.stringify({ insights: this.learningBuffer }),
281
+ signal: controller.signal
282
+ });
283
+ clearTimeout(timeoutId);
284
+ if (!response.ok) {
285
+ throw new Error(`Sync failed with status ${response.status}`);
286
+ }
287
+ logger.info("Learnings synced to DiffMem", {
288
+ count: this.learningBuffer.length
289
+ });
290
+ } catch (error) {
291
+ clearTimeout(timeoutId);
292
+ throw error;
293
+ }
294
+ }
295
+ /**
296
+ * Inject fetched memories as frame anchors
297
+ */
298
+ async injectAsAnchors(sessionId) {
299
+ if (!this.frameManager || this.fetchedMemories.length === 0) {
300
+ return;
301
+ }
302
+ try {
303
+ const byCategory = /* @__PURE__ */ new Map();
304
+ for (const memory of this.fetchedMemories) {
305
+ if (!byCategory.has(memory.category)) {
306
+ byCategory.set(memory.category, []);
307
+ }
308
+ const categoryList = byCategory.get(memory.category);
309
+ if (categoryList) {
310
+ categoryList.push(memory);
311
+ }
312
+ }
313
+ for (const [category, memories] of byCategory) {
314
+ const highConfidence = memories.filter((m) => m.confidence >= 0.8);
315
+ for (const memory of highConfidence.slice(0, 5)) {
316
+ const anchorType = this.categoryToAnchorType(category);
317
+ const priority = Math.round(memory.confidence * 10);
318
+ this.frameManager.addAnchor(anchorType, memory.content, priority, {
319
+ source: "diffmem",
320
+ category: memory.category,
321
+ memoryId: memory.id,
322
+ confidence: memory.confidence,
323
+ sessionId
324
+ });
325
+ }
326
+ }
327
+ logger.debug("Memories injected as anchors", {
328
+ totalMemories: this.fetchedMemories.length,
329
+ anchorsCreated: Math.min(
330
+ this.fetchedMemories.filter((m) => m.confidence >= 0.8).length,
331
+ 25
332
+ )
333
+ });
334
+ } catch (error) {
335
+ logger.warn("Failed to inject memories as anchors", {
336
+ error: error instanceof Error ? error.message : String(error)
337
+ });
338
+ }
339
+ }
340
+ /**
341
+ * Map memory category to anchor type
342
+ */
343
+ categoryToAnchorType(category) {
344
+ switch (category) {
345
+ case "preference":
346
+ return "CONSTRAINT";
347
+ case "expertise":
348
+ return "FACT";
349
+ case "project_knowledge":
350
+ return "FACT";
351
+ case "pattern":
352
+ return "INTERFACE_CONTRACT";
353
+ case "correction":
354
+ return "DECISION";
355
+ default:
356
+ return "FACT";
357
+ }
358
+ }
359
+ }
360
+ let instance = null;
361
+ function getDiffMemHooks(config) {
362
+ if (!instance) {
363
+ instance = new DiffMemHooks(config);
364
+ } else if (config) {
365
+ instance.updateConfig(config);
366
+ }
367
+ return instance;
368
+ }
369
+ function resetDiffMemHooks() {
370
+ instance = null;
371
+ }
372
+ export {
373
+ DiffMemHooks,
374
+ getDiffMemHooks,
375
+ resetDiffMemHooks
376
+ };
377
+ //# sourceMappingURL=diffmem-hooks.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/hooks/diffmem-hooks.ts"],
4
+ "sourcesContent": ["/**\n * DiffMem Session Hooks\n * Integrates DiffMem user memory with StackMemory session lifecycle\n */\n\nimport { logger } from '../core/monitoring/logger.js';\nimport type { HookEventEmitter, HookEventData } from './events.js';\nimport type { RefactoredFrameManager } from '../core/context/refactored-frame-manager.js';\nimport type {\n UserMemory,\n MemoryQuery,\n LearnedInsight,\n DiffMemStatus,\n} from '../integrations/diffmem/types.js';\n\nexport interface DiffMemHookConfig {\n enabled: boolean;\n endpoint: string;\n autoFetchCategories: string[];\n autoLearnEnabled: boolean;\n learningConfidenceThreshold: number;\n maxMemoriesPerSession: number;\n}\n\ninterface SessionStartEvent extends HookEventData {\n type: 'session_start';\n data: {\n sessionId?: string;\n projectId?: string;\n context?: Record<string, unknown>;\n };\n}\n\ninterface SessionEndEvent extends HookEventData {\n type: 'session_end';\n data: {\n sessionId?: string;\n duration?: number;\n exitCode?: number | null;\n };\n}\n\nconst DEFAULT_CONFIG: DiffMemHookConfig = {\n enabled: !!process.env.DIFFMEM_ENDPOINT,\n endpoint: process.env.DIFFMEM_ENDPOINT || 'http://localhost:3100',\n autoFetchCategories: ['preference', 'expertise', 'pattern'],\n autoLearnEnabled: true,\n learningConfidenceThreshold: 0.7,\n maxMemoriesPerSession: 50,\n};\n\n/**\n * DiffMem Session Hooks\n * Manages user memory fetch on session start and sync on session end\n */\nexport class DiffMemHooks {\n private config: DiffMemHookConfig;\n private fetchedMemories: UserMemory[] = [];\n private learningBuffer: LearnedInsight[] = [];\n private isConnected: boolean = false;\n private frameManager?: RefactoredFrameManager;\n private sessionStartTime: number = 0;\n\n constructor(config: Partial<DiffMemHookConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Register session hooks with the event emitter\n */\n register(\n emitter: HookEventEmitter,\n frameManager?: RefactoredFrameManager\n ): void {\n this.frameManager = frameManager;\n\n if (!this.config.enabled) {\n logger.debug('DiffMem hooks disabled - skipping registration');\n return;\n }\n\n emitter.registerHandler('session_start', this.onSessionStart.bind(this));\n emitter.registerHandler('session_end', this.onSessionEnd.bind(this));\n\n logger.info('DiffMem hooks registered', {\n endpoint: this.config.endpoint,\n autoFetchCategories: this.config.autoFetchCategories,\n autoLearnEnabled: this.config.autoLearnEnabled,\n });\n }\n\n /**\n * Handle session start - fetch user knowledge\n */\n async onSessionStart(event: HookEventData): Promise<void> {\n const sessionEvent = event as SessionStartEvent;\n this.sessionStartTime = Date.now();\n\n try {\n // Check DiffMem connectivity\n const status = await this.checkStatus();\n this.isConnected = status.connected;\n\n if (!this.isConnected) {\n logger.debug('DiffMem not available - skipping memory fetch');\n return;\n }\n\n // Fetch user memories for configured categories\n const query: MemoryQuery = {\n categories: this.config.autoFetchCategories,\n limit: this.config.maxMemoriesPerSession,\n minConfidence: this.config.learningConfidenceThreshold,\n };\n\n this.fetchedMemories = await this.fetchMemories(query);\n\n // Inject as frame anchors if frame manager available\n if (this.frameManager && this.fetchedMemories.length > 0) {\n await this.injectAsAnchors(sessionEvent.data.sessionId);\n }\n\n logger.info('DiffMem session start completed', {\n memoriesFetched: this.fetchedMemories.length,\n sessionId: sessionEvent.data.sessionId,\n });\n } catch (error) {\n // Graceful degradation - log and continue\n logger.warn('DiffMem session start failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n this.isConnected = false;\n }\n }\n\n /**\n * Handle session end - sync buffered learnings\n */\n async onSessionEnd(event: HookEventData): Promise<void> {\n const sessionEvent = event as SessionEndEvent;\n\n if (!this.config.autoLearnEnabled || this.learningBuffer.length === 0) {\n logger.debug('No learnings to sync on session end');\n return;\n }\n\n if (!this.isConnected) {\n // Try to reconnect before giving up\n const status = await this.checkStatus();\n if (!status.connected) {\n logger.warn('DiffMem not available - learnings not synced', {\n bufferedCount: this.learningBuffer.length,\n });\n return;\n }\n this.isConnected = true;\n }\n\n try {\n await this.syncLearnings();\n\n const sessionDuration = Date.now() - this.sessionStartTime;\n logger.info('DiffMem session end completed', {\n learningSynced: this.learningBuffer.length,\n sessionId: sessionEvent.data.sessionId,\n sessionDurationMs: sessionDuration,\n });\n\n // Clear buffer after successful sync\n this.learningBuffer = [];\n } catch (error) {\n logger.warn('DiffMem session end sync failed', {\n error: error instanceof Error ? error.message : String(error),\n bufferedCount: this.learningBuffer.length,\n });\n }\n }\n\n /**\n * Record a learning during session\n */\n recordLearning(insight: Omit<LearnedInsight, 'timestamp'>): void {\n if (!this.config.autoLearnEnabled) {\n return;\n }\n\n // Filter by confidence threshold\n if (insight.confidence < this.config.learningConfidenceThreshold) {\n logger.debug('Learning below confidence threshold', {\n confidence: insight.confidence,\n threshold: this.config.learningConfidenceThreshold,\n });\n return;\n }\n\n const learning: LearnedInsight = {\n ...insight,\n timestamp: Date.now(),\n };\n\n this.learningBuffer.push(learning);\n\n logger.debug('Learning recorded', {\n category: learning.category,\n confidence: learning.confidence,\n bufferSize: this.learningBuffer.length,\n });\n }\n\n /**\n * Get fetched memories\n */\n getUserKnowledge(): UserMemory[] {\n return [...this.fetchedMemories];\n }\n\n /**\n * Format memories for LLM context with token budget\n */\n formatForContext(maxTokens: number = 2000): string {\n if (this.fetchedMemories.length === 0) {\n return '';\n }\n\n // Sort by confidence (highest first) then by timestamp (newest first)\n const sortedMemories = [...this.fetchedMemories].sort((a, b) => {\n if (b.confidence !== a.confidence) {\n return b.confidence - a.confidence;\n }\n return b.timestamp - a.timestamp;\n });\n\n const sections: Map<string, string[]> = new Map();\n\n // Group by category\n for (const memory of sortedMemories) {\n const category = memory.category;\n if (!sections.has(category)) {\n sections.set(category, []);\n }\n const categoryMemories = sections.get(category);\n if (categoryMemories) {\n categoryMemories.push(memory.content);\n }\n }\n\n // Build output with token estimation (~4 chars per token)\n const lines: string[] = ['## User Knowledge'];\n let estimatedTokens = 5; // Header\n\n const categoryLabels: Record<string, string> = {\n preference: 'Preferences',\n expertise: 'Expertise',\n project_knowledge: 'Project Knowledge',\n pattern: 'Patterns',\n correction: 'Corrections',\n };\n\n for (const [category, contents] of sections) {\n const label = categoryLabels[category] || category;\n const categoryHeader = `\\n### ${label}`;\n const headerTokens = Math.ceil(categoryHeader.length / 4);\n\n if (estimatedTokens + headerTokens > maxTokens) {\n break;\n }\n\n lines.push(categoryHeader);\n estimatedTokens += headerTokens;\n\n for (const content of contents) {\n const contentLine = `- ${content}`;\n const contentTokens = Math.ceil(contentLine.length / 4);\n\n if (estimatedTokens + contentTokens > maxTokens) {\n lines.push('- (additional items truncated for token budget)');\n break;\n }\n\n lines.push(contentLine);\n estimatedTokens += contentTokens;\n }\n }\n\n return lines.join('\\n');\n }\n\n /**\n * Get current connection status\n */\n getStatus(): {\n connected: boolean;\n memoriesLoaded: number;\n learningsBuffered: number;\n } {\n return {\n connected: this.isConnected,\n memoriesLoaded: this.fetchedMemories.length,\n learningsBuffered: this.learningBuffer.length,\n };\n }\n\n /**\n * Update configuration\n */\n updateConfig(config: Partial<DiffMemHookConfig>): void {\n this.config = { ...this.config, ...config };\n logger.debug('DiffMem config updated', { config: this.config });\n }\n\n // Private methods\n\n /**\n * Check DiffMem service status\n */\n private async checkStatus(): Promise<DiffMemStatus> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 3000);\n\n const response = await fetch(`${this.config.endpoint}/status`, {\n method: 'GET',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n return { connected: false, memoryCount: 0, lastSync: null };\n }\n\n const status = await response.json();\n return {\n connected: true,\n memoryCount: status.memoryCount || 0,\n lastSync: status.lastSync || null,\n version: status.version,\n };\n } catch {\n return { connected: false, memoryCount: 0, lastSync: null };\n }\n }\n\n /**\n * Fetch memories from DiffMem\n */\n private async fetchMemories(query: MemoryQuery): Promise<UserMemory[]> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 5000);\n\n const response = await fetch(`${this.config.endpoint}/memories/query`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(query),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n logger.warn('DiffMem query failed', { status: response.status });\n return [];\n }\n\n const data = await response.json();\n return data.memories || [];\n } catch (error) {\n logger.debug('DiffMem fetch failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n return [];\n }\n }\n\n /**\n * Sync buffered learnings to DiffMem\n */\n private async syncLearnings(): Promise<void> {\n if (this.learningBuffer.length === 0) {\n return;\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n try {\n const response = await fetch(`${this.config.endpoint}/memories/learn`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ insights: this.learningBuffer }),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`Sync failed with status ${response.status}`);\n }\n\n logger.info('Learnings synced to DiffMem', {\n count: this.learningBuffer.length,\n });\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n }\n\n /**\n * Inject fetched memories as frame anchors\n */\n private async injectAsAnchors(sessionId?: string): Promise<void> {\n if (!this.frameManager || this.fetchedMemories.length === 0) {\n return;\n }\n\n try {\n // Group by category for efficient anchor creation\n const byCategory = new Map<string, UserMemory[]>();\n for (const memory of this.fetchedMemories) {\n if (!byCategory.has(memory.category)) {\n byCategory.set(memory.category, []);\n }\n const categoryList = byCategory.get(memory.category);\n if (categoryList) {\n categoryList.push(memory);\n }\n }\n\n // Create anchors for high-confidence memories\n for (const [category, memories] of byCategory) {\n const highConfidence = memories.filter((m) => m.confidence >= 0.8);\n\n for (const memory of highConfidence.slice(0, 5)) {\n const anchorType = this.categoryToAnchorType(category);\n const priority = Math.round(memory.confidence * 10);\n\n this.frameManager.addAnchor(anchorType, memory.content, priority, {\n source: 'diffmem',\n category: memory.category,\n memoryId: memory.id,\n confidence: memory.confidence,\n sessionId,\n });\n }\n }\n\n logger.debug('Memories injected as anchors', {\n totalMemories: this.fetchedMemories.length,\n anchorsCreated: Math.min(\n this.fetchedMemories.filter((m) => m.confidence >= 0.8).length,\n 25\n ),\n });\n } catch (error) {\n logger.warn('Failed to inject memories as anchors', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n /**\n * Map memory category to anchor type\n */\n private categoryToAnchorType(\n category: string\n ):\n | 'FACT'\n | 'DECISION'\n | 'CONSTRAINT'\n | 'INTERFACE_CONTRACT'\n | 'TODO'\n | 'RISK' {\n switch (category) {\n case 'preference':\n return 'CONSTRAINT';\n case 'expertise':\n return 'FACT';\n case 'project_knowledge':\n return 'FACT';\n case 'pattern':\n return 'INTERFACE_CONTRACT';\n case 'correction':\n return 'DECISION';\n default:\n return 'FACT';\n }\n }\n}\n\n// Singleton instance\nlet instance: DiffMemHooks | null = null;\n\n/**\n * Get the singleton DiffMemHooks instance\n */\nexport function getDiffMemHooks(\n config?: Partial<DiffMemHookConfig>\n): DiffMemHooks {\n if (!instance) {\n instance = new DiffMemHooks(config);\n } else if (config) {\n instance.updateConfig(config);\n }\n return instance;\n}\n\n/**\n * Reset the singleton (for testing)\n */\nexport function resetDiffMemHooks(): void {\n instance = null;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,209 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { DEFAULT_DIFFMEM_CONFIG } from "./config.js";
6
+ class DiffMemClientError extends Error {
7
+ constructor(message, code, statusCode) {
8
+ super(message);
9
+ this.code = code;
10
+ this.statusCode = statusCode;
11
+ this.name = "DiffMemClientError";
12
+ }
13
+ }
14
+ class DiffMemClient {
15
+ endpoint;
16
+ userId;
17
+ timeout;
18
+ maxRetries;
19
+ constructor(config = {}) {
20
+ const mergedConfig = { ...DEFAULT_DIFFMEM_CONFIG, ...config };
21
+ this.endpoint = mergedConfig.endpoint.replace(/\/$/, "");
22
+ this.userId = mergedConfig.userId;
23
+ this.timeout = mergedConfig.timeout;
24
+ this.maxRetries = mergedConfig.maxRetries;
25
+ }
26
+ async request(path, options = {}) {
27
+ const controller = new AbortController();
28
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
29
+ let lastError;
30
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
31
+ try {
32
+ const response = await fetch(`${this.endpoint}${path}`, {
33
+ ...options,
34
+ signal: controller.signal,
35
+ headers: {
36
+ "Content-Type": "application/json",
37
+ ...options.headers
38
+ }
39
+ });
40
+ clearTimeout(timeoutId);
41
+ if (!response.ok) {
42
+ const errorBody = await response.text().catch(() => "");
43
+ throw new DiffMemClientError(
44
+ `Request failed: ${response.statusText}`,
45
+ "HTTP_ERROR",
46
+ response.status
47
+ );
48
+ }
49
+ return await response.json();
50
+ } catch (error) {
51
+ lastError = error;
52
+ if (error instanceof DiffMemClientError) {
53
+ throw error;
54
+ }
55
+ if (error.name === "AbortError") {
56
+ throw new DiffMemClientError("Request timeout", "TIMEOUT");
57
+ }
58
+ if (attempt < this.maxRetries) {
59
+ await new Promise(
60
+ (resolve) => setTimeout(resolve, Math.pow(2, attempt) * 100)
61
+ );
62
+ continue;
63
+ }
64
+ }
65
+ }
66
+ clearTimeout(timeoutId);
67
+ throw new DiffMemClientError(
68
+ lastError?.message || "Request failed after retries",
69
+ "NETWORK_ERROR"
70
+ );
71
+ }
72
+ /**
73
+ * Get user context/memories from DiffMem
74
+ * Maps to POST /memory/{user_id}/context
75
+ */
76
+ async getMemories(query = {}) {
77
+ try {
78
+ const conversation = query.query ? [{ role: "user", content: query.query }] : [{ role: "user", content: "What do you know about me?" }];
79
+ const response = await this.request(`/memory/${this.userId}/context`, {
80
+ method: "POST",
81
+ body: JSON.stringify({
82
+ conversation,
83
+ depth: "wide"
84
+ })
85
+ });
86
+ if (response.entities) {
87
+ return response.entities.slice(0, query.limit || 10).map((entity) => ({
88
+ id: entity.id,
89
+ content: entity.content,
90
+ category: "project_knowledge",
91
+ confidence: entity.score || 0.7,
92
+ timestamp: Date.now()
93
+ }));
94
+ }
95
+ return [];
96
+ } catch {
97
+ return [];
98
+ }
99
+ }
100
+ /**
101
+ * Store an insight/learning in DiffMem
102
+ * Maps to POST /memory/{user_id}/process-and-commit
103
+ */
104
+ async storeInsight(insight) {
105
+ const response = await this.request(`/memory/${this.userId}/process-and-commit`, {
106
+ method: "POST",
107
+ body: JSON.stringify({
108
+ memory_input: `[${insight.category}] ${insight.content}`,
109
+ session_id: `sm-${Date.now()}`,
110
+ session_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
111
+ })
112
+ });
113
+ return { id: response.session_id || `insight-${Date.now()}` };
114
+ }
115
+ /**
116
+ * Search memories in DiffMem
117
+ * Maps to POST /memory/{user_id}/search
118
+ */
119
+ async search(query) {
120
+ try {
121
+ const response = await this.request(`/memory/${this.userId}/search`, {
122
+ method: "POST",
123
+ body: JSON.stringify({
124
+ query: query.query || "",
125
+ k: query.limit || 10
126
+ })
127
+ });
128
+ if (response.results) {
129
+ return response.results.map((result) => ({
130
+ id: result.snippet.id,
131
+ content: result.snippet.content,
132
+ category: "project_knowledge",
133
+ confidence: result.score,
134
+ timestamp: Date.now(),
135
+ metadata: { filePath: result.snippet.file_path }
136
+ }));
137
+ }
138
+ return [];
139
+ } catch {
140
+ return [];
141
+ }
142
+ }
143
+ /**
144
+ * Get DiffMem server status
145
+ * Maps to GET /health
146
+ */
147
+ async getStatus() {
148
+ try {
149
+ const health = await this.request("/health", { method: "GET" });
150
+ return {
151
+ connected: health.status === "healthy",
152
+ memoryCount: health.active_contexts || 0,
153
+ lastSync: Date.now(),
154
+ version: health.version
155
+ };
156
+ } catch {
157
+ return {
158
+ connected: false,
159
+ memoryCount: 0,
160
+ lastSync: null
161
+ };
162
+ }
163
+ }
164
+ /**
165
+ * Batch sync multiple insights
166
+ * Processes each insight individually since DiffMem doesn't have batch API
167
+ */
168
+ async batchSync(insights) {
169
+ if (insights.length === 0) {
170
+ return { synced: 0, failed: 0 };
171
+ }
172
+ let synced = 0;
173
+ let failed = 0;
174
+ for (const insight of insights) {
175
+ try {
176
+ await this.storeInsight(insight);
177
+ synced++;
178
+ } catch {
179
+ failed++;
180
+ }
181
+ }
182
+ return { synced, failed };
183
+ }
184
+ /**
185
+ * Onboard a new user in DiffMem
186
+ */
187
+ async onboardUser(userInfo) {
188
+ try {
189
+ const response = await this.request(
190
+ `/memory/${this.userId}/onboard`,
191
+ {
192
+ method: "POST",
193
+ body: JSON.stringify({
194
+ user_info: userInfo,
195
+ session_id: `onboard-${Date.now()}`
196
+ })
197
+ }
198
+ );
199
+ return { success: response.status === "success" };
200
+ } catch {
201
+ return { success: false };
202
+ }
203
+ }
204
+ }
205
+ export {
206
+ DiffMemClient,
207
+ DiffMemClientError
208
+ };
209
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/integrations/diffmem/client.ts"],
4
+ "sourcesContent": ["/**\n * DiffMem API Client\n * Handles communication with the DiffMem memory service\n */\n\nimport type {\n UserMemory,\n MemoryQuery,\n LearnedInsight,\n DiffMemStatus,\n} from './types.js';\nimport type { DiffMemIntegrationConfig } from './config.js';\nimport { DEFAULT_DIFFMEM_CONFIG } from './config.js';\n\nexport class DiffMemClientError extends Error {\n constructor(\n message: string,\n public readonly code?: string,\n public readonly statusCode?: number\n ) {\n super(message);\n this.name = 'DiffMemClientError';\n }\n}\n\nexport class DiffMemClient {\n private readonly endpoint: string;\n private readonly userId: string;\n private readonly timeout: number;\n private readonly maxRetries: number;\n\n constructor(config: Partial<DiffMemIntegrationConfig> = {}) {\n const mergedConfig = { ...DEFAULT_DIFFMEM_CONFIG, ...config };\n this.endpoint = mergedConfig.endpoint.replace(/\\/$/, '');\n this.userId = mergedConfig.userId;\n this.timeout = mergedConfig.timeout;\n this.maxRetries = mergedConfig.maxRetries;\n }\n\n private async request<T>(\n path: string,\n options: RequestInit = {}\n ): Promise<T> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const response = await fetch(`${this.endpoint}${path}`, {\n ...options,\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new DiffMemClientError(\n `Request failed: ${response.statusText}`,\n 'HTTP_ERROR',\n response.status\n );\n }\n\n return (await response.json()) as T;\n } catch (error) {\n lastError = error as Error;\n\n if (error instanceof DiffMemClientError) {\n throw error;\n }\n\n if ((error as Error).name === 'AbortError') {\n throw new DiffMemClientError('Request timeout', 'TIMEOUT');\n }\n\n // Retry on network errors\n if (attempt < this.maxRetries) {\n await new Promise((resolve) =>\n setTimeout(resolve, Math.pow(2, attempt) * 100)\n );\n continue;\n }\n }\n }\n\n clearTimeout(timeoutId);\n throw new DiffMemClientError(\n lastError?.message || 'Request failed after retries',\n 'NETWORK_ERROR'\n );\n }\n\n /**\n * Get user context/memories from DiffMem\n * Maps to POST /memory/{user_id}/context\n */\n async getMemories(query: MemoryQuery = {}): Promise<UserMemory[]> {\n try {\n // DiffMem uses conversation-based context retrieval\n const conversation = query.query\n ? [{ role: 'user', content: query.query }]\n : [{ role: 'user', content: 'What do you know about me?' }];\n\n const response = await this.request<{\n status: string;\n context?: string;\n entities?: Array<{ id: string; content: string; score?: number }>;\n }>(`/memory/${this.userId}/context`, {\n method: 'POST',\n body: JSON.stringify({\n conversation,\n depth: 'wide',\n }),\n });\n\n // Transform DiffMem response to UserMemory format\n if (response.entities) {\n return response.entities.slice(0, query.limit || 10).map((entity) => ({\n id: entity.id,\n content: entity.content,\n category: 'project_knowledge' as const,\n confidence: entity.score || 0.7,\n timestamp: Date.now(),\n }));\n }\n\n return [];\n } catch {\n return [];\n }\n }\n\n /**\n * Store an insight/learning in DiffMem\n * Maps to POST /memory/{user_id}/process-and-commit\n */\n async storeInsight(insight: LearnedInsight): Promise<{ id: string }> {\n const response = await this.request<{\n status: string;\n session_id?: string;\n }>(`/memory/${this.userId}/process-and-commit`, {\n method: 'POST',\n body: JSON.stringify({\n memory_input: `[${insight.category}] ${insight.content}`,\n session_id: `sm-${Date.now()}`,\n session_date: new Date().toISOString().split('T')[0],\n }),\n });\n\n return { id: response.session_id || `insight-${Date.now()}` };\n }\n\n /**\n * Search memories in DiffMem\n * Maps to POST /memory/{user_id}/search\n */\n async search(query: MemoryQuery): Promise<UserMemory[]> {\n try {\n const response = await this.request<{\n status: string;\n results?: Array<{\n score: number;\n snippet: { id: string; content: string; file_path?: string };\n }>;\n }>(`/memory/${this.userId}/search`, {\n method: 'POST',\n body: JSON.stringify({\n query: query.query || '',\n k: query.limit || 10,\n }),\n });\n\n if (response.results) {\n return response.results.map((result) => ({\n id: result.snippet.id,\n content: result.snippet.content,\n category: 'project_knowledge' as const,\n confidence: result.score,\n timestamp: Date.now(),\n metadata: { filePath: result.snippet.file_path },\n }));\n }\n\n return [];\n } catch {\n return [];\n }\n }\n\n /**\n * Get DiffMem server status\n * Maps to GET /health\n */\n async getStatus(): Promise<DiffMemStatus> {\n try {\n const health = await this.request<{\n status: string;\n active_contexts?: number;\n version?: string;\n }>('/health', { method: 'GET' });\n\n return {\n connected: health.status === 'healthy',\n memoryCount: health.active_contexts || 0,\n lastSync: Date.now(),\n version: health.version,\n };\n } catch {\n return {\n connected: false,\n memoryCount: 0,\n lastSync: null,\n };\n }\n }\n\n /**\n * Batch sync multiple insights\n * Processes each insight individually since DiffMem doesn't have batch API\n */\n async batchSync(\n insights: LearnedInsight[]\n ): Promise<{ synced: number; failed: number }> {\n if (insights.length === 0) {\n return { synced: 0, failed: 0 };\n }\n\n let synced = 0;\n let failed = 0;\n\n for (const insight of insights) {\n try {\n await this.storeInsight(insight);\n synced++;\n } catch {\n failed++;\n }\n }\n\n return { synced, failed };\n }\n\n /**\n * Onboard a new user in DiffMem\n */\n async onboardUser(userInfo: string): Promise<{ success: boolean }> {\n try {\n const response = await this.request<{ status: string }>(\n `/memory/${this.userId}/onboard`,\n {\n method: 'POST',\n body: JSON.stringify({\n user_info: userInfo,\n session_id: `onboard-${Date.now()}`,\n }),\n }\n );\n\n return { success: response.status === 'success' };\n } catch {\n return { success: false };\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,15 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ const DEFAULT_DIFFMEM_CONFIG = {
6
+ endpoint: process.env.DIFFMEM_ENDPOINT || "http://localhost:8000",
7
+ userId: process.env.DIFFMEM_USER_ID || "default",
8
+ timeout: 5e3,
9
+ maxRetries: 3,
10
+ enabled: process.env.DIFFMEM_ENABLED === "true" || !!process.env.DIFFMEM_ENDPOINT
11
+ };
12
+ export {
13
+ DEFAULT_DIFFMEM_CONFIG
14
+ };
15
+ //# sourceMappingURL=config.js.map