multimodemind 0.0.1 → 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.
- package/LICENSE +21 -0
- package/README.md +71 -4
- package/dist/cli/config.d.ts +14 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +30 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/dashboard.d.ts +14 -0
- package/dist/cli/dashboard.d.ts.map +1 -0
- package/dist/cli/dashboard.js +277 -0
- package/dist/cli/dashboard.js.map +1 -0
- package/dist/embeddings/base.d.ts +19 -0
- package/dist/embeddings/base.d.ts.map +1 -0
- package/dist/embeddings/base.js +6 -0
- package/dist/embeddings/base.js.map +1 -0
- package/dist/embeddings/index.d.ts +11 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/index.js +18 -0
- package/dist/embeddings/index.js.map +1 -0
- package/dist/embeddings/local.d.ts +19 -0
- package/dist/embeddings/local.d.ts.map +1 -0
- package/dist/embeddings/local.js +40 -0
- package/dist/embeddings/local.js.map +1 -0
- package/dist/embeddings/openai.d.ts +15 -0
- package/dist/embeddings/openai.d.ts.map +1 -0
- package/dist/embeddings/openai.js +33 -0
- package/dist/embeddings/openai.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/router/index.d.ts +25 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +82 -0
- package/dist/router/index.js.map +1 -0
- package/dist/stores/base.d.ts +40 -0
- package/dist/stores/base.d.ts.map +1 -0
- package/dist/stores/base.js +6 -0
- package/dist/stores/base.js.map +1 -0
- package/dist/stores/files.d.ts +23 -0
- package/dist/stores/files.d.ts.map +1 -0
- package/dist/stores/files.js +144 -0
- package/dist/stores/files.js.map +1 -0
- package/dist/stores/index.d.ts +7 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +6 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/stores/leveldb.d.ts +21 -0
- package/dist/stores/leveldb.d.ts.map +1 -0
- package/dist/stores/leveldb.js +99 -0
- package/dist/stores/leveldb.js.map +1 -0
- package/dist/stores/markdown.d.ts +25 -0
- package/dist/stores/markdown.d.ts.map +1 -0
- package/dist/stores/markdown.js +133 -0
- package/dist/stores/markdown.js.map +1 -0
- package/dist/stores/sqlite.d.ts +22 -0
- package/dist/stores/sqlite.d.ts.map +1 -0
- package/dist/stores/sqlite.js +125 -0
- package/dist/stores/sqlite.js.map +1 -0
- package/dist/stores/utils.d.ts +17 -0
- package/dist/stores/utils.d.ts.map +1 -0
- package/dist/stores/utils.js +40 -0
- package/dist/stores/utils.js.map +1 -0
- package/dist/stores/vector.d.ts +26 -0
- package/dist/stores/vector.d.ts.map +1 -0
- package/dist/stores/vector.js +106 -0
- package/dist/stores/vector.js.map +1 -0
- package/dist/tools/retrieve.d.ts +9 -0
- package/dist/tools/retrieve.d.ts.map +1 -0
- package/dist/tools/retrieve.js +41 -0
- package/dist/tools/retrieve.js.map +1 -0
- package/dist/tools/sources.d.ts +9 -0
- package/dist/tools/sources.d.ts.map +1 -0
- package/dist/tools/sources.js +34 -0
- package/dist/tools/sources.js.map +1 -0
- package/dist/tools/store.d.ts +10 -0
- package/dist/tools/store.d.ts.map +1 -0
- package/dist/tools/store.js +51 -0
- package/dist/tools/store.js.map +1 -0
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -13
- package/index.js +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI embedding provider (default).
|
|
3
|
+
* Model: text-embedding-3-small — 1536 dimensions, MIT-compatible API.
|
|
4
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
5
|
+
*/
|
|
6
|
+
import OpenAI from 'openai';
|
|
7
|
+
const MODEL = 'text-embedding-3-small';
|
|
8
|
+
const DIMENSIONS = 1536;
|
|
9
|
+
export class OpenAIEmbeddingProvider {
|
|
10
|
+
name = 'openai';
|
|
11
|
+
dimensions = DIMENSIONS;
|
|
12
|
+
client;
|
|
13
|
+
constructor(apiKey) {
|
|
14
|
+
this.client = new OpenAI({
|
|
15
|
+
apiKey: apiKey ?? process.env['OPENAI_API_KEY'],
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async embed(text) {
|
|
19
|
+
const response = await this.client.embeddings.create({
|
|
20
|
+
model: MODEL,
|
|
21
|
+
input: text.slice(0, 8191), // token safety trim
|
|
22
|
+
});
|
|
23
|
+
return response.data[0].embedding;
|
|
24
|
+
}
|
|
25
|
+
async embedBatch(texts) {
|
|
26
|
+
const response = await this.client.embeddings.create({
|
|
27
|
+
model: MODEL,
|
|
28
|
+
input: texts.map((t) => t.slice(0, 8191)),
|
|
29
|
+
});
|
|
30
|
+
return response.data.map((d) => d.embedding);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/embeddings/openai.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,MAAM,KAAK,GAAG,wBAAwB,CAAC;AACvC,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,MAAM,OAAO,uBAAuB;IACzB,IAAI,GAAG,QAAQ,CAAC;IAChB,UAAU,GAAG,UAAU,CAAC;IAEzB,MAAM,CAAS;IAEvB,YAAY,MAAe;QACzB,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC;YACvB,MAAM,EAAE,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;SAChD,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YACnD,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,oBAAoB;SACjD,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,KAAe;QAC9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;YACnD,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;SAC1C,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC/C,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Multimode Mind — MCP server entry point.
|
|
4
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* MMIND_VAULT_PATH=/path/to/vault npx mmind
|
|
8
|
+
*
|
|
9
|
+
* All paths can also be set via environment variables (see MultimodeMindConfig).
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Multimode Mind — MCP server entry point.
|
|
4
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* MMIND_VAULT_PATH=/path/to/vault npx mmind
|
|
8
|
+
*
|
|
9
|
+
* All paths can also be set via environment variables (see MultimodeMindConfig).
|
|
10
|
+
*/
|
|
11
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
+
import { createEmbeddingProvider } from './embeddings/index.js';
|
|
14
|
+
import { RetrieveRouter } from './router/index.js';
|
|
15
|
+
import { SqliteStore, LevelDbStore, MarkdownStore, FilesStore, VectorStore, } from './stores/index.js';
|
|
16
|
+
import { registerRetrieveTool } from './tools/retrieve.js';
|
|
17
|
+
import { registerStoreTool } from './tools/store.js';
|
|
18
|
+
import { registerSourcesTool } from './tools/sources.js';
|
|
19
|
+
// ─── Config from environment ──────────────────────────────────────────────────
|
|
20
|
+
function loadConfig() {
|
|
21
|
+
return {
|
|
22
|
+
vaultPath: process.env['MMIND_VAULT_PATH'],
|
|
23
|
+
filesPath: process.env['MMIND_FILES_PATH'] ?? '.mmind/files',
|
|
24
|
+
sqlitePath: process.env['MMIND_SQLITE_PATH'] ?? '.mmind/memory.db',
|
|
25
|
+
leveldbPath: process.env['MMIND_LEVELDB_PATH'] ?? '.mmind/leveldb',
|
|
26
|
+
vectorIndexPath: process.env['MMIND_VECTOR_PATH'] ?? '.mmind/vector-index',
|
|
27
|
+
openAiApiKey: process.env['OPENAI_API_KEY'],
|
|
28
|
+
retrieveLimit: Number(process.env['MMIND_RETRIEVE_LIMIT'] ?? 10),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// ─── Bootstrap ────────────────────────────────────────────────────────────────
|
|
32
|
+
async function main() {
|
|
33
|
+
const config = loadConfig();
|
|
34
|
+
// Build the store list — markdown vault is optional
|
|
35
|
+
const stores = [
|
|
36
|
+
new SqliteStore(config.sqlitePath),
|
|
37
|
+
new LevelDbStore(config.leveldbPath),
|
|
38
|
+
new FilesStore(config.filesPath),
|
|
39
|
+
new VectorStore(config.vectorIndexPath),
|
|
40
|
+
];
|
|
41
|
+
if (config.vaultPath) {
|
|
42
|
+
stores.push(new MarkdownStore(config.vaultPath));
|
|
43
|
+
}
|
|
44
|
+
// Probe availability (non-fatal — stores log their own warnings)
|
|
45
|
+
const checks = await Promise.allSettled(stores.map((s) => s.isAvailable()));
|
|
46
|
+
for (let i = 0; i < checks.length; i++) {
|
|
47
|
+
const check = checks[i];
|
|
48
|
+
const store = stores[i];
|
|
49
|
+
if (check.status === 'rejected' || (check.status === 'fulfilled' && !check.value)) {
|
|
50
|
+
console.warn(`[mmind] Store unavailable at startup: ${store.name}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const embedder = createEmbeddingProvider(config.openAiApiKey);
|
|
54
|
+
const router = new RetrieveRouter(stores, embedder);
|
|
55
|
+
// ─── MCP server setup ───────────────────────────────────────────────────────
|
|
56
|
+
const server = new McpServer({
|
|
57
|
+
name: 'multimodemind',
|
|
58
|
+
version: '0.1.0',
|
|
59
|
+
});
|
|
60
|
+
registerRetrieveTool(server, router);
|
|
61
|
+
registerStoreTool(server, stores, embedder);
|
|
62
|
+
registerSourcesTool(server, stores);
|
|
63
|
+
// ─── Transport ──────────────────────────────────────────────────────────────
|
|
64
|
+
const transport = new StdioServerTransport();
|
|
65
|
+
await server.connect(transport);
|
|
66
|
+
console.error('[mmind] Multimode Mind MCP server running (stdio)');
|
|
67
|
+
// Graceful shutdown
|
|
68
|
+
const shutdown = async () => {
|
|
69
|
+
await Promise.allSettled(stores.map((s) => s.close()));
|
|
70
|
+
process.exit(0);
|
|
71
|
+
};
|
|
72
|
+
process.on('SIGINT', shutdown);
|
|
73
|
+
process.on('SIGTERM', shutdown);
|
|
74
|
+
}
|
|
75
|
+
main().catch((err) => {
|
|
76
|
+
console.error('[mmind] Fatal startup error:', err);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieve router — the differentiator.
|
|
3
|
+
* Fans out to all available stores, scores and de-dupes results,
|
|
4
|
+
* returns a single ranked context bundle with full provenance.
|
|
5
|
+
*
|
|
6
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
7
|
+
*/
|
|
8
|
+
import type { EmbeddingProvider } from '../embeddings/index.js';
|
|
9
|
+
import type { MemoryStore } from '../stores/base.js';
|
|
10
|
+
import type { RetrieveResult, StoreType } from '../types.js';
|
|
11
|
+
export interface RouterOptions {
|
|
12
|
+
/** How many results to return after merging across stores */
|
|
13
|
+
limit?: number;
|
|
14
|
+
/** Only query these store types (default: all available) */
|
|
15
|
+
stores?: StoreType[];
|
|
16
|
+
/** Per-store candidate fetch multiplier before final ranking */
|
|
17
|
+
candidateMultiplier?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class RetrieveRouter {
|
|
20
|
+
private readonly stores;
|
|
21
|
+
private readonly embedder;
|
|
22
|
+
constructor(stores: MemoryStore[], embedder: EmbeddingProvider);
|
|
23
|
+
retrieve(query: string, opts?: RouterOptions): Promise<RetrieveResult>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAe,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE1E,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAF3B,YACmB,MAAM,EAAE,WAAW,EAAE,EACrB,QAAQ,EAAE,iBAAiB,EAC1C;IAEE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CA+D/E;CACF"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieve router — the differentiator.
|
|
3
|
+
* Fans out to all available stores, scores and de-dupes results,
|
|
4
|
+
* returns a single ranked context bundle with full provenance.
|
|
5
|
+
*
|
|
6
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
7
|
+
*/
|
|
8
|
+
export class RetrieveRouter {
|
|
9
|
+
stores;
|
|
10
|
+
embedder;
|
|
11
|
+
constructor(stores, embedder) {
|
|
12
|
+
this.stores = stores;
|
|
13
|
+
this.embedder = embedder;
|
|
14
|
+
}
|
|
15
|
+
async retrieve(query, opts = {}) {
|
|
16
|
+
const limit = opts.limit ?? 10;
|
|
17
|
+
const candidatePer = limit * (opts.candidateMultiplier ?? 3);
|
|
18
|
+
// Filter to requested store types
|
|
19
|
+
const activeStores = opts.stores
|
|
20
|
+
? this.stores.filter((s) => opts.stores.includes(s.type))
|
|
21
|
+
: this.stores;
|
|
22
|
+
// Generate query embedding once, reuse across all stores
|
|
23
|
+
let embedding = [];
|
|
24
|
+
try {
|
|
25
|
+
embedding = await this.embedder.embed(query);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
// Degraded mode: embedding failed, fall back to keyword-only
|
|
29
|
+
console.warn('[mmind:router] Embedding failed, using keyword-only scoring:', err);
|
|
30
|
+
}
|
|
31
|
+
// Fan out to all stores in parallel
|
|
32
|
+
const storeResults = await Promise.allSettled(activeStores.map((store) => store.search(query, embedding, candidatePer)));
|
|
33
|
+
const storesQueried = [];
|
|
34
|
+
const allCandidates = [];
|
|
35
|
+
for (let i = 0; i < storeResults.length; i++) {
|
|
36
|
+
const result = storeResults[i];
|
|
37
|
+
const store = activeStores[i];
|
|
38
|
+
if (result.status === 'fulfilled') {
|
|
39
|
+
storesQueried.push(store.type);
|
|
40
|
+
allCandidates.push(...result.value);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.warn(`[mmind:router] Store ${store.name} failed:`, result.reason);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// De-duplicate by content fingerprint (same text from multiple stores)
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
const deduped = [];
|
|
49
|
+
for (const candidate of allCandidates) {
|
|
50
|
+
const fp = contentFingerprint(candidate.entry.content);
|
|
51
|
+
if (!seen.has(fp)) {
|
|
52
|
+
seen.add(fp);
|
|
53
|
+
deduped.push(candidate);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Final ranking: sort by score desc, apply reciprocal rank fusion for
|
|
57
|
+
// entries that appear (as near-dupes) across multiple stores
|
|
58
|
+
const ranked = deduped
|
|
59
|
+
.sort((a, b) => b.score - a.score)
|
|
60
|
+
.slice(0, limit);
|
|
61
|
+
return {
|
|
62
|
+
entries: ranked,
|
|
63
|
+
query,
|
|
64
|
+
totalCandidates: allCandidates.length,
|
|
65
|
+
storesQueried,
|
|
66
|
+
// conflicts: [] — v2
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
71
|
+
/**
|
|
72
|
+
* Rough content fingerprint for de-duplication.
|
|
73
|
+
* Lowercases, strips whitespace, takes first 200 chars.
|
|
74
|
+
*/
|
|
75
|
+
function contentFingerprint(content) {
|
|
76
|
+
return content
|
|
77
|
+
.toLowerCase()
|
|
78
|
+
.replace(/\s+/g, ' ')
|
|
79
|
+
.trim()
|
|
80
|
+
.slice(0, 200);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryStore — the contract every store adapter must implement.
|
|
3
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
4
|
+
*/
|
|
5
|
+
import type { MemoryEntry, RankedEntry, SourceInfo, StoreType } from '../types.js';
|
|
6
|
+
export interface MemoryStore {
|
|
7
|
+
readonly type: StoreType;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
/**
|
|
10
|
+
* Returns true if the store is reachable and ready.
|
|
11
|
+
* Called at startup and by the `sources` tool.
|
|
12
|
+
*/
|
|
13
|
+
isAvailable(): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* Search this store for entries relevant to the query.
|
|
16
|
+
*
|
|
17
|
+
* @param query Natural-language query (for text/BM25 stores)
|
|
18
|
+
* @param embedding Query vector (for vector-capable stores; may be empty [])
|
|
19
|
+
* @param limit Max entries to return
|
|
20
|
+
*/
|
|
21
|
+
search(query: string, embedding: number[], limit: number): Promise<RankedEntry[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Persist a memory entry and return its generated ID.
|
|
24
|
+
* Callers pass the full entry minus auto-assigned fields.
|
|
25
|
+
*/
|
|
26
|
+
store(entry: Omit<MemoryEntry, 'id' | 'createdAt' | 'updatedAt'>): Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Fetch a single entry by ID, or null if not found.
|
|
29
|
+
*/
|
|
30
|
+
get(id: string): Promise<MemoryEntry | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Return health/metadata for the `sources` tool.
|
|
33
|
+
*/
|
|
34
|
+
sources(): Promise<SourceInfo>;
|
|
35
|
+
/**
|
|
36
|
+
* Graceful shutdown — close file handles, DB connections, etc.
|
|
37
|
+
*/
|
|
38
|
+
close(): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/stores/base.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEnF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAEhC;;;;;;OAMG;IACH,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EAAE,EACnB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAE1B;;;OAGG;IACH,KAAK,CACH,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,GACzD,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE7C;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAE/B;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/stores/base.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files directory store adapter — general-purpose file memory with a JSON metadata index.
|
|
3
|
+
* Stores arbitrary content as flat files; maintains a sidecar index for fast lookup.
|
|
4
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
5
|
+
*/
|
|
6
|
+
import type { MemoryEntry, RankedEntry, SourceInfo } from '../types.js';
|
|
7
|
+
import type { MemoryStore } from './base.js';
|
|
8
|
+
export declare class FilesStore implements MemoryStore {
|
|
9
|
+
readonly type: 'files';
|
|
10
|
+
readonly name: string;
|
|
11
|
+
private readonly filesPath;
|
|
12
|
+
private readonly indexPath;
|
|
13
|
+
constructor(filesPath: string);
|
|
14
|
+
private readIndex;
|
|
15
|
+
private writeIndex;
|
|
16
|
+
isAvailable(): Promise<boolean>;
|
|
17
|
+
search(query: string, embedding: number[], limit: number): Promise<RankedEntry[]>;
|
|
18
|
+
store(entry: Omit<MemoryEntry, 'id' | 'createdAt' | 'updatedAt'>): Promise<string>;
|
|
19
|
+
get(id: string): Promise<MemoryEntry | null>;
|
|
20
|
+
sources(): Promise<SourceInfo>;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/stores/files.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAe7C,qBAAa,UAAW,YAAW,WAAW;IAC5C,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,YAAY,SAAS,EAAE,MAAM,EAI5B;IAED,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,UAAU;IAKZ,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAOpC;IAEK,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAoCtF;IAEK,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBvF;IAEK,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAmBjD;IAEK,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAgBnC;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;CACF"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files directory store adapter — general-purpose file memory with a JSON metadata index.
|
|
3
|
+
* Stores arbitrary content as flat files; maintains a sidecar index for fast lookup.
|
|
4
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync, statSync, existsSync, } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { newId, nowIso, keywordScore, cosineSimilarity } from './utils.js';
|
|
9
|
+
const INDEX_FILENAME = '.mmind-index.json';
|
|
10
|
+
export class FilesStore {
|
|
11
|
+
type = 'files';
|
|
12
|
+
name;
|
|
13
|
+
filesPath;
|
|
14
|
+
indexPath;
|
|
15
|
+
constructor(filesPath) {
|
|
16
|
+
this.filesPath = filesPath;
|
|
17
|
+
this.indexPath = join(filesPath, INDEX_FILENAME);
|
|
18
|
+
this.name = `files:${filesPath}`;
|
|
19
|
+
}
|
|
20
|
+
readIndex() {
|
|
21
|
+
if (!existsSync(this.indexPath))
|
|
22
|
+
return {};
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(readFileSync(this.indexPath, 'utf-8'));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
writeIndex(index) {
|
|
31
|
+
mkdirSync(this.filesPath, { recursive: true });
|
|
32
|
+
writeFileSync(this.indexPath, JSON.stringify(index, null, 2), 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
async isAvailable() {
|
|
35
|
+
try {
|
|
36
|
+
mkdirSync(this.filesPath, { recursive: true });
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async search(query, embedding, limit) {
|
|
44
|
+
const index = this.readIndex();
|
|
45
|
+
const results = [];
|
|
46
|
+
for (const [id, meta] of Object.entries(index)) {
|
|
47
|
+
const filePath = join(this.filesPath, meta.filename);
|
|
48
|
+
let content = '';
|
|
49
|
+
try {
|
|
50
|
+
content = readFileSync(filePath, 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const vecScore = embedding.length > 0 && meta.embedding
|
|
56
|
+
? cosineSimilarity(embedding, meta.embedding)
|
|
57
|
+
: 0;
|
|
58
|
+
const kwScore = keywordScore(content, query);
|
|
59
|
+
const score = embedding.length > 0
|
|
60
|
+
? vecScore * 0.7 + kwScore * 0.3
|
|
61
|
+
: kwScore;
|
|
62
|
+
if (score > 0) {
|
|
63
|
+
const entry = {
|
|
64
|
+
id,
|
|
65
|
+
content,
|
|
66
|
+
metadata: meta.metadata,
|
|
67
|
+
storeType: this.type,
|
|
68
|
+
createdAt: meta.createdAt,
|
|
69
|
+
updatedAt: meta.updatedAt,
|
|
70
|
+
embedding: meta.embedding,
|
|
71
|
+
};
|
|
72
|
+
results.push({ entry, score, source: `${this.name}/${meta.filename}` });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
76
|
+
}
|
|
77
|
+
async store(entry) {
|
|
78
|
+
mkdirSync(this.filesPath, { recursive: true });
|
|
79
|
+
const id = newId();
|
|
80
|
+
const now = nowIso();
|
|
81
|
+
const filename = `${id}.txt`;
|
|
82
|
+
const filePath = join(this.filesPath, filename);
|
|
83
|
+
writeFileSync(filePath, entry.content, 'utf-8');
|
|
84
|
+
const index = this.readIndex();
|
|
85
|
+
index[id] = {
|
|
86
|
+
id,
|
|
87
|
+
filename,
|
|
88
|
+
metadata: entry.metadata,
|
|
89
|
+
storeType: entry.storeType,
|
|
90
|
+
createdAt: now,
|
|
91
|
+
updatedAt: now,
|
|
92
|
+
embedding: entry.embedding,
|
|
93
|
+
};
|
|
94
|
+
this.writeIndex(index);
|
|
95
|
+
return id;
|
|
96
|
+
}
|
|
97
|
+
async get(id) {
|
|
98
|
+
const index = this.readIndex();
|
|
99
|
+
const meta = index[id];
|
|
100
|
+
if (!meta)
|
|
101
|
+
return null;
|
|
102
|
+
const filePath = join(this.filesPath, meta.filename);
|
|
103
|
+
try {
|
|
104
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
105
|
+
return {
|
|
106
|
+
id,
|
|
107
|
+
content,
|
|
108
|
+
metadata: meta.metadata,
|
|
109
|
+
storeType: this.type,
|
|
110
|
+
createdAt: meta.createdAt,
|
|
111
|
+
updatedAt: meta.updatedAt,
|
|
112
|
+
embedding: meta.embedding,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async sources() {
|
|
120
|
+
try {
|
|
121
|
+
const index = this.readIndex();
|
|
122
|
+
let sizeBytes = 0;
|
|
123
|
+
try {
|
|
124
|
+
sizeBytes = statSync(this.filesPath).size;
|
|
125
|
+
}
|
|
126
|
+
catch { /* ok */ }
|
|
127
|
+
return {
|
|
128
|
+
type: this.type,
|
|
129
|
+
name: this.name,
|
|
130
|
+
available: true,
|
|
131
|
+
path: this.filesPath,
|
|
132
|
+
entryCount: Object.keys(index).length,
|
|
133
|
+
sizeBytes,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
return { type: this.type, name: this.name, available: false, error: String(err) };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async close() {
|
|
141
|
+
// No open handles
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/stores/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { MemoryStore } from './base.js';
|
|
2
|
+
export { SqliteStore } from './sqlite.js';
|
|
3
|
+
export { LevelDbStore } from './leveldb.js';
|
|
4
|
+
export { MarkdownStore } from './markdown.js';
|
|
5
|
+
export { FilesStore } from './files.js';
|
|
6
|
+
export { VectorStore } from './vector.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LevelDB store adapter — fast key-value memory for agent session state.
|
|
3
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
4
|
+
*/
|
|
5
|
+
import type { MemoryEntry, RankedEntry, SourceInfo } from '../types.js';
|
|
6
|
+
import type { MemoryStore } from './base.js';
|
|
7
|
+
export declare class LevelDbStore implements MemoryStore {
|
|
8
|
+
readonly type: 'leveldb';
|
|
9
|
+
readonly name: string;
|
|
10
|
+
private db;
|
|
11
|
+
private readonly dbPath;
|
|
12
|
+
constructor(dbPath?: string);
|
|
13
|
+
private getDb;
|
|
14
|
+
isAvailable(): Promise<boolean>;
|
|
15
|
+
search(query: string, embedding: number[], limit: number): Promise<RankedEntry[]>;
|
|
16
|
+
store(entry: Omit<MemoryEntry, 'id' | 'createdAt' | 'updatedAt'>): Promise<string>;
|
|
17
|
+
get(id: string): Promise<MemoryEntry | null>;
|
|
18
|
+
sources(): Promise<SourceInfo>;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=leveldb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leveldb.d.ts","sourceRoot":"","sources":["../../src/stores/leveldb.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAK7C,qBAAa,YAAa,YAAW,WAAW;IAC9C,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAU;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,OAAO,CAAC,EAAE,CAAsC;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC,YAAY,MAAM,SAAe,EAGhC;YAEa,KAAK;IAQb,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAOpC;IAEK,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAoBtF;IAEK,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,WAAW,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAOvF;IAEK,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAQjD;IAEK,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,CAkBnC;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAG3B;CACF"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LevelDB store adapter — fast key-value memory for agent session state.
|
|
3
|
+
* Copyright Old Cart Technology LLC — MIT License
|
|
4
|
+
*/
|
|
5
|
+
import { Level } from 'level';
|
|
6
|
+
import { statSync } from 'fs';
|
|
7
|
+
import { newId, nowIso, cosineSimilarity, keywordScore } from './utils.js';
|
|
8
|
+
const DEFAULT_PATH = '.mmind/leveldb';
|
|
9
|
+
export class LevelDbStore {
|
|
10
|
+
type = 'leveldb';
|
|
11
|
+
name;
|
|
12
|
+
db = null;
|
|
13
|
+
dbPath;
|
|
14
|
+
constructor(dbPath = DEFAULT_PATH) {
|
|
15
|
+
this.dbPath = dbPath;
|
|
16
|
+
this.name = `leveldb:${dbPath}`;
|
|
17
|
+
}
|
|
18
|
+
async getDb() {
|
|
19
|
+
if (!this.db) {
|
|
20
|
+
this.db = new Level(this.dbPath, { valueEncoding: 'utf8' });
|
|
21
|
+
await this.db.open();
|
|
22
|
+
}
|
|
23
|
+
return this.db;
|
|
24
|
+
}
|
|
25
|
+
async isAvailable() {
|
|
26
|
+
try {
|
|
27
|
+
await this.getDb();
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async search(query, embedding, limit) {
|
|
35
|
+
const db = await this.getDb();
|
|
36
|
+
const results = [];
|
|
37
|
+
for await (const value of db.values()) {
|
|
38
|
+
const entry = JSON.parse(value);
|
|
39
|
+
const vecScore = embedding.length > 0 && entry.embedding
|
|
40
|
+
? cosineSimilarity(embedding, entry.embedding)
|
|
41
|
+
: 0;
|
|
42
|
+
const kwScore = keywordScore(entry.content, query);
|
|
43
|
+
const score = embedding.length > 0
|
|
44
|
+
? vecScore * 0.7 + kwScore * 0.3
|
|
45
|
+
: kwScore;
|
|
46
|
+
if (score > 0) {
|
|
47
|
+
results.push({ entry, score, source: this.name });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return results.sort((a, b) => b.score - a.score).slice(0, limit);
|
|
51
|
+
}
|
|
52
|
+
async store(entry) {
|
|
53
|
+
const db = await this.getDb();
|
|
54
|
+
const id = newId();
|
|
55
|
+
const now = nowIso();
|
|
56
|
+
const full = { ...entry, id, createdAt: now, updatedAt: now };
|
|
57
|
+
await db.put(id, JSON.stringify(full));
|
|
58
|
+
return id;
|
|
59
|
+
}
|
|
60
|
+
async get(id) {
|
|
61
|
+
const db = await this.getDb();
|
|
62
|
+
try {
|
|
63
|
+
const raw = await db.get(id);
|
|
64
|
+
return JSON.parse(raw);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async sources() {
|
|
71
|
+
try {
|
|
72
|
+
const db = await this.getDb();
|
|
73
|
+
let count = 0;
|
|
74
|
+
for await (const _ of db.keys())
|
|
75
|
+
count++;
|
|
76
|
+
let sizeBytes;
|
|
77
|
+
try {
|
|
78
|
+
sizeBytes = statSync(this.dbPath).size;
|
|
79
|
+
}
|
|
80
|
+
catch { /* dir, not file */ }
|
|
81
|
+
return {
|
|
82
|
+
type: this.type,
|
|
83
|
+
name: this.name,
|
|
84
|
+
available: true,
|
|
85
|
+
path: this.dbPath,
|
|
86
|
+
entryCount: count,
|
|
87
|
+
sizeBytes,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return { type: this.type, name: this.name, available: false, error: String(err) };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async close() {
|
|
95
|
+
await this.db?.close();
|
|
96
|
+
this.db = null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=leveldb.js.map
|