@jungjaehoon/mama-core 1.1.4 → 1.2.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.
package/README.md CHANGED
@@ -108,7 +108,7 @@ const mamaApi = require('@jungjaehoon/mama-core/mama-api');
108
108
 
109
109
  - **@huggingface/transformers** - Local embedding generation
110
110
  - **better-sqlite3** - SQLite database
111
- - **sqlite-vec** - Vector similarity extension
111
+ - **Pure-TS cosine similarity** - Vector search (no native extensions)
112
112
 
113
113
  ## Development
114
114
 
@@ -0,0 +1,12 @@
1
+ -- Migration 013: Replace vss_memories (sqlite-vec virtual table) with plain embeddings table
2
+ -- Vector search now uses pure TypeScript brute-force cosine similarity.
3
+ -- No native extensions required.
4
+
5
+ CREATE TABLE IF NOT EXISTS embeddings (
6
+ rowid INTEGER PRIMARY KEY,
7
+ embedding BLOB NOT NULL
8
+ );
9
+
10
+ -- Note: Data migration from vss_memories is handled programmatically in sqlite-adapter.ts
11
+ -- because vss_memories is a vec0 virtual table that requires sqlite-vec to read.
12
+ -- If sqlite-vec is available at migration time, the adapter will copy rows automatically.
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * SQLite Database Adapter
3
3
  *
4
- * Implements DatabaseAdapter interface using better-sqlite3 + sqlite-vec
5
- * This is the current production implementation extracted from memory-store.js
4
+ * Implements DatabaseAdapter interface using better-sqlite3
5
+ * Vector search uses pure TypeScript brute-force cosine similarity (no native extensions)
6
6
  *
7
7
  * @module sqlite-adapter
8
8
  */
@@ -53,11 +53,13 @@ export declare class SQLiteAdapter extends DatabaseAdapter {
53
53
  */
54
54
  transaction<T>(fn: () => T): T;
55
55
  /**
56
- * Vector similarity search using sqlite-vec (vec0 virtual table)
56
+ * Vector similarity search using brute-force cosine similarity
57
+ * Loads all embeddings from the embeddings table, computes similarity in JS,
58
+ * and returns top-N results sorted by similarity descending.
57
59
  */
58
60
  vectorSearch(embedding: Float32Array | number[], limit?: number): VectorSearchResult[] | null;
59
61
  /**
60
- * Insert vector embedding
62
+ * Insert vector embedding into the embeddings table (plain BLOB storage)
61
63
  */
62
64
  insertEmbedding(rowid: number, embedding: Float32Array | number[]): RunResult | null;
63
65
  /**
@@ -68,6 +70,11 @@ export declare class SQLiteAdapter extends DatabaseAdapter {
68
70
  * Run migrations
69
71
  */
70
72
  runMigrations(migrationsDir: string): void;
73
+ /**
74
+ * Attempt to migrate data from legacy vss_memories (sqlite-vec) to embeddings table.
75
+ * This is best-effort: if sqlite-vec is not loaded, the vss_memories table won't be readable.
76
+ */
77
+ private _migrateFromVssMemories;
71
78
  }
72
79
  export default SQLiteAdapter;
73
80
  //# sourceMappingURL=sqlite-adapter.d.ts.map
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * SQLite Database Adapter
4
4
  *
5
- * Implements DatabaseAdapter interface using better-sqlite3 + sqlite-vec
6
- * This is the current production implementation extracted from memory-store.js
5
+ * Implements DatabaseAdapter interface using better-sqlite3
6
+ * Vector search uses pure TypeScript brute-force cosine similarity (no native extensions)
7
7
  *
8
8
  * @module sqlite-adapter
9
9
  */
@@ -20,16 +20,6 @@ const base_adapter_js_1 = require("./base-adapter.js");
20
20
  const statement_js_1 = require("./statement.js");
21
21
  const debug_logger_js_1 = require("../debug-logger.js");
22
22
  const embeddings_js_1 = require("../embeddings.js");
23
- // Try to load sqlite-vec
24
- let sqliteVec = null;
25
- try {
26
- // eslint-disable-next-line @typescript-eslint/no-require-imports
27
- sqliteVec = require('sqlite-vec');
28
- }
29
- catch {
30
- // Defer logging until connect() so we have logger context initialized
31
- sqliteVec = null;
32
- }
33
23
  // Database paths
