@veedubin/boomerang-v3 0.1.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 (53) hide show
  1. package/.github/workflows/npm-publish.yml +58 -0
  2. package/.opencode/skills/boomerang-agent-builder/SKILL.md +226 -0
  3. package/.opencode/skills/boomerang-architect/SKILL.md +252 -0
  4. package/.opencode/skills/boomerang-coder/SKILL.md +283 -0
  5. package/.opencode/skills/boomerang-explorer/SKILL.md +58 -0
  6. package/.opencode/skills/boomerang-git/SKILL.md +115 -0
  7. package/.opencode/skills/boomerang-handoff/SKILL.md +209 -0
  8. package/.opencode/skills/boomerang-init/SKILL.md +117 -0
  9. package/.opencode/skills/boomerang-linter/SKILL.md +92 -0
  10. package/.opencode/skills/boomerang-orchestrator/SKILL.md +401 -0
  11. package/.opencode/skills/boomerang-release/SKILL.md +116 -0
  12. package/.opencode/skills/boomerang-scraper/SKILL.md +105 -0
  13. package/.opencode/skills/boomerang-tester/SKILL.md +107 -0
  14. package/.opencode/skills/boomerang-writer/SKILL.md +93 -0
  15. package/.opencode/skills/mcp-specialist/SKILL.md +130 -0
  16. package/.opencode/skills/researcher/SKILL.md +118 -0
  17. package/AGENTS.md +333 -0
  18. package/README.md +305 -0
  19. package/dist/index.js +13 -0
  20. package/dist/memini-client/index.js +560 -0
  21. package/dist/memini-client/schema.js +13 -0
  22. package/dist/memory/contradictions.js +119 -0
  23. package/dist/memory/graph.js +86 -0
  24. package/dist/memory/index.js +314 -0
  25. package/dist/memory/kg.js +111 -0
  26. package/dist/memory/schema.js +10 -0
  27. package/dist/memory/tiered.js +104 -0
  28. package/dist/memory/trust.js +148 -0
  29. package/dist/protocol/types.js +6 -0
  30. package/package.json +41 -0
  31. package/packages/opencode-plugin/src/asset-loader.ts +201 -0
  32. package/packages/opencode-plugin/src/git.ts +77 -0
  33. package/packages/opencode-plugin/src/index.ts +346 -0
  34. package/packages/opencode-plugin/src/memory.ts +109 -0
  35. package/packages/opencode-plugin/src/orchestrator.ts +263 -0
  36. package/packages/opencode-plugin/src/quality-gates.ts +75 -0
  37. package/packages/opencode-plugin/src/types.ts +141 -0
  38. package/src/index.ts +16 -0
  39. package/src/memini-client/index.ts +762 -0
  40. package/src/memini-client/schema.ts +60 -0
  41. package/src/memory/contradictions.ts +164 -0
  42. package/src/memory/graph.ts +116 -0
  43. package/src/memory/index.ts +422 -0
  44. package/src/memory/kg.ts +166 -0
  45. package/src/memory/schema.ts +274 -0
  46. package/src/memory/tiered.ts +133 -0
  47. package/src/memory/trust.ts +218 -0
  48. package/src/protocol/types.ts +79 -0
  49. package/tests/index.test.ts +58 -0
  50. package/tests/memini-client.test.ts +321 -0
  51. package/tests/memory/index.test.ts +214 -0
  52. package/tsconfig.json +17 -0
  53. package/vitest.config.ts +19 -0