34
24
  const LEGACY_DB_PATH = path_1.default.join(os_1.default.homedir(), '.spinelift', 'memories.db');
35
25
  // Default to ~/.claude/mama-memory.db for Claude Code/Desktop compatibility
@@ -37,7 +27,7 @@ const DEFAULT_DB_PATH = path_1.default.join(os_1.default.homedir(), '.claude', '
37
27
  class SQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
38
28
  config;
39
29
  db = null;
40
- _vectorSearchEnabled = false;
30
+ _vectorSearchEnabled = true; // Always true: pure TS cosine similarity
41
31
  constructor(config = {}) {
42
32
  super();
43
33
  this.config = config;
@@ -93,23 +83,7 @@ class SQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
93
83
  this.db.pragma('cache_size = -64000'); // 64MB cache
94
84
  this.db.pragma('temp_store = MEMORY');
95
85
  this.db.pragma('foreign_keys = ON');
96
- // Load sqlite-vec extension (graceful degradation if unavailable)
97
- if (sqliteVec) {
98
- try {
99
- sqliteVec.load(this.db);
100
- this._vectorSearchEnabled = true;
101
- (0, debug_logger_js_1.info)('[sqlite-adapter] Loaded sqlite-vec extension');
102
- }
103
- catch (err) {
104
- this._vectorSearchEnabled = false;
105
- const message = err instanceof Error ? err.message : String(err);
106
- (0, debug_logger_js_1.warn)(`[sqlite-adapter] sqlite-vec unavailable (Tier 2 fallback): ${message}`);
107
- }
108
- }
109
- else {
110
- this._vectorSearchEnabled = false;
111
- (0, debug_logger_js_1.warn)('[sqlite-adapter] sqlite-vec package not installed; vector search disabled');
112
- }
86
+ (0, debug_logger_js_1.info)('[sqlite-adapter] Vector search: pure TS cosine similarity (no native extensions)');
113
87
  return this.db;
114
88
  }
115
89
  /**
@@ -158,64 +132,55 @@ class SQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
158
132
  return txn();
159
133
  }
160
134
  /**
161
- * Vector similarity search using sqlite-vec (vec0 virtual table)
135
+ * Vector similarity search using brute-force cosine similarity
136
+ * Loads all embeddings from the embeddings table, computes similarity in JS,
137
+ * and returns top-N results sorted by similarity descending.
162
138
  */
163
139
  vectorSearch(embedding, limit = 5) {
164
140
  if (!this.isConnected()) {
165
141
  throw new Error('Database not connected');
166
142
  }
167
- if (!this._vectorSearchEnabled) {
168
- return null;
169
- }
170
- const embeddingJson = JSON.stringify(Array.from(embedding));
171
- const stmt = this.prepare(`
172
- SELECT
173
- rowid,
174
- embedding,
175
- distance
176
- FROM vss_memories
177
- WHERE embedding MATCH vec_f32(?)
178
- LIMIT ?
179
- `);
180
143
  const queryVector = embedding instanceof Float32Array ? embedding : Float32Array.from(embedding);
181
- const results = stmt.all(embeddingJson, Math.max(limit, 1));
182
- return results
183
- .map((row) => {
144
+ // Check if embeddings table exists
145
+ const tableCheck = this.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'`).all();
146
+ if (tableCheck.length === 0) {
147
+ throw new Error('Embeddings table missing');
148
+ }
149
+ const rows = this.prepare('SELECT rowid, embedding FROM embeddings').all();
150
+ const scored = [];
151
+ for (const row of rows) {
184
152
  const candidate = bufferToVector(row.embedding);
185
- if (!candidate) {
186
- return null;
153
+ if (!candidate)
154
+ continue;
155
+ if (candidate.length !== queryVector.length) {
156
+ (0, debug_logger_js_1.warn)(`Skipping rowid ${row.rowid}: dimension mismatch (${candidate.length} vs ${queryVector.length})`);
157
+ continue;
187
158
  }
188
159
  const similarity = (0, embeddings_js_1.cosineSimilarity)(candidate, queryVector);
189
- return {
160
+ scored.push({
190
161
  rowid: row.rowid,
191
162
  similarity,
192
163
  distance: 1 - similarity,
193
- };
194
- })
195
- .filter((r) => r !== null);
164
+ });
165
+ }
166
+ // Sort by similarity descending, return top-N
167
+ scored.sort((a, b) => (b.similarity ?? 0) - (a.similarity ?? 0));
168
+ return scored.slice(0, Math.max(limit, 1));
196
169
  }
197
170
  /**
198
- * Insert vector embedding
171
+ * Insert vector embedding into the embeddings table (plain BLOB storage)
199
172
  */
200
173
  insertEmbedding(rowid, embedding) {
201
174
  if (!this.isConnected()) {
202
175
  throw new Error('Database not connected');
203
176
  }
204
- if (!this._vectorSearchEnabled) {
205
- return null;
206
- }
207
- const embeddingJson = JSON.stringify(Array.from(embedding));
208
- // CRITICAL FIX: sqlite-vec virtual tables accept rowid as literal but not via ? placeholder
209
- // Using template literal with Number() cast for safety (prevents SQL injection)
210
- const safeRowid = Number(rowid);
211
- if (!Number.isInteger(safeRowid) || safeRowid < 1) {
212
- throw new Error(`Invalid rowid: ${rowid}`);
213
- }
177
+ const vec = embedding instanceof Float32Array ? embedding : Float32Array.from(embedding);
178
+ const buffer = Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
214
179
  const stmt = this.prepare(`
215
- INSERT OR REPLACE INTO vss_memories(rowid, embedding)
216
- VALUES (${safeRowid}, ?)
180
+ INSERT OR REPLACE INTO embeddings(rowid, embedding)
181
+ VALUES (?, ?)
217
182
  `);
218
- return stmt.run(embeddingJson);
183
+ return stmt.run(rowid, buffer);
219
184
  }
220
185
  /**
221
186
  * Get last inserted row ID
@@ -298,33 +263,61 @@ class SQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
298
263
  throw new Error(`Migration ${file} failed: ${message}`);
299
264
  }
300
265
  }
301
- // Create vss_memories table if not exists
302
- if (this._vectorSearchEnabled) {
303
- const vssTables = this.prepare(`
304
- SELECT name FROM sqlite_master
305
- WHERE type='table' AND name='vss_memories'
306
- `).all();
266
+ // Ensure embeddings table exists (plain table, no native extensions)
267
+ const embeddingsTables = this.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'`).all();
268
+ if (embeddingsTables.length === 0) {
269
+ (0, debug_logger_js_1.info)('[sqlite-adapter] Creating embeddings table');
270
+ this.exec(`
271
+ CREATE TABLE embeddings (
272
+ rowid INTEGER PRIMARY KEY,
273
+ embedding BLOB NOT NULL
274
+ )
275
+ `);
276
+ }
277
+ // Always attempt migration — uses INSERT OR IGNORE so safe to run multiple times
278
+ this._migrateFromVssMemories();
279
+ }
280
+ /**
281
+ * Attempt to migrate data from legacy vss_memories (sqlite-vec) to embeddings table.
282
+ * This is best-effort: if sqlite-vec is not loaded, the vss_memories table won't be readable.
283
+ */
284
+ _migrateFromVssMemories() {
285
+ try {
286
+ const vssTables = this.prepare(`SELECT name FROM sqlite_master WHERE name='vss_memories'`).all();
307
287
  if (vssTables.length === 0) {
308
- (0, debug_logger_js_1.info)('[sqlite-adapter] Creating vss_memories virtual table via sqlite-vec');
309
- this.exec(`
310
- CREATE VIRTUAL TABLE vss_memories USING vec0(
311
- embedding float[384]
312
- )
313
- `);
288
+ return; // No legacy table
289
+ }
290
+ // Try to read from vss_memories — will fail if sqlite-vec extension isn't loaded
291
+ const rows = this.prepare('SELECT rowid, embedding FROM vss_memories').all();
292
+ if (rows.length > 0) {
293
+ const insertStmt = this.prepare('INSERT OR IGNORE INTO embeddings (rowid, embedding) VALUES (?, ?)');
294
+ let migrated = 0;
295
+ for (const row of rows) {
296
+ const res = insertStmt.run(row.rowid, row.embedding);
297
+ if (res.changes > 0)
298
+ migrated++;
299
+ }
300
+ (0, debug_logger_js_1.info)(`[sqlite-adapter] Migrated ${migrated} embeddings from vss_memories to embeddings`);
314
301
  }
315
302
  }
316
- else {
317
- (0, debug_logger_js_1.warn)('[sqlite-adapter] Skipping vss_memories creation (sqlite-vec unavailable)');
303
+ catch (err) {
304
+ const message = err instanceof Error ? err.message : String(err);
305
+ (0, debug_logger_js_1.warn)(`[sqlite-adapter] Could not migrate from vss_memories (expected if sqlite-vec not installed): ${message}`);
318
306
  }
319
307
  }
320
308
  }
321
309
  exports.SQLiteAdapter = SQLiteAdapter;
322
310
  exports.default = SQLiteAdapter;
323
311
  function bufferToVector(buffer) {
324
- if (!buffer) {
312
+ if (!buffer || buffer.byteLength % 4 !== 0) {
313
+ return null;
314
+ }
315
+ try {
316
+ const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
317
+ return new Float32Array(arrayBuffer);
318
+ }
319
+ catch {
325
320
  return null;
326
321
  }
327
- const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
328
- return new Float32Array(arrayBuffer);
329
322
  }
330
323
  //# sourceMappingURL=sqlite-adapter.js.map
@@ -2,7 +2,7 @@
2
2
  * MAMA Database Manager (SQLite-only)
3
3
  *
4
4
  * SQLite-exclusive database interface for MAMA Plugin.
5
- * Uses better-sqlite3 + sqlite-vec for local storage.
5
+ * Uses better-sqlite3 for local storage.
6
6
  *
7
7
  * PostgreSQL support is only available in the legacy mcp-server repository.
8
8
  *
@@ -10,7 +10,7 @@
10
10
  * - WAL mode for better concurrency
11
11
  * - synchronous=NORMAL for performance
12
12
  * - Automatic migration management
13
- * - Vector similarity search (when sqlite-vec available)
13
+ * - Vector similarity search (pure TS cosine similarity)
14
14
  *
15
15
  * @module db-manager
16
16
  * @version 2.1 (Plugin - SQLite-only)
@@ -53,7 +53,7 @@ export interface DecisionRecord {
53
53
  refined_from?: string | string[] | null;
54
54
  created_at: number;
55
55
  updated_at?: number;
56
- edges?: unknown[];
56
+ edges?: DecisionEdgeRow[];
57
57
  }
58
58
  export interface OutcomeData {
59
59
  outcome?: string | null;
@@ -68,17 +68,40 @@ export interface VectorSearchParams {
68
68
  threshold?: number;
69
69
  timeWindow?: number;
70
70
  }
71
+ export interface DecisionEdgeRow {
72
+ from_id: string;
73
+ to_id: string;
74
+ relationship: string;
75
+ reason?: string | null;
76
+ weight?: number;
77
+ created_at?: number;
78
+ created_by?: string;
79
+ approved_by_user?: number | null;
80
+ decision_id?: string | null;
81
+ evidence?: string | null;
82
+ }
83
+ export interface SemanticEdgeItem {
84
+ from_id: string;
85
+ to_id: string;
86
+ relationship: string;
87
+ reason?: string;
88
+ topic: string;
89
+ decision: string;
90
+ confidence?: number;
91
+ created_at?: string;
92
+ approved_by_user?: number | null;
93
+ }
71
94
  export interface SemanticEdges {
72
- refines: unknown[];
73
- refined_by: unknown[];
74
- contradicts: unknown[];
75
- contradicted_by: unknown[];
76
- builds_on: unknown[];
77
- built_on_by: unknown[];
78
- debates: unknown[];
79
- debated_by: unknown[];
80
- synthesizes: unknown[];
81
- synthesized_by: unknown[];
95
+ refines: SemanticEdgeItem[];
96
+ refined_by: SemanticEdgeItem[];
97
+ contradicts: SemanticEdgeItem[];
98
+ contradicted_by: SemanticEdgeItem[];
99
+ builds_on: SemanticEdgeItem[];
100
+ built_on_by: SemanticEdgeItem[];
101
+ debates: SemanticEdgeItem[];
102
+ debated_by: SemanticEdgeItem[];
103
+ synthesizes: SemanticEdgeItem[];
104
+ synthesized_by: SemanticEdgeItem[];
82
105
  }
83
106
  /**
84
107
  * Initialize SQLite database adapter and connect
@@ -120,8 +143,7 @@ export declare function closeDB(): Promise<void>;
120
143
  /**
121
144
  * Insert embedding into vector search table
122
145
  *
123
- * Uses sqlite-vec for vector similarity search
124
- * Gracefully degrades if sqlite-vec is not available
146
+ * Stores embedding in the embeddings table for vector similarity search
125
147
  *
126
148
  * @param decisionRowid - SQLite rowid
127
149
  * @param embedding - 384-dim embedding vector
@@ -3,7 +3,7 @@
3
3
  * MAMA Database Manager (SQLite-only)
4
4
  *
5
5
  * SQLite-exclusive database interface for MAMA Plugin.
6
- * Uses better-sqlite3 + sqlite-vec for local storage.
6
+ * Uses better-sqlite3 for local storage.
7
7
  *
8
8
  * PostgreSQL support is only available in the legacy mcp-server repository.
9
9
  *
@@ -11,7 +11,7 @@
11
11
  * - WAL mode for better concurrency
12
12
  * - synchronous=NORMAL for performance
13
13
  * - Automatic migration management
14
- * - Vector similarity search (when sqlite-vec available)
14
+ * - Vector similarity search (pure TS cosine similarity)
15
15
  *
16
16
  * @module db-manager
17
17
  * @version 2.1 (Plugin - SQLite-only)
@@ -142,8 +142,7 @@ async function closeDB() {
142
142
  /**
143
143
  * Insert embedding into vector search table
144
144
  *
145
- * Uses sqlite-vec for vector similarity search
146
- * Gracefully degrades if sqlite-vec is not available
145
+ * Stores embedding in the embeddings table for vector similarity search
147
146
  *
148
147
  * @param decisionRowid - SQLite rowid
149
148
  * @param embedding - 384-dim embedding vector
@@ -172,7 +171,7 @@ async function insertEmbedding(decisionRowid, embedding) {
172
171
  async function vectorSearch(queryEmbedding, limit = 5, threshold = 0.7) {
173
172
  const adapter = getAdapter();
174
173
  try {
175
- // SQLite adapter returns null if sqlite-vec not available
174
+ // Brute-force cosine similarity over all embeddings
176
175
  const results = await adapter.vectorSearch(queryEmbedding, limit * 3);
177
176
  if (!results || results.length === 0) {
178
177
  return []; // No keyword fallback - fast fail
@@ -185,7 +185,7 @@ export declare function getSupersededChainDepth(topic: string): Promise<{
185
185
  * Task 3.5: If previous exists: Create supersedes edge, Update previous superseded_by
186
186
  * Task 3.6: If multi-parent refinement: Store refined_from, Calculate combined confidence
187
187
  * Task 3.7: Generate enhanced embedding
188
- * Task 3.8: Store in vss_memories (link via rowid)
188
+ * Task 3.8: Store embedding (link via rowid)
189
189
  *
190
190
  * AC #1: Decision stored with outcome=NULL, confidence from LLM
191
191
  * AC #2: Supersedes relationship creation
@@ -380,7 +380,7 @@ async function getSupersededChainDepth(topic) {
380
380
  * Task 3.5: If previous exists: Create supersedes edge, Update previous superseded_by
381
381
  * Task 3.6: If multi-parent refinement: Store refined_from, Calculate combined confidence
382
382
  * Task 3.7: Generate enhanced embedding
383
- * Task 3.8: Store in vss_memories (link via rowid)
383
+ * Task 3.8: Store embedding (link via rowid)
384
384
  *
385
385
  * AC #1: Decision stored with outcome=NULL, confidence from LLM
386
386
  * AC #2: Supersedes relationship creation
@@ -461,7 +461,7 @@ async function learnDecision(detection, toolExecution, sessionContext) {
461
461
  : null,
462
462
  risks: detection.risks || null,
463
463
  };
464
- // Task 3.7, 3.8: Generate enhanced embedding and store in vss_memories
464
+ // Task 3.7, 3.8: Generate enhanced embedding and store in embeddings table
465
465
  // (Handled by insertDecisionWithEmbedding function)
466
466
  await (0, memory_store_js_1.insertDecisionWithEmbedding)(decision);
467
467
  // ════════════════════════════════════════════════════════
@@ -20,9 +20,17 @@ import type { Server as HTTPServer } from 'http';
20
20
  /**
21
21
  * Message router interface
22
22
  */
23
+ interface StreamCallbacks {
24
+ onDelta?: (text: string) => void;
25
+ onToolUse?: (toolName: string, input: Record<string, unknown>) => void;
26
+ onToolComplete?: (toolName: string, toolUseId: string, isError: boolean) => void;
27
+ onFinal?: (response: unknown) => void;
28
+ onError?: (error: Error) => void;
29
+ }
23
30
  interface MessageRouter {
24
31
  process(message: NormalizedMessage, options?: {
25
32
  onQueued?: () => void;
33
+ onStream?: StreamCallbacks;
26
34
  }): Promise<RouterResult>;
27
35
  }
28
36
  /**
@@ -218,6 +218,8 @@ async function handleClientMessage(clientId, message, clientInfo, messageRouter,
218
218
  }
219
219
  }, 10000); // Every 10 seconds
220
220
  let result;
221
+ let deltasSent = false;
222
+ let toolIndex = 0;
221
223
  try {
222
224
  // Build contentBlocks from attachments (files are pre-compressed by upload-handler)
223
225
  let contentBlocks = undefined;
@@ -303,18 +305,33 @@ async function handleClientMessage(clientId, message, clientInfo, messageRouter,
303
305
  }
304
306
  logger.info(`Message queued for ${clientId} (session busy)`);
305
307
  },
308
+ onStream: {
309
+ onDelta: (text) => {
310
+ deltasSent = true;
311
+ safeSend(clientInfo.ws, JSON.stringify({ type: 'stream', content: text }));
312
+ },
313
+ onToolUse: (tool, input) => {
314
+ const idx = toolIndex++;
315
+ safeSend(clientInfo.ws, JSON.stringify({ type: 'tool_use', tool, index: idx, input }));
316
+ },
317
+ onToolComplete: (tool, _toolUseId, isError) => {
318
+ safeSend(clientInfo.ws, JSON.stringify({ type: 'tool_complete', tool, index: toolIndex - 1, isError }));
319
+ },
320
+ },
306
321
  });
307
322
  }
308
323
  finally {
309
324
  clearInterval(keepAliveInterval);
310
325
  }
311
- // Send response as stream (for Chat tab compatibility)
312
- safeSend(clientInfo.ws, JSON.stringify({
313
- type: 'stream',
314
- content: result.response,
315
- sessionId: result.sessionId,
316
- }));
317
- // Send stream end
326
+ // Only send full response if no deltas were streamed (Codex fallback)
327
+ if (!deltasSent) {
328
+ safeSend(clientInfo.ws, JSON.stringify({
329
+ type: 'stream',
330
+ content: result.response,
331
+ sessionId: result.sessionId,
332
+ }));
333
+ }
334
+ // Always send stream end
318
335
  safeSend(clientInfo.ws, JSON.stringify({
319
336
  type: 'stream_end',
320
337
  sessionId: result.sessionId,
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  export { generateEmbedding, generateEnhancedEmbedding, generateBatchEmbeddings, cosineSimilarity, embeddingCache, EMBEDDING_DIM, MODEL_NAME, } from './embeddings.js';
11
11
  export { EmbeddingCache } from './embedding-cache.js';
12
12
  export { getEmbeddingFromServer, getServerStatus, isServerRunning, getServerPort, DEFAULT_PORT, HOST, TIMEOUT_MS, type ServerStatus, } from './embedding-client.js';
13
- export { initDB, getDB, getAdapter, closeDB, insertEmbedding, vectorSearch, queryVectorSearch, insertDecisionWithEmbedding, queryDecisionGraph, querySemanticEdges, updateDecisionOutcome, getPreparedStmt, getDbPath, type DatabaseAdapter as DBManagerAdapter, type PreparedStatement, type DecisionRecord, type OutcomeData, type VectorSearchParams, type SemanticEdges, type DecisionInput, } from './db-manager.js';
13
+ export { initDB, getDB, getAdapter, closeDB, insertEmbedding, vectorSearch, queryVectorSearch, insertDecisionWithEmbedding, queryDecisionGraph, querySemanticEdges, updateDecisionOutcome, getPreparedStmt, getDbPath, type DatabaseAdapter as DBManagerAdapter, type PreparedStatement, type DecisionRecord, type OutcomeData, type VectorSearchParams, type SemanticEdges, type SemanticEdgeItem, type DecisionInput, } from './db-manager.js';
14
14
  export { createAdapter, DatabaseAdapter, SQLiteAdapter, type AdapterConfig, type Statement, type VectorSearchResult, type RunResult, } from './db-adapter/index.js';
15
15
  export { getSessionDecisions, incrementUsageSuccess, incrementUsageFailure, getDecisionById, traverseDecisionChain, DB_PATH, DB_DIR, LEGACY_DB_PATH, DEFAULT_DB_PATH, } from './memory-store.js';
16
16
  import mama from './mama-api.js';
@@ -172,6 +172,9 @@ export interface LoadCheckpointResult {
172
172
  * Outcome badge map type
173
173
  */
174
174
  export type OutcomeBadgeMap = Record<string, string | null>;
175
+ /**
176
+ * Raw semantic edge from database
177
+ */
175
178
  /**
176
179
  * Recall options
177
180
  */
@@ -412,7 +415,51 @@ declare function save({ topic, decision, reasoning, confidence, type, outcome, f
412
415
  * const markdown = await mama.recall('auth_strategy', { format: 'markdown' });
413
416
  * // → "📋 Decision History: auth_strategy\n━━━━━━━━..."
414
417
  */
415
- declare function recall(topic: string, options?: RecallOptions): Promise<unknown>;
418
+ interface RecallEdgeRef {
419
+ to_topic?: string;
420
+ to_decision?: string;
421
+ to_id?: string;
422
+ from_topic?: string;
423
+ from_decision?: string;
424
+ from_id?: string;
425
+ reason?: string | null;
426
+ confidence?: number;
427
+ created_at?: string | number;
428
+ }
429
+ interface RecallGraphResult {
430
+ topic: string;
431
+ supersedes_chain: Array<{
432
+ id: string;
433
+ decision: string;
434
+ reasoning?: string | null;
435
+ confidence?: number;
436
+ outcome?: string | null;
437
+ failure_reason?: string | null;
438
+ created_at: number;
439
+ updated_at?: number;
440
+ superseded_by?: string | null;
441
+ supersedes?: string | null;
442
+ }>;
443
+ semantic_edges: {
444
+ refines: RecallEdgeRef[];
445
+ refined_by: RecallEdgeRef[];
446
+ contradicts: RecallEdgeRef[];
447
+ contradicted_by: RecallEdgeRef[];
448
+ };
449
+ meta: {
450
+ count: number;
451
+ latest_id?: string;
452
+ has_supersedes_chain: boolean;
453
+ has_semantic_edges: boolean;
454
+ semantic_edges_count: {
455
+ refines: number;
456
+ refined_by: number;
457
+ contradicts: number;
458
+ contradicted_by: number;
459
+ };
460
+ };
461
+ }
462
+ declare function recall(topic: string, options?: RecallOptions): Promise<string | RecallGraphResult>;
416
463
  /**
417
464
  * Update outcome of a decision
418
465
  *
@@ -505,13 +552,21 @@ declare function saveCheckpoint(summary: string, openFiles?: string[], nextSteps
505
552
  *
506
553
  * @returns {Promise<Object|null>} Latest checkpoint or null
507
554
  */
555
+ interface ConversationMessage {
556
+ role: string;
557
+ content: string | Array<{
558
+ type: string;
559
+ text?: string;
560
+ [key: string]: unknown;
561
+ }>;
562
+ }
508
563
  interface CheckpointRow {
509
564
  id?: number;
510
565
  timestamp?: number;
511
566
  summary?: string;
512
567
  open_files?: string | string[];
513
568
  next_steps?: string;
514
- recent_conversation?: string | unknown[];
569
+ recent_conversation?: string | ConversationMessage[];
515
570
  status?: string;
516
571
  }
517
572
  declare function loadCheckpoint(): Promise<CheckpointRow | null>;
package/dist/mama-api.js CHANGED
@@ -347,26 +347,6 @@ async function _getReasoningGraphInfo(topic, currentId) {
347
347
  };
348
348
  }
349
349
  }
350
- /**
351
- * Recall decisions by topic
352
- *
353
- * DEFAULT: Returns JSON object with decisions and edges (LLM-first design)
354
- * OPTIONAL: Returns Markdown string if format='markdown' (for human display)
355
- *
356
- * @param {string} topic - Decision topic to recall
357
- * @param {Object} [options] - Options
358
- * @param {string} [options.format='json'] - Output format: 'json' (default) or 'markdown'
359
- * @returns {Promise<Object|string>} Decision history as JSON or Markdown
360
- *
361
- * @example
362
- * // LLM usage (default)
363
- * const data = await mama.recall('auth_strategy');
364
- * // → { topic, decisions: [...], edges: [...], meta: {...} }
365
- *
366
- * // Human display
367
- * const markdown = await mama.recall('auth_strategy', { format: 'markdown' });
368
- * // → "📋 Decision History: auth_strategy\n━━━━━━━━..."
369
- */
370
350
  async function recall(topic, options = {}) {
371
351
  if (!topic || typeof topic !== 'string') {
372
352
  throw new Error('mama.recall() requires topic (string)');
@@ -382,18 +362,22 @@ async function recall(topic, options = {}) {
382
362
  topic,
383
363
  supersedes_chain: [],
384
364
  semantic_edges: { refines: [], refined_by: [], contradicts: [], contradicted_by: [] },
385
- meta: { count: 0 },
365
+ meta: {
366
+ count: 0,
367
+ has_supersedes_chain: false,
368
+ has_semantic_edges: false,
369
+ semantic_edges_count: { refines: 0, refined_by: 0, contradicts: 0, contradicted_by: 0 },
370
+ },
386
371
  };
387
372
  }
388
373
  // Query semantic edges for all decisions
389
374
  const decisionIds = decisions.map((d) => d.id);
390
- const rawEdgesResult = await (0, memory_store_js_1.querySemanticEdges)(decisionIds);
391
- const rawEdges = (rawEdgesResult || {});
375
+ const rawEdges = await (0, memory_store_js_1.querySemanticEdges)(decisionIds);
392
376
  const semanticEdges = {
393
- refines: (rawEdges.refines || []),
394
- refined_by: (rawEdges.refined_by || []),
395
- contradicts: (rawEdges.contradicts || []),
396
- contradicted_by: (rawEdges.contradicted_by || []),
377
+ refines: rawEdges.refines || [],
378
+ refined_by: rawEdges.refined_by || [],
379
+ contradicts: rawEdges.contradicts || [],
380
+ contradicted_by: rawEdges.contradicted_by || [],
397
381
  };
398
382
  // Markdown format (for human display)
399
383
  if (format === 'markdown') {
@@ -566,7 +550,6 @@ async function expandWithGraph(candidates) {
566
550
  synthesized_by: rawEdges.synthesized_by || [],
567
551
  };
568
552
  // Helper to add edge to graph
569
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic property access with idField
570
553
  const addEdge = (edge, idField, source, rank, simFactor) => {
571
554
  const id = edge[idField];
572
555
  if (!graphEnhanced.has(id)) {
@@ -698,8 +681,10 @@ async function suggest(userQuestion, options = {}) {
698
681
  let results = [];
699
682
  let searchMethod = 'vector';
700
683
  try {
701
- // Check if vectorSearch prepared statement exists
702
- (0, memory_store_js_1.getPreparedStmt)('vectorSearch');
684
+ // Check if vector search is available
685
+ if (!(0, memory_store_js_1.getAdapter)().vectorSearchEnabled) {
686
+ throw new Error('Vector search not available');
687
+ }
703
688
  // Generate query embedding
704
689
  const queryEmbedding = await (0, embeddings_js_1.generateEmbedding)(userQuestion);
705
690
  // Adaptive threshold (shorter queries need higher confidence)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungjaehoon/mama-core",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
4
4
  "description": "MAMA Core - Shared modules for Memory-Augmented MCP Assistant",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -62,11 +62,8 @@
62
62
  "node": ">=18.0.0"
63
63
  },
64
64
  "dependencies": {
65
- "@anthropic-ai/sdk": "^0.72.1",
66
65
  "@huggingface/transformers": "^3.8.1",
67
66
  "better-sqlite3": "^11.0.0",
68
- "mime-types": "^3.0.2",
69
- "sqlite-vec": "^0.1.0",
70
67
  "ws": "^8.19.0"
71
68
  },
72
69
  "devDependencies": {