@@ -0,0 +1,560 @@
1
+ /**
2
+ * Memini Client - TypeScript wrapper for memini-ai MCP server
3
+ *
4
+ * Communicates with the Python memini-ai FastMCP server via stdio.
5
+ * Provides the same interface as Super-Memory-TS MemorySystem.
6
+ *
7
+ * Uses persistent process architecture: ONE server process for all tool calls,
8
+ * with proper MCP JSON-RPC message framing over stdio.
9
+ */
10
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
11
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
12
+ // ============================================================================
13
+ // MeminiClient - Persistent MCP stdio Client
14
+ // ============================================================================
15
+ /**
16
+ * Convert memini MemoryEntry to plugin MemoryEntry
17
+ */
18
+ function toMemoryEntry(meminiEntry) {
19
+ return {
20
+ id: meminiEntry.id,
21
+ text: meminiEntry.text,
22
+ sourceType: meminiEntry.sourceType,
23
+ sourcePath: meminiEntry.sourcePath || '',
24
+ timestamp: new Date(meminiEntry.timestamp).getTime(),
25
+ metadataJson: meminiEntry.metadataJson || '{}',
26
+ sessionId: meminiEntry.sessionId,
27
+ score: meminiEntry.score,
28
+ trustScore: meminiEntry.trustScore,
29
+ };
30
+ }
31
+ /**
32
+ * Parse tool result content - FastMCP returns content array with text
33
+ */
34
+ function parseToolResult(result) {
35
+ const res = result;
36
+ if (Array.isArray(res.content)) {
37
+ const textContent = res.content.find((c) => c.type === 'text');
38
+ if (textContent && typeof textContent.text === 'string') {
39
+ try {
40
+ return JSON.parse(textContent.text);
41
+ }
42
+ catch {
43
+ return textContent.text;
44
+ }
45
+ }
46
+ }
47
+ return result;
48
+ }
49
+ /**
50
+ * MeminiClient - wraps memini-ai MCP server with MemorySystem-compatible API
51
+ *
52
+ * Uses persistent process architecture with proper MCP stdio transport.
53
+ */
54
+ export class MeminiClient {
55
+ static instance = null;
56
+ _initialized = false;
57
+ _serverUrl = 'http://localhost:6333';
58
+ _mcpClient = null;
59
+ _transport = null;
60
+ constructor() { }
61
+ static getInstance() {
62
+ if (!MeminiClient.instance) {
63
+ MeminiClient.instance = new MeminiClient();
64
+ }
65
+ return MeminiClient.instance;
66
+ }
67
+ isInitialized() {
68
+ return this._initialized;
69
+ }
70
+ async initialize(serverUrl) {
71
+ if (this._initialized) {
72
+ return;
73
+ }
74
+ if (serverUrl) {
75
+ this._serverUrl = serverUrl;
76
+ }
77
+ try {
78
+ // Create stdio transport - SDK handles process spawning
79
+ this._transport = new StdioClientTransport({
80
+ command: 'python',
81
+ args: ['-m', 'memini_ai.server'],
82
+ env: { ...process.env, PYTHONUNBUFFERED: '1' },
83
+ });
84
+ // Create MCP client
85
+ this._mcpClient = new Client({
86
+ name: 'memini-ai-client',
87
+ version: '1.0.0',
88
+ }, {
89
+ capabilities: {},
90
+ });
91
+ // Connect client to transport
92
+ await this._mcpClient.connect(this._transport);
93
+ console.log('memini-ai MCP client connected');
94
+ // Verify connection with get_status
95
+ const status = await this._mcpClient.callTool({ name: 'get_status' });
96
+ if (status) {
97
+ console.log('memini-ai server initialized:', status);
98
+ }
99
+ this._initialized = true;
100
+ }
101
+ catch (err) {
102
+ console.warn('memini-ai server not available, operating in degraded mode:', err);
103
+ // Graceful degradation - mark as initialized to allow other operations
104
+ this._initialized = true;
105
+ }
106
+ }
107
+ /**
108
+ * Ensure MCP client is connected
109
+ */
110
+ ensureConnected() {
111
+ if (!this._mcpClient) {
112
+ throw new Error('MeminiClient not connected. Call initialize() first.');
113
+ }
114
+ }
115
+ /**
116
+ * Call MCP tool with arguments
117
+ */
118
+ async callTool(toolName, args) {
119
+ const result = await this._mcpClient.callTool({ name: toolName, arguments: args });
120
+ return parseToolResult(result);
121
+ }
122
+ async search(query, options) {
123
+ this.ensureConnected();
124
+ const topK = options?.topK || 10;
125
+ const strategy = options?.strategy || 'tiered';
126
+ try {
127
+ const result = await this.callTool('query_memories', {
128
+ query,
129
+ limit: topK,
130
+ strategy,
131
+ });
132
+ return result.memories.map((mem) => ({
133
+ entry: toMemoryEntry(mem),
134
+ score: mem.score || 0,
135
+ }));
136
+ }
137
+ catch (err) {
138
+ console.error('memini-ai query_memories failed:', err);
139
+ return [];
140
+ }
141
+ }
142
+ async addMemory(entry) {
143
+ this.ensureConnected();
144
+ const input = {
145
+ content: entry.text || '',
146
+ sourceType: entry.sourceType || 'boomerang',
147
+ sourcePath: entry.sourcePath || '',
148
+ metadata: entry.metadataJson ? JSON.parse(entry.metadataJson) : {},
149
+ sessionId: entry.sessionId || '',
150
+ };
151
+ try {
152
+ const result = await this.callTool('add_memory', input);
153
+ // Get the created memory
154
+ const memories = await this.search(input.content, { topK: 1 });
155
+ if (memories.length > 0) {
156
+ return memories[0].entry;
157
+ }
158
+ // Fallback: construct from input
159
+ return {
160
+ id: result.id,
161
+ text: input.content,
162
+ sourceType: input.sourceType,
163
+ sourcePath: input.sourcePath,
164
+ timestamp: Date.now(),
165
+ metadataJson: JSON.stringify(input.metadata),
166
+ sessionId: input.sessionId,
167
+ };
168
+ }
169
+ catch (err) {
170
+ console.error('memini-ai add_memory failed:', err);
171
+ throw err;
172
+ }
173
+ }
174
+ async saveContext(sessionId, context) {
175
+ return this.addMemory({
176
+ text: context,
177
+ sourceType: 'session',
178
+ sourcePath: `session://${sessionId}`,
179
+ metadataJson: JSON.stringify({ type: 'context' }),
180
+ sessionId,
181
+ });
182
+ }
183
+ async getTrustScore(memoryId) {
184
+ try {
185
+ this.ensureConnected();
186
+ const result = await this.callTool('get_trust_score', { memory_id: memoryId });
187
+ return result?.trustScore ?? null;
188
+ }
189
+ catch {
190
+ return null;
191
+ }
192
+ }
193
+ async adjustTrust(memoryId, signal) {
194
+ try {
195
+ this.ensureConnected();
196
+ await this.callTool('adjust_trust', { memory_id: memoryId, signal });
197
+ }
198
+ catch (err) {
199
+ console.error('memini-ai adjust_trust failed:', err);
200
+ }
201
+ }
202
+ // =========================================================================
203
+ // Contradiction Methods
204
+ // =========================================================================
205
+ async findContradictions(query, limit = 10) {
206
+ try {
207
+ this.ensureConnected();
208
+ const result = await this.callTool('find_contradictions', {
209
+ query: query || '',
210
+ limit,
211
+ });
212
+ return result.contradictions || [];
213
+ }
214
+ catch (err) {
215
+ console.error('memini-ai find_contradictions failed:', err);
216
+ return [];
217
+ }
218
+ }
219
+ async resolveContradiction(memoryIdA, memoryIdB) {
220
+ try {
221
+ this.ensureConnected();
222
+ const result = await this.callTool('resolve_contradiction', {
223
+ memory_id_a: memoryIdA,
224
+ memory_id_b: memoryIdB,
225
+ });
226
+ return result.resolution;
227
+ }
228
+ catch (err) {
229
+ console.error('memini-ai resolve_contradiction failed:', err);
230
+ return null;
231
+ }
232
+ }
233
+ async getDialecticHistory(memoryId) {
234
+ try {
235
+ this.ensureConnected();
236
+ const result = await this.callTool('get_dialectic_history', {
237
+ memory_id: memoryId,
238
+ });
239
+ return result.history || [];
240
+ }
241
+ catch (err) {
242
+ console.error('memini-ai get_dialectic_history failed:', err);
243
+ return [];
244
+ }
245
+ }
246
+ async challengeMemory(memoryId, challengeText) {
247
+ try {
248
+ this.ensureConnected();
249
+ const result = await this.callTool('challenge_memory', {
250
+ memory_id: memoryId,
251
+ challenge_text: challengeText,
252
+ });
253
+ return result.challenge;
254
+ }
255
+ catch (err) {
256
+ console.error('memini-ai challenge_memory failed:', err);
257
+ return null;
258
+ }
259
+ }
260
+ // =========================================================================
261
+ // Relationship Methods
262
+ // =========================================================================
263
+ async findRelatedMemories(memoryId, relationshipType, limit = 10) {
264
+ try {
265
+ this.ensureConnected();
266
+ const result = await this.callTool('find_related_memories', {
267
+ memoryId,
268
+ relationshipType: relationshipType || null,
269
+ limit,
270
+ });
271
+ return (result.memories || []).map(toMemoryEntry);
272
+ }
273
+ catch (err) {
274
+ console.error('memini-ai find_related_memories failed:', err);
275
+ return [];
276
+ }
277
+ }
278
+ async createRelationship(sourceId, targetId, relationshipType, confidence = 1.0) {
279
+ try {
280
+ this.ensureConnected();
281
+ await this.callTool('create_relationship', {
282
+ sourceId,
283
+ targetId,
284
+ relationshipType,
285
+ confidence,
286
+ });
287
+ }
288
+ catch (err) {
289
+ console.error('memini-ai create_relationship failed:', err);
290
+ }
291
+ }
292
+ async getRelationshipSummary(memoryId) {
293
+ try {
294
+ this.ensureConnected();
295
+ const result = await this.callTool('get_relationship_summary', {
296
+ memoryId,
297
+ });
298
+ return result.summary;
299
+ }
300
+ catch (err) {
301
+ console.error('memini-ai get_relationship_summary failed:', err);
302
+ return null;
303
+ }
304
+ }
305
+ // =========================================================================
306
+ // Knowledge Graph Methods
307
+ // =========================================================================
308
+ async queryKG(query) {
309
+ try {
310
+ this.ensureConnected();
311
+ const result = await this.callTool('query_kg', {
312
+ query: JSON.stringify(query),
313
+ });
314
+ return result.result;
315
+ }
316
+ catch (err) {
317
+ console.error('memini-ai query_kg failed:', err);
318
+ return null;
319
+ }
320
+ }
321
+ async extractEntities(memoryId) {
322
+ try {
323
+ this.ensureConnected();
324
+ const result = await this.callTool('extract_entities', {
325
+ memory_id: memoryId,
326
+ });
327
+ return result.entities || [];
328
+ }
329
+ catch (err) {
330
+ console.error('memini-ai extract_entities failed:', err);
331
+ return [];
332
+ }
333
+ }
334
+ async getEntityGraph(entityId, depth = 1) {
335
+ try {
336
+ this.ensureConnected();
337
+ const result = await this.callTool('get_entity_graph', {
338
+ entity_id: entityId,
339
+ depth,
340
+ });
341
+ return result.graph;
342
+ }
343
+ catch (err) {
344
+ console.error('memini-ai get_entity_graph failed:', err);
345
+ return null;
346
+ }
347
+ }
348
+ async getInferenceChain(startEntity, endEntity, maxDepth = 3) {
349
+ try {
350
+ this.ensureConnected();
351
+ const result = await this.callTool('get_inference_chain', {
352
+ start_entity: startEntity,
353
+ end_entity: endEntity,
354
+ max_depth: maxDepth,
355
+ });
356
+ return result.paths || [];
357
+ }
358
+ catch (err) {
359
+ console.error('memini-ai get_inference_chain failed:', err);
360
+ return [];
361
+ }
362
+ }
363
+ async searchEntities(name, limit = 10) {
364
+ try {
365
+ this.ensureConnected();
366
+ const result = await this.callTool('search_entities', {
367
+ name,
368
+ limit,
369
+ });
370
+ return result.entities || [];
371
+ }
372
+ catch (err) {
373
+ console.error('memini-ai search_entities failed:', err);
374
+ return [];
375
+ }
376
+ }
377
+ async getGraphVisualization(limit = 100) {
378
+ try {
379
+ this.ensureConnected();
380
+ const result = await this.callTool('get_graph_visualization', {
381
+ limit,
382
+ });
383
+ return result.html || '';
384
+ }
385
+ catch (err) {
386
+ console.error('memini-ai get_graph_visualization failed:', err);
387
+ return '';
388
+ }
389
+ }
390
+ // =========================================================================
391
+ // Tiered Memory Methods
392
+ // =========================================================================
393
+ async getTier0Summary(forceRefresh = false) {
394
+ try {
395
+ this.ensureConnected();
396
+ const result = await this.callTool('get_tier0_summary', {
397
+ force_refresh: forceRefresh,
398
+ });
399
+ return result.summary;
400
+ }
401
+ catch (err) {
402
+ console.error('memini-ai get_tier0_summary failed:', err);
403
+ return null;
404
+ }
405
+ }
406
+ async getTier1Summary(forceRefresh = false) {
407
+ try {
408
+ this.ensureConnected();
409
+ const result = await this.callTool('get_tier1_summary', {
410
+ force_refresh: forceRefresh,
411
+ });
412
+ return result.summary;
413
+ }
414
+ catch (err) {
415
+ console.error('memini-ai get_tier1_summary failed:', err);
416
+ return null;
417
+ }
418
+ }
419
+ async triggerExtraction(conversation) {
420
+ try {
421
+ this.ensureConnected();
422
+ const result = await this.callTool('trigger_extraction', {
423
+ conversation: conversation || null,
424
+ });
425
+ return result.memory_ids || [];
426
+ }
427
+ catch (err) {
428
+ console.error('memini-ai trigger_extraction failed:', err);
429
+ return [];
430
+ }
431
+ }
432
+ async preconpressExtraction(contextContent) {
433
+ try {
434
+ this.ensureConnected();
435
+ const result = await this.callTool('preconpress_extraction', {
436
+ context_content: contextContent || null,
437
+ });
438
+ return result.result;
439
+ }
440
+ catch (err) {
441
+ console.error('memini-ai preconpress_extraction failed:', err);
442
+ return null;
443
+ }
444
+ }
445
+ // =========================================================================
446
+ // Trust/Decay Methods
447
+ // =========================================================================
448
+ async listArchived(limit = 50, offset = 0) {
449
+ try {
450
+ this.ensureConnected();
451
+ const result = await this.callTool('list_archived', {
452
+ limit,
453
+ offset,
454
+ });
455
+ return (result.memories || []).map(toMemoryEntry);
456
+ }
457
+ catch (err) {
458
+ console.error('memini-ai list_archived failed:', err);
459
+ return [];
460
+ }
461
+ }
462
+ async getDecayStatus() {
463
+ try {
464
+ this.ensureConnected();
465
+ const result = await this.callTool('get_decay_status', {});
466
+ return result.status;
467
+ }
468
+ catch (err) {
469
+ console.error('memini-ai get_decay_status failed:', err);
470
+ return null;
471
+ }
472
+ }
473
+ async listFadingMemories(limit = 20) {
474
+ try {
475
+ this.ensureConnected();
476
+ const result = await this.callTool('list_fading_memories', {
477
+ limit,
478
+ });
479
+ return (result.memories || []).map(toMemoryEntry);
480
+ }
481
+ catch (err) {
482
+ console.error('memini-ai list_fading_memories failed:', err);
483
+ return [];
484
+ }
485
+ }
486
+ async adjustDecayRate(memoryId, decayRate) {
487
+ try {
488
+ this.ensureConnected();
489
+ await this.callTool('adjust_decay_rate', {
490
+ memory_id: memoryId,
491
+ decay_rate: decayRate,
492
+ });
493
+ }
494
+ catch (err) {
495
+ console.error('memini-ai adjust_decay_rate failed:', err);
496
+ }
497
+ }
498
+ async triggerConsolidation(force = false) {
499
+ try {
500
+ this.ensureConnected();
501
+ const result = await this.callTool('trigger_consolidation', {
502
+ force,
503
+ });
504
+ return result.result;
505
+ }
506
+ catch (err) {
507
+ console.error('memini-ai trigger_consolidation failed:', err);
508
+ return null;
509
+ }
510
+ }
511
+ // =========================================================================
512
+ // Server Status / Query Aliases
513
+ // =========================================================================
514
+ async getStatus() {
515
+ try {
516
+ this.ensureConnected();
517
+ return await this.callTool('get_status', {});
518
+ }
519
+ catch (err) {
520
+ console.error('memini-ai get_status failed:', err);
521
+ return null;
522
+ }
523
+ }
524
+ async queryMemories(query, options) {
525
+ return this.search(query, options);
526
+ }
527
+ /**
528
+ * Shutdown the client and close the transport
529
+ */
530
+ async shutdown() {
531
+ if (this._mcpClient) {
532
+ await this._mcpClient.close();
533
+ this._mcpClient = null;
534
+ }
535
+ if (this._transport) {
536
+ await this._transport.close();
537
+ this._transport = null;
538
+ }
539
+ this._initialized = false;
540
+ }
541
+ ensureInitialized() {
542
+ if (!this._initialized) {
543
+ throw new Error('MeminiClient not initialized. Call initialize() first.');
544
+ }
545
+ }
546
+ }
547
+ // Singleton accessor
548
+ let clientInstance = null;
549
+ export function getClient() {
550
+ if (!clientInstance) {
551
+ clientInstance = MeminiClient.getInstance();
552
+ }
553
+ return clientInstance;
554
+ }
555
+ export async function initializeClient(pythonPath, serverModule) {
556
+ const client = getClient();
557
+ await client.initialize();
558
+ return client;
559
+ }
560
+ export { MeminiClient as MeminiMCPClient, MeminiClient as MemorySystem };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Memory Schema Definitions
3
+ *
4
+ * Shared types between memini-client and plugin.
5
+ */
6
+ // Constants
7
+ export const MEMORY_TABLE_NAME = 'memories';
8
+ export const DEFAULT_SEARCH_OPTIONS = {
9
+ topK: 5,
10
+ strategy: 'TIERED',
11
+ threshold: 0.72,
12
+ filter: {},
13
+ };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Contradiction detection integration for memini-ai
3
+ *
4
+ * Finds and resolves contradictory memory pairs using dialectic reasoning
5
+ */
6
+ import { getClient } from '../memini-client/index.js';
7
+ /**
8
+ * Find memory pairs that contradict each other
9
+ */
10
+ export async function findContradictions(query, limit = 10, client) {
11
+ const mc = client ?? getClient();
12
+ const result = await mc.findContradictions(query, limit);
13
+ return result.map((c) => ({
14
+ memoryA: { id: c.memoryIdA, text: c.textA },
15
+ memoryB: { id: c.memoryIdB, text: c.textB },
16
+ contradictionType: c.contradictionType,
17
+ evidence: [],
18
+ }));
19
+ }
20
+ /**
21
+ * Resolve contradiction between two memories
22
+ *
23
+ * Generates dialectic resolution with pro/con arguments
24
+ */
25
+ export async function resolveContradiction(memoryIdA, memoryIdB, client) {
26
+ const mc = client ?? getClient();
27
+ const result = await mc.resolveContradiction(memoryIdA, memoryIdB);
28
+ if (!result) {
29
+ return null;
30
+ }
31
+ return {
32
+ memoryAId: memoryIdA,
33
+ memoryBId: memoryIdB,
34
+ proArguments: [],
35
+ conArguments: [],
36
+ resolution: result.resolutionText,
37
+ winner: null,
38
+ reasoning: '',
39
+ confidence: result.confidence,
40
+ timestamp: new Date().toISOString(),
41
+ };
42
+ }
43
+ /**
44
+ * Get dialectic history for a memory
45
+ */
46
+ export async function getDialecticHistory(memoryId, client) {
47
+ const mc = client ?? getClient();
48
+ const results = await mc.getDialecticHistory(memoryId);
49
+ if (results.length === 0) {
50
+ return {
51
+ memoryId,
52
+ notes: [],
53
+ challenges: [],
54
+ resolutions: [],
55
+ };
56
+ }
57
+ // Get the first history entry
58
+ const first = results[0];
59
+ return {
60
+ memoryId: first.memoryId,
61
+ notes: [],
62
+ challenges: [{
63
+ challengeText: first.challengeText,
64
+ response: first.responseText ?? '',
65
+ confidenceDelta: 0,
66
+ timestamp: first.timestamp,
67
+ }],
68
+ resolutions: [],
69
+ };
70
+ }
71
+ /**
72
+ * Challenge a memory with a counter-argument
73
+ */
74
+ export async function challengeMemory(memoryId, challengeText, client) {
75
+ const mc = client ?? getClient();
76
+ const result = await mc.challengeMemory(memoryId, challengeText);
77
+ if (!result) {
78
+ return null;
79
+ }
80
+ return {
81
+ success: true,
82
+ memoryId: result.memoryId,
83
+ challengeText: result.challengeText,
84
+ response: '',
85
+ confidenceDelta: 0,
86
+ timestamp: result.timestamp,
87
+ };
88
+ }
89
+ /**
90
+ * Adapt a raw memory from memini-ai to our MemoryEntry type
91
+ */
92
+ function adaptMemoryEntry(raw) {
93
+ let timestamp;
94
+ if (typeof raw.timestamp === 'number') {
95
+ timestamp = raw.timestamp;
96
+ }
97
+ else if (typeof raw.timestamp === 'string') {
98
+ timestamp = new Date(raw.timestamp).getTime();
99
+ }
100
+ else {
101
+ timestamp = Date.now();
102
+ }
103
+ return {
104
+ id: raw.id,
105
+ text: raw.text,
106
+ vector: raw.vector ?? [],
107
+ sourceType: raw.sourceType ?? 'manual',
108
+ sourcePath: raw.sourcePath ?? '',
109
+ timestamp,
110
+ contentHash: raw.contentHash,
111
+ metadataJson: raw.metadataJson,
112
+ sessionId: raw.sessionId,
113
+ projectId: raw.projectId,
114
+ score: raw.score,
115
+ trustScore: raw.trustScore,
116
+ retrievalCount: raw.retrievalCount,
117
+ isArchived: raw.isArchived,
118
+ };
119
+ }