@tekmidian/pai 0.66.1 → 0.66.2
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/dist/{aibroker-client-B8c42Lh8.mjs → aibroker-client-CHEEvJZW.mjs} +2 -2
- package/dist/{aibroker-client-C5Fw7DNz.mjs → aibroker-client-Dfv7j1Od.mjs} +3 -3
- package/dist/{aibroker-client-C5Fw7DNz.mjs.map → aibroker-client-Dfv7j1Od.mjs.map} +1 -1
- package/dist/{auto-route-o0BOsXn7.mjs → auto-route-BnizyALK.mjs} +65 -4
- package/dist/auto-route-BnizyALK.mjs.map +1 -0
- package/dist/auto-route-D6fW1Q8z.mjs +3 -0
- package/dist/{chain-CZfKIEq9.mjs → chain-D5876UQX.mjs} +3 -4
- package/dist/{chain-CZfKIEq9.mjs.map → chain-D5876UQX.mjs.map} +1 -1
- package/dist/cli/index.mjs +18 -23
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.mjs +17 -22
- package/dist/{config-BbLFD7Uf.mjs → config-C-LiGlop.mjs} +2 -2
- package/dist/{config-BbLFD7Uf.mjs.map → config-C-LiGlop.mjs.map} +1 -1
- package/dist/{config-YinjgXEJ.mjs → config-DKIA7wgF.mjs} +1 -1
- package/dist/{context-handover-cache-pkHzmL3c.mjs → context-handover-cache-9PGvXRIw.mjs} +984 -10
- package/dist/context-handover-cache-9PGvXRIw.mjs.map +1 -0
- package/dist/daemon/index.mjs +17 -19
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/daemon-BUgNvMUh.mjs +5029 -0
- package/dist/daemon-BUgNvMUh.mjs.map +1 -0
- package/dist/daemon-DL0eglMJ.mjs +18 -0
- package/dist/daemon-mcp/index.mjs +12 -9
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{embeddings-CcscYWwk.mjs → embeddings-CEBGrzwu.mjs} +1 -1
- package/dist/{embeddings-Bx3q0QOY.mjs → embeddings-DOLZnT1X.mjs} +1 -1
- package/dist/{embeddings-Bx3q0QOY.mjs.map → embeddings-DOLZnT1X.mjs.map} +1 -1
- package/dist/{env-JNEIrQWg.mjs → env-DiolswKQ.mjs} +1 -1
- package/dist/{env-JNEIrQWg.mjs.map → env-DiolswKQ.mjs.map} +1 -1
- package/dist/factory-DrBg24UT.mjs +8 -0
- package/dist/factory-Ypf8r0fK.mjs +2892 -0
- package/dist/factory-Ypf8r0fK.mjs.map +1 -0
- package/dist/{fallback-CIiq2Raw.mjs → fallback-D51R4shY.mjs} +23 -11
- package/dist/fallback-D51R4shY.mjs.map +1 -0
- package/dist/{chunker-BH4i-F2b.mjs → helpers-CZsi_49C.mjs} +221 -2
- package/dist/helpers-CZsi_49C.mjs.map +1 -0
- package/dist/hooks/worker-status-line.mjs.map +2 -2
- package/dist/index.mjs +6 -7
- package/dist/{ipc-client-D16Xw6Uo.mjs → ipc-client-BLYX51nG.mjs} +2 -2
- package/dist/{ipc-client-D16Xw6Uo.mjs.map → ipc-client-BLYX51nG.mjs.map} +1 -1
- package/dist/main-resolver-BYgP86C1.mjs +7 -0
- package/dist/{main-resolver-DPPwHNtn.mjs → main-resolver-kVkMJZUD.mjs} +6 -6
- package/dist/{main-resolver-DPPwHNtn.mjs.map → main-resolver-kVkMJZUD.mjs.map} +1 -1
- package/dist/{migrate-BD7D8EEh.mjs → migrate-Bq0esMOa.mjs} +2 -2
- package/dist/{migrate-BD7D8EEh.mjs.map → migrate-Bq0esMOa.mjs.map} +1 -1
- package/dist/{pai-marker-D1MMswkz.mjs → pai-marker-DXVpFsYz.mjs} +1 -1
- package/dist/{pai-marker-D1MMswkz.mjs.map → pai-marker-DXVpFsYz.mjs.map} +1 -1
- package/dist/{planner-Dm5Acyo1.mjs → planner-D5yt0EfA.mjs} +10 -7
- package/dist/{planner-Dm5Acyo1.mjs.map → planner-D5yt0EfA.mjs.map} +1 -1
- package/dist/postgres-BmLr0MUm.mjs +5 -0
- package/dist/{postgres-Ceqsa64C.mjs → postgres-D3xc2RB4.mjs} +364 -9
- package/dist/postgres-D3xc2RB4.mjs.map +1 -0
- package/dist/{program-JJ0TwSHI.mjs → program-DIXRtAFy.mjs} +77 -70
- package/dist/program-DIXRtAFy.mjs.map +1 -0
- package/dist/{query-feedback-BIaZTTFO.mjs → query-feedback-B7FYE4JR.mjs} +1 -1
- package/dist/{query-feedback-BIaZTTFO.mjs.map → query-feedback-B7FYE4JR.mjs.map} +1 -1
- package/dist/{reranker-DKv80KO5.mjs → reranker-3lnggwgq.mjs} +1 -1
- package/dist/{reranker-DKv80KO5.mjs.map → reranker-3lnggwgq.mjs.map} +1 -1
- package/dist/{reranker-CFiEzHQu.mjs → reranker-CZ2mP4cf.mjs} +1 -1
- package/dist/router-BK-hFeQ6.mjs +3 -0
- package/dist/{router-a5G7q8_3.mjs → router-BaTbc9VX.mjs} +2 -2
- package/dist/{router-a5G7q8_3.mjs.map → router-BaTbc9VX.mjs.map} +1 -1
- package/dist/{run-BCpbxqA5.mjs → run-CLYTOm0x.mjs} +599 -22
- package/dist/run-CLYTOm0x.mjs.map +1 -0
- package/dist/{run-env-DRcp7A8K.mjs → run-env-Bnmf6bRI.mjs} +2 -2
- package/dist/{run-env-DRcp7A8K.mjs.map → run-env-Bnmf6bRI.mjs.map} +1 -1
- package/dist/{runtime-paths-CHTg3ywb.mjs → runtime-paths-D3FKAm69.mjs} +1 -1
- package/dist/{runtime-paths-CHTg3ywb.mjs.map → runtime-paths-D3FKAm69.mjs.map} +1 -1
- package/dist/{search-BOSphCJ1.mjs → search-CNAGTiJP.mjs} +2 -2
- package/dist/{search-BOSphCJ1.mjs.map → search-CNAGTiJP.mjs.map} +1 -1
- package/dist/{sources-CijVso3n.mjs → sources-CM2g-CLT.mjs} +2 -2
- package/dist/{sources-CijVso3n.mjs.map → sources-CM2g-CLT.mjs.map} +1 -1
- package/dist/{stop-words-BdQuaE9K.mjs → stop-words-DtxaTWU_.mjs} +1 -1
- package/dist/{stop-words-BdQuaE9K.mjs.map → stop-words-DtxaTWU_.mjs.map} +1 -1
- package/dist/{utils-C9HsYDpO.mjs → utils-DddRwMsG.mjs} +2 -2
- package/dist/{utils-C9HsYDpO.mjs.map → utils-DddRwMsG.mjs.map} +1 -1
- package/dist/{zettelkasten-CtGHQWPU.mjs → zettelkasten-BhZMvmoK.mjs} +149 -7
- package/dist/zettelkasten-BhZMvmoK.mjs.map +1 -0
- package/dist/zettelkasten-BwV1pluL.mjs +5 -0
- package/docs/commands/worker.md +2 -0
- package/package.json +1 -1
- package/dist/async-C2Bm_Lal.mjs +0 -300
- package/dist/async-C2Bm_Lal.mjs.map +0 -1
- package/dist/auto-route-o0BOsXn7.mjs.map +0 -1
- package/dist/chunker-BH4i-F2b.mjs.map +0 -1
- package/dist/clusters-BCtD3fbe.mjs +0 -169
- package/dist/clusters-BCtD3fbe.mjs.map +0 -1
- package/dist/context-handover-cache-pkHzmL3c.mjs.map +0 -1
- package/dist/daemon-DXlaVCkw.mjs +0 -1741
- package/dist/daemon-DXlaVCkw.mjs.map +0 -1
- package/dist/daemon-DyORmxZu.mjs +0 -19
- package/dist/detector-BEPFsINR.mjs +0 -3
- package/dist/detector-BRTtAWrM.mjs +0 -65
- package/dist/detector-BRTtAWrM.mjs.map +0 -1
- package/dist/factory-CNpPQQ-5.mjs +0 -249
- package/dist/factory-CNpPQQ-5.mjs.map +0 -1
- package/dist/factory-DY7x73mE.mjs +0 -3
- package/dist/fallback-CIiq2Raw.mjs.map +0 -1
- package/dist/federation-db-BTyoufBh.mjs +0 -139
- package/dist/federation-db-BTyoufBh.mjs.map +0 -1
- package/dist/federation-db-HfIFc7FG.mjs +0 -3
- package/dist/helpers-BRCJg0G3.mjs +0 -223
- package/dist/helpers-BRCJg0G3.mjs.map +0 -1
- package/dist/indexer-backend-DFF2FrYx.mjs +0 -5
- package/dist/indexer-backend-isSLg6yE.mjs +0 -1
- package/dist/kg-entity-DCOcsVFD.mjs +0 -29
- package/dist/kg-entity-DCOcsVFD.mjs.map +0 -1
- package/dist/latent-ideas-wwAXquGe.mjs +0 -191
- package/dist/latent-ideas-wwAXquGe.mjs.map +0 -1
- package/dist/link-boost-CnI7UVMJ.mjs +0 -34
- package/dist/link-boost-CnI7UVMJ.mjs.map +0 -1
- package/dist/main-resolver-BKz_OuEh.mjs +0 -7
- package/dist/merge-2gqRPFu2.mjs +0 -3
- package/dist/merge-DgU9OgZy.mjs +0 -6
- package/dist/merge-DgU9OgZy.mjs.map +0 -1
- package/dist/module-paths-DdRzbkUI.mjs +0 -44
- package/dist/module-paths-DdRzbkUI.mjs.map +0 -1
- package/dist/neighborhood-2FsmzoxG.mjs +0 -114
- package/dist/neighborhood-2FsmzoxG.mjs.map +0 -1
- package/dist/note-context-De63k8na.mjs +0 -106
- package/dist/note-context-De63k8na.mjs.map +0 -1
- package/dist/postgres-Ceqsa64C.mjs.map +0 -1
- package/dist/program-JJ0TwSHI.mjs.map +0 -1
- package/dist/query-feedback-B_iigYj-.mjs +0 -3
- package/dist/registry-db-C7voqML9.mjs +0 -213
- package/dist/registry-db-C7voqML9.mjs.map +0 -1
- package/dist/registry-db-JHPhA8vF.mjs +0 -3
- package/dist/registry-postgres-nOfyBSIW.mjs +0 -795
- package/dist/registry-postgres-nOfyBSIW.mjs.map +0 -1
- package/dist/registry-sqlite-DrCW1aRK.mjs +0 -593
- package/dist/registry-sqlite-DrCW1aRK.mjs.map +0 -1
- package/dist/router-CXUGsv85.mjs +0 -3
- package/dist/run-BCpbxqA5.mjs.map +0 -1
- package/dist/search-Bf3Kub3F.mjs +0 -3
- package/dist/server-DgmAHyFK.mjs +0 -403
- package/dist/server-DgmAHyFK.mjs.map +0 -1
- package/dist/session-keepalive-DxjTHGcK.mjs +0 -582
- package/dist/session-keepalive-DxjTHGcK.mjs.map +0 -1
- package/dist/sqlite-CqsTy6xo.mjs +0 -936
- package/dist/sqlite-CqsTy6xo.mjs.map +0 -1
- package/dist/state-DW8zdweW.mjs +0 -3
- package/dist/state-HyjqTihC.mjs +0 -76
- package/dist/state-HyjqTihC.mjs.map +0 -1
- package/dist/themes-CTaOj3e1.mjs +0 -148
- package/dist/themes-CTaOj3e1.mjs.map +0 -1
- package/dist/tools-BcOKPBtg.mjs +0 -1489
- package/dist/tools-BcOKPBtg.mjs.map +0 -1
- package/dist/tools-C-_l0o8Z.mjs +0 -6
- package/dist/trace-CtV0RO6n.mjs +0 -137
- package/dist/trace-CtV0RO6n.mjs.map +0 -1
- package/dist/utils-BrX9FLeK.mjs +0 -3
- package/dist/vault-indexer-BG1sEVyM.mjs +0 -537
- package/dist/vault-indexer-BG1sEVyM.mjs.map +0 -1
- package/dist/wakeup-D8n33hYV.mjs +0 -335
- package/dist/wakeup-D8n33hYV.mjs.map +0 -1
- package/dist/work-queue-worker-4tLa5gpQ.mjs +0 -567
- package/dist/work-queue-worker-4tLa5gpQ.mjs.map +0 -1
- package/dist/work-queue-worker-rFZvTO3p.mjs +0 -11
- package/dist/zettelkasten-CtGHQWPU.mjs.map +0 -1
package/dist/sqlite-CqsTy6xo.mjs
DELETED
|
@@ -1,936 +0,0 @@
|
|
|
1
|
-
import { i as fileContentHash, n as chunkMarkdown, s as sha256File } from "./chunker-BH4i-F2b.mjs";
|
|
2
|
-
import { a as parseSessionTitleChunk, c as yieldToEventLoop, i as isPathTooBroadForContentScan, n as chunkId, o as walkContentFiles, r as detectTier, s as walkMdFiles, t as INDEX_YIELD_EVERY } from "./helpers-BRCJg0G3.mjs";
|
|
3
|
-
import { t as STOP_WORDS } from "./stop-words-BdQuaE9K.mjs";
|
|
4
|
-
import { n as buildFtsQuery, r as isQuerySyntaxError } from "./search-BOSphCJ1.mjs";
|
|
5
|
-
import { n as cosineSimilarity, r as deserializeEmbedding } from "./embeddings-Bx3q0QOY.mjs";
|
|
6
|
-
import { t as entityContentId } from "./kg-entity-DCOcsVFD.mjs";
|
|
7
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
8
|
-
import { basename, join, relative } from "node:path";
|
|
9
|
-
|
|
10
|
-
//#region src/storage/sqlite/indexer.ts
|
|
11
|
-
/**
|
|
12
|
-
* Synchronous (SQLite) indexer — moved from memory/indexer/sync.ts so the
|
|
13
|
-
* only file that opens prepared statements against the federation database
|
|
14
|
-
* lives under src/storage/ (design doc docs/design/postgres-only.md, unit 7).
|
|
15
|
-
*
|
|
16
|
-
* Scans project memory/ and Notes/ directories, chunks markdown files, and
|
|
17
|
-
* inserts the resulting chunks into the federation memory store for BM25
|
|
18
|
-
* search. Uses raw better-sqlite3 Database directly for maximum SQLite
|
|
19
|
-
* performance (synchronous transactions, no serialisation overhead) — the
|
|
20
|
-
* reason SQLiteBackend keeps this path instead of routing through the
|
|
21
|
-
* generic (StorageBackend) indexer that Postgres uses.
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* Index a single file into the federation database.
|
|
25
|
-
*
|
|
26
|
-
* @returns true if the file was re-indexed (changed or new), false if skipped.
|
|
27
|
-
*/
|
|
28
|
-
function indexFile(db, projectId, rootPath, relativePath, source, tier) {
|
|
29
|
-
const absPath = join(rootPath, relativePath);
|
|
30
|
-
let content;
|
|
31
|
-
let stat;
|
|
32
|
-
try {
|
|
33
|
-
content = readFileSync(absPath, "utf8");
|
|
34
|
-
stat = statSync(absPath);
|
|
35
|
-
} catch {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
const hash = fileContentHash(content);
|
|
39
|
-
const mtime = Math.floor(stat.mtimeMs);
|
|
40
|
-
const size = stat.size;
|
|
41
|
-
if (db.prepare("SELECT hash FROM memory_files WHERE project_id = ? AND path = ?").get(projectId, relativePath)?.hash === hash) return false;
|
|
42
|
-
const oldChunkIds = db.prepare("SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?").all(projectId, relativePath);
|
|
43
|
-
const deleteFts = db.prepare("DELETE FROM memory_fts WHERE id = ?");
|
|
44
|
-
const deleteChunk = db.prepare("DELETE FROM memory_chunks WHERE project_id = ? AND path = ?");
|
|
45
|
-
db.transaction(() => {
|
|
46
|
-
for (const row of oldChunkIds) deleteFts.run(row.id);
|
|
47
|
-
deleteChunk.run(projectId, relativePath);
|
|
48
|
-
})();
|
|
49
|
-
const chunks = chunkMarkdown(content);
|
|
50
|
-
const insertChunk = db.prepare(`
|
|
51
|
-
INSERT INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)
|
|
52
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
53
|
-
`);
|
|
54
|
-
const insertFts = db.prepare(`
|
|
55
|
-
INSERT INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)
|
|
56
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
57
|
-
`);
|
|
58
|
-
const upsertFile = db.prepare(`
|
|
59
|
-
INSERT INTO memory_files (project_id, path, source, tier, hash, mtime, size)
|
|
60
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
61
|
-
ON CONFLICT(project_id, path) DO UPDATE SET
|
|
62
|
-
source = excluded.source,
|
|
63
|
-
tier = excluded.tier,
|
|
64
|
-
hash = excluded.hash,
|
|
65
|
-
mtime = excluded.mtime,
|
|
66
|
-
size = excluded.size
|
|
67
|
-
`);
|
|
68
|
-
const updatedAt = Date.now();
|
|
69
|
-
db.transaction(() => {
|
|
70
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
71
|
-
const chunk = chunks[i];
|
|
72
|
-
const id = chunkId(projectId, relativePath, i, chunk.startLine, chunk.endLine);
|
|
73
|
-
insertChunk.run(id, projectId, source, tier, relativePath, chunk.startLine, chunk.endLine, chunk.hash, chunk.text, updatedAt);
|
|
74
|
-
insertFts.run(chunk.text, id, projectId, relativePath, source, tier, chunk.startLine, chunk.endLine);
|
|
75
|
-
}
|
|
76
|
-
upsertFile.run(projectId, relativePath, source, tier, hash, mtime, size);
|
|
77
|
-
})();
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Index all memory, Notes, and content files for a single registered project.
|
|
82
|
-
*/
|
|
83
|
-
async function indexProject(db, projectId, rootPath, claudeNotesDir) {
|
|
84
|
-
const result = {
|
|
85
|
-
filesProcessed: 0,
|
|
86
|
-
chunksCreated: 0,
|
|
87
|
-
filesSkipped: 0
|
|
88
|
-
};
|
|
89
|
-
const filesToIndex = [];
|
|
90
|
-
const rootMemoryMd = join(rootPath, "MEMORY.md");
|
|
91
|
-
if (existsSync(rootMemoryMd)) filesToIndex.push({
|
|
92
|
-
absPath: rootMemoryMd,
|
|
93
|
-
rootBase: rootPath,
|
|
94
|
-
source: "memory",
|
|
95
|
-
tier: "evergreen"
|
|
96
|
-
});
|
|
97
|
-
const memoryDir = join(rootPath, "memory");
|
|
98
|
-
for (const absPath of walkMdFiles(memoryDir)) {
|
|
99
|
-
const tier = detectTier(relative(rootPath, absPath));
|
|
100
|
-
filesToIndex.push({
|
|
101
|
-
absPath,
|
|
102
|
-
rootBase: rootPath,
|
|
103
|
-
source: "memory",
|
|
104
|
-
tier
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
const notesDir = join(rootPath, "Notes");
|
|
108
|
-
for (const absPath of walkMdFiles(notesDir)) filesToIndex.push({
|
|
109
|
-
absPath,
|
|
110
|
-
rootBase: rootPath,
|
|
111
|
-
source: "notes",
|
|
112
|
-
tier: "session"
|
|
113
|
-
});
|
|
114
|
-
{
|
|
115
|
-
const titleInsertChunk = db.prepare(`
|
|
116
|
-
INSERT OR IGNORE INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)
|
|
117
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
118
|
-
`);
|
|
119
|
-
const titleInsertFts = db.prepare(`
|
|
120
|
-
INSERT OR IGNORE INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)
|
|
121
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
122
|
-
`);
|
|
123
|
-
const updatedAt = Date.now();
|
|
124
|
-
for (const absPath of walkMdFiles(notesDir)) {
|
|
125
|
-
const text = parseSessionTitleChunk(basename(absPath));
|
|
126
|
-
if (!text) continue;
|
|
127
|
-
const syntheticPath = `${relative(rootPath, absPath)}::title`;
|
|
128
|
-
const id = chunkId(projectId, syntheticPath, 0, 0, 0);
|
|
129
|
-
const hash = sha256File(text);
|
|
130
|
-
db.transaction(() => {
|
|
131
|
-
titleInsertChunk.run(id, projectId, "notes", "session", syntheticPath, 0, 0, hash, text, updatedAt);
|
|
132
|
-
titleInsertFts.run(text, id, projectId, syntheticPath, "notes", "session", 0, 0);
|
|
133
|
-
})();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (!isPathTooBroadForContentScan(rootPath)) for (const absPath of walkContentFiles(rootPath)) filesToIndex.push({
|
|
137
|
-
absPath,
|
|
138
|
-
rootBase: rootPath,
|
|
139
|
-
source: "content",
|
|
140
|
-
tier: "topic"
|
|
141
|
-
});
|
|
142
|
-
if (claudeNotesDir && claudeNotesDir !== notesDir) {
|
|
143
|
-
for (const absPath of walkMdFiles(claudeNotesDir)) filesToIndex.push({
|
|
144
|
-
absPath,
|
|
145
|
-
rootBase: claudeNotesDir,
|
|
146
|
-
source: "notes",
|
|
147
|
-
tier: "session"
|
|
148
|
-
});
|
|
149
|
-
{
|
|
150
|
-
const updatedAt = Date.now();
|
|
151
|
-
const titleInsertChunk2 = db.prepare(`
|
|
152
|
-
INSERT OR IGNORE INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)
|
|
153
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
154
|
-
`);
|
|
155
|
-
const titleInsertFts2 = db.prepare(`
|
|
156
|
-
INSERT OR IGNORE INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)
|
|
157
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
158
|
-
`);
|
|
159
|
-
for (const absPath of walkMdFiles(claudeNotesDir)) {
|
|
160
|
-
const text = parseSessionTitleChunk(basename(absPath));
|
|
161
|
-
if (!text) continue;
|
|
162
|
-
const syntheticPath = `${relative(claudeNotesDir, absPath)}::title`;
|
|
163
|
-
const id = chunkId(projectId, syntheticPath, 0, 0, 0);
|
|
164
|
-
const hash = sha256File(text);
|
|
165
|
-
db.transaction(() => {
|
|
166
|
-
titleInsertChunk2.run(id, projectId, "notes", "session", syntheticPath, 0, 0, hash, text, updatedAt);
|
|
167
|
-
titleInsertFts2.run(text, id, projectId, syntheticPath, "notes", "session", 0, 0);
|
|
168
|
-
})();
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (claudeNotesDir.endsWith("/Notes")) {
|
|
172
|
-
const claudeProjectDir = claudeNotesDir.slice(0, -6);
|
|
173
|
-
const claudeMemoryDir = join(claudeProjectDir, "memory");
|
|
174
|
-
const claudeMemoryMd = join(claudeProjectDir, "MEMORY.md");
|
|
175
|
-
if (existsSync(claudeMemoryMd)) filesToIndex.push({
|
|
176
|
-
absPath: claudeMemoryMd,
|
|
177
|
-
rootBase: claudeProjectDir,
|
|
178
|
-
source: "memory",
|
|
179
|
-
tier: "evergreen"
|
|
180
|
-
});
|
|
181
|
-
for (const absPath of walkMdFiles(claudeMemoryDir)) {
|
|
182
|
-
const tier = detectTier(relative(claudeProjectDir, absPath));
|
|
183
|
-
filesToIndex.push({
|
|
184
|
-
absPath,
|
|
185
|
-
rootBase: claudeProjectDir,
|
|
186
|
-
source: "memory",
|
|
187
|
-
tier
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
await yieldToEventLoop();
|
|
193
|
-
let filesSinceYield = 0;
|
|
194
|
-
for (const { absPath, rootBase, source, tier } of filesToIndex) {
|
|
195
|
-
if (filesSinceYield >= INDEX_YIELD_EVERY) {
|
|
196
|
-
await yieldToEventLoop();
|
|
197
|
-
filesSinceYield = 0;
|
|
198
|
-
}
|
|
199
|
-
filesSinceYield++;
|
|
200
|
-
const relPath = relative(rootBase, absPath);
|
|
201
|
-
if (indexFile(db, projectId, rootBase, relPath, source, tier)) {
|
|
202
|
-
const count = db.prepare("SELECT COUNT(*) as n FROM memory_chunks WHERE project_id = ? AND path = ?").get(projectId, relPath);
|
|
203
|
-
result.filesProcessed++;
|
|
204
|
-
result.chunksCreated += count.n;
|
|
205
|
-
} else result.filesSkipped++;
|
|
206
|
-
}
|
|
207
|
-
const livePaths = /* @__PURE__ */ new Set();
|
|
208
|
-
for (const { absPath, rootBase } of filesToIndex) livePaths.add(relative(rootBase, absPath));
|
|
209
|
-
const dbChunkPaths = db.prepare("SELECT DISTINCT path FROM memory_chunks WHERE project_id = ?").all(projectId);
|
|
210
|
-
const stalePaths = [];
|
|
211
|
-
for (const row of dbChunkPaths) {
|
|
212
|
-
const basePath = row.path.endsWith("::title") ? row.path.slice(0, -7) : row.path;
|
|
213
|
-
if (!livePaths.has(basePath)) stalePaths.push(row.path);
|
|
214
|
-
}
|
|
215
|
-
if (stalePaths.length > 0) {
|
|
216
|
-
const deleteChunksFts = db.prepare("DELETE FROM memory_fts WHERE id = ?");
|
|
217
|
-
const deleteChunks = db.prepare("DELETE FROM memory_chunks WHERE project_id = ? AND path = ?");
|
|
218
|
-
const deleteFile = db.prepare("DELETE FROM memory_files WHERE project_id = ? AND path = ?");
|
|
219
|
-
db.transaction(() => {
|
|
220
|
-
for (const stalePath of stalePaths) {
|
|
221
|
-
const chunkIds = db.prepare("SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?").all(projectId, stalePath);
|
|
222
|
-
for (const { id } of chunkIds) deleteChunksFts.run(id);
|
|
223
|
-
deleteChunks.run(projectId, stalePath);
|
|
224
|
-
deleteFile.run(projectId, stalePath);
|
|
225
|
-
}
|
|
226
|
-
})();
|
|
227
|
-
}
|
|
228
|
-
return result;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Index all active projects registered in the registry DB.
|
|
232
|
-
*
|
|
233
|
-
* Async: yields to the event loop between each project so that the daemon's
|
|
234
|
-
* Unix socket server can process IPC requests (e.g. status) while indexing.
|
|
235
|
-
*/
|
|
236
|
-
async function indexAllSqlite(db, registry) {
|
|
237
|
-
const projects = await registry.listProjects({ status: "active" });
|
|
238
|
-
const totals = {
|
|
239
|
-
filesProcessed: 0,
|
|
240
|
-
chunksCreated: 0,
|
|
241
|
-
filesSkipped: 0
|
|
242
|
-
};
|
|
243
|
-
for (const project of projects) {
|
|
244
|
-
await yieldToEventLoop();
|
|
245
|
-
const r = await indexProject(db, project.id, project.root_path, project.claude_notes_dir);
|
|
246
|
-
totals.filesProcessed += r.filesProcessed;
|
|
247
|
-
totals.chunksCreated += r.chunksCreated;
|
|
248
|
-
totals.filesSkipped += r.filesSkipped;
|
|
249
|
-
}
|
|
250
|
-
return {
|
|
251
|
-
projects: projects.length,
|
|
252
|
-
result: totals
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
//#endregion
|
|
257
|
-
//#region src/storage/sqlite/tunnels.ts
|
|
258
|
-
async function findTunnelsForSlugMap(db, slugMap, opts) {
|
|
259
|
-
const projectIds = Object.keys(slugMap).map(Number);
|
|
260
|
-
if (projectIds.length < 2) return {
|
|
261
|
-
tunnels: [],
|
|
262
|
-
projects_analyzed: projectIds.length,
|
|
263
|
-
total_concepts_evaluated: 0
|
|
264
|
-
};
|
|
265
|
-
let candidateTerms = [];
|
|
266
|
-
try {
|
|
267
|
-
candidateTerms = db.prepare(`SELECT term, SUM(doc) AS doc_count, SUM(cnt) AS total_cnt
|
|
268
|
-
FROM memory_fts_v
|
|
269
|
-
GROUP BY term
|
|
270
|
-
HAVING SUM(cnt) >= ?
|
|
271
|
-
ORDER BY SUM(doc) DESC
|
|
272
|
-
LIMIT 500`).all(opts.min_occurrences).map((r) => r.term).filter((t) => t.length >= 3 && !STOP_WORDS.has(t));
|
|
273
|
-
} catch {
|
|
274
|
-
const sampleRows = db.prepare(`SELECT LOWER(text) AS text FROM memory_chunks
|
|
275
|
-
WHERE LENGTH(text) > 20
|
|
276
|
-
ORDER BY RANDOM()
|
|
277
|
-
LIMIT 2000`).all();
|
|
278
|
-
const freq = /* @__PURE__ */ new Map();
|
|
279
|
-
for (const { text } of sampleRows) {
|
|
280
|
-
const tokens = text.split(/[\s\p{P}]+/u).filter(Boolean).filter((t) => t.length >= 3 && !STOP_WORDS.has(t));
|
|
281
|
-
for (const t of tokens) freq.set(t, (freq.get(t) ?? 0) + 1);
|
|
282
|
-
}
|
|
283
|
-
candidateTerms = [...freq.entries()].filter(([, n]) => n >= opts.min_occurrences).sort((a, b) => b[1] - a[1]).slice(0, 200).map(([t]) => t);
|
|
284
|
-
}
|
|
285
|
-
if (candidateTerms.length === 0) return {
|
|
286
|
-
tunnels: [],
|
|
287
|
-
projects_analyzed: projectIds.length,
|
|
288
|
-
total_concepts_evaluated: 0
|
|
289
|
-
};
|
|
290
|
-
const tunnels = [];
|
|
291
|
-
for (const term of candidateTerms) try {
|
|
292
|
-
const rows = db.prepare(`SELECT c.project_id, COUNT(*) AS cnt,
|
|
293
|
-
MIN(c.updated_at) AS first_seen,
|
|
294
|
-
MAX(c.updated_at) AS last_seen
|
|
295
|
-
FROM memory_fts f
|
|
296
|
-
JOIN memory_chunks c ON c.id = f.id
|
|
297
|
-
WHERE memory_fts MATCH ?
|
|
298
|
-
AND c.project_id IN (${projectIds.map(() => "?").join(", ")})
|
|
299
|
-
GROUP BY c.project_id`).all(`"${term.replace(/"/g, "\"\"")}"`, ...projectIds);
|
|
300
|
-
if (rows.length < opts.min_projects) continue;
|
|
301
|
-
const totalOccurrences = rows.reduce((s, r) => s + Number(r.cnt), 0);
|
|
302
|
-
if (totalOccurrences < opts.min_occurrences) continue;
|
|
303
|
-
const projects = rows.map((r) => slugMap[r.project_id] ?? String(r.project_id)).filter(Boolean);
|
|
304
|
-
const firstSeen = Math.min(...rows.map((r) => r.first_seen));
|
|
305
|
-
const lastSeen = Math.max(...rows.map((r) => r.last_seen));
|
|
306
|
-
tunnels.push({
|
|
307
|
-
concept: term,
|
|
308
|
-
projects,
|
|
309
|
-
occurrences: totalOccurrences,
|
|
310
|
-
first_seen: firstSeen,
|
|
311
|
-
last_seen: lastSeen
|
|
312
|
-
});
|
|
313
|
-
} catch {
|
|
314
|
-
continue;
|
|
315
|
-
}
|
|
316
|
-
tunnels.sort((a, b) => {
|
|
317
|
-
const byProjects = b.projects.length - a.projects.length;
|
|
318
|
-
if (byProjects !== 0) return byProjects;
|
|
319
|
-
return b.occurrences - a.occurrences;
|
|
320
|
-
});
|
|
321
|
-
return {
|
|
322
|
-
tunnels: tunnels.slice(0, opts.limit),
|
|
323
|
-
projects_analyzed: projectIds.length,
|
|
324
|
-
total_concepts_evaluated: candidateTerms.length
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
/** Find cross-project concept tunnels against a SQLite federation database. */
|
|
328
|
-
async function findTunnelsSqlite(db, registry, options) {
|
|
329
|
-
const opts = {
|
|
330
|
-
min_projects: options?.min_projects ?? 2,
|
|
331
|
-
min_occurrences: options?.min_occurrences ?? 3,
|
|
332
|
-
limit: options?.limit ?? 20
|
|
333
|
-
};
|
|
334
|
-
const projectRows = await registry.listProjects({ excludeArchived: true });
|
|
335
|
-
const slugMap = {};
|
|
336
|
-
for (const { id, slug } of projectRows) slugMap[id] = slug;
|
|
337
|
-
return findTunnelsForSlugMap(db, slugMap, opts);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
//#endregion
|
|
341
|
-
//#region src/storage/sqlite/sources.ts
|
|
342
|
-
function getMemorySourcesReportSqlite(db) {
|
|
343
|
-
return {
|
|
344
|
-
composition: db.prepare(`SELECT source, tier, COUNT(*) AS chunks,
|
|
345
|
-
SUM(CASE WHEN embedding IS NOT NULL THEN 1 ELSE 0 END) AS embedded
|
|
346
|
-
FROM memory_chunks GROUP BY source, tier ORDER BY COUNT(*) DESC`).all(),
|
|
347
|
-
paths: db.prepare(`SELECT path, COUNT(*) AS chunks FROM memory_chunks GROUP BY path`).all(),
|
|
348
|
-
churn: db.prepare(`SELECT date(updated_at/1000,'unixepoch') AS day, COUNT(*) AS chunks,
|
|
349
|
-
SUM(CASE WHEN embedding IS NOT NULL THEN 1 ELSE 0 END) AS embedded
|
|
350
|
-
FROM memory_chunks GROUP BY day ORDER BY day DESC LIMIT 10`).all()
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
//#endregion
|
|
355
|
-
//#region src/storage/sqlite/search.ts
|
|
356
|
-
/**
|
|
357
|
-
* Search across all indexed memory using FTS5 BM25 ranking.
|
|
358
|
-
*
|
|
359
|
-
* Results are ordered by BM25 score (most relevant first).
|
|
360
|
-
* FTS5 bm25() returns negative values; closer to 0 = more relevant.
|
|
361
|
-
* We negate the score so callers get positive values where higher = better.
|
|
362
|
-
*/
|
|
363
|
-
function searchMemory(db, query, opts) {
|
|
364
|
-
const maxResults = opts?.maxResults ?? 10;
|
|
365
|
-
const ftsQuery = buildFtsQuery(query);
|
|
366
|
-
const conditions = [];
|
|
367
|
-
const params = [ftsQuery];
|
|
368
|
-
if (opts?.projectIds && opts.projectIds.length > 0) {
|
|
369
|
-
const placeholders = opts.projectIds.map(() => "?").join(", ");
|
|
370
|
-
conditions.push(`c.project_id IN (${placeholders})`);
|
|
371
|
-
params.push(...opts.projectIds);
|
|
372
|
-
}
|
|
373
|
-
if (opts?.sources && opts.sources.length > 0) {
|
|
374
|
-
const placeholders = opts.sources.map(() => "?").join(", ");
|
|
375
|
-
conditions.push(`c.source IN (${placeholders})`);
|
|
376
|
-
params.push(...opts.sources);
|
|
377
|
-
}
|
|
378
|
-
if (opts?.tiers && opts.tiers.length > 0) {
|
|
379
|
-
const placeholders = opts.tiers.map(() => "?").join(", ");
|
|
380
|
-
conditions.push(`c.tier IN (${placeholders})`);
|
|
381
|
-
params.push(...opts.tiers);
|
|
382
|
-
}
|
|
383
|
-
const whereClause = conditions.length > 0 ? "AND " + conditions.join(" AND ") : "";
|
|
384
|
-
params.push(maxResults);
|
|
385
|
-
const sql = `
|
|
386
|
-
SELECT
|
|
387
|
-
c.id,
|
|
388
|
-
c.project_id,
|
|
389
|
-
c.path,
|
|
390
|
-
c.start_line,
|
|
391
|
-
c.end_line,
|
|
392
|
-
c.text AS snippet,
|
|
393
|
-
c.tier,
|
|
394
|
-
c.source,
|
|
395
|
-
c.updated_at,
|
|
396
|
-
c.last_accessed_at,
|
|
397
|
-
c.relevance_score,
|
|
398
|
-
bm25(memory_fts) AS bm25_score
|
|
399
|
-
FROM memory_fts
|
|
400
|
-
JOIN memory_chunks c ON memory_fts.id = c.id
|
|
401
|
-
WHERE memory_fts MATCH ?
|
|
402
|
-
${whereClause}
|
|
403
|
-
ORDER BY bm25_score
|
|
404
|
-
LIMIT ?
|
|
405
|
-
`;
|
|
406
|
-
let rows;
|
|
407
|
-
try {
|
|
408
|
-
rows = db.prepare(sql).all(...params);
|
|
409
|
-
} catch (e) {
|
|
410
|
-
if (!isQuerySyntaxError(e)) throw new Error(`Memory keyword search failed — the index is unusable, so this is NOT an empty result set. Cause: ${e instanceof Error ? e.message : String(e)}`);
|
|
411
|
-
return [];
|
|
412
|
-
}
|
|
413
|
-
const minScore = opts?.minScore ?? 0;
|
|
414
|
-
return rows.map((row) => {
|
|
415
|
-
const score = -row.bm25_score * (.5 + (row.relevance_score ?? .5));
|
|
416
|
-
return {
|
|
417
|
-
chunkId: row.id,
|
|
418
|
-
projectId: row.project_id,
|
|
419
|
-
path: row.path,
|
|
420
|
-
startLine: row.start_line,
|
|
421
|
-
endLine: row.end_line,
|
|
422
|
-
snippet: row.snippet,
|
|
423
|
-
score,
|
|
424
|
-
tier: row.tier,
|
|
425
|
-
source: row.source,
|
|
426
|
-
updatedAt: row.updated_at,
|
|
427
|
-
lastAccessedAt: row.last_accessed_at ?? void 0
|
|
428
|
-
};
|
|
429
|
-
}).filter((r) => r.score >= minScore);
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Search chunks using brute-force cosine similarity over stored embeddings.
|
|
433
|
-
*
|
|
434
|
-
* Only chunks that have a non-null embedding BLOB are considered.
|
|
435
|
-
*
|
|
436
|
-
* @param queryEmbedding Pre-computed Float32Array for the search query.
|
|
437
|
-
*/
|
|
438
|
-
function searchMemorySemantic(db, queryEmbedding, opts) {
|
|
439
|
-
const maxResults = opts?.maxResults ?? 10;
|
|
440
|
-
const conditions = ["embedding IS NOT NULL"];
|
|
441
|
-
const params = [];
|
|
442
|
-
if (opts?.projectIds && opts.projectIds.length > 0) {
|
|
443
|
-
const placeholders = opts.projectIds.map(() => "?").join(", ");
|
|
444
|
-
conditions.push(`project_id IN (${placeholders})`);
|
|
445
|
-
params.push(...opts.projectIds);
|
|
446
|
-
}
|
|
447
|
-
if (opts?.sources && opts.sources.length > 0) {
|
|
448
|
-
const placeholders = opts.sources.map(() => "?").join(", ");
|
|
449
|
-
conditions.push(`source IN (${placeholders})`);
|
|
450
|
-
params.push(...opts.sources);
|
|
451
|
-
}
|
|
452
|
-
if (opts?.tiers && opts.tiers.length > 0) {
|
|
453
|
-
const placeholders = opts.tiers.map(() => "?").join(", ");
|
|
454
|
-
conditions.push(`tier IN (${placeholders})`);
|
|
455
|
-
params.push(...opts.tiers);
|
|
456
|
-
}
|
|
457
|
-
const sql = `
|
|
458
|
-
SELECT id, project_id, path, start_line, end_line, text, tier, source, embedding, updated_at, last_accessed_at, relevance_score
|
|
459
|
-
FROM memory_chunks
|
|
460
|
-
${"WHERE " + conditions.join(" AND ")}
|
|
461
|
-
LIMIT 5000
|
|
462
|
-
`;
|
|
463
|
-
const rows = db.prepare(sql).all(...params);
|
|
464
|
-
if (rows.length === 0) return [];
|
|
465
|
-
const scored = rows.map((row) => {
|
|
466
|
-
const score = cosineSimilarity(queryEmbedding, deserializeEmbedding(row.embedding)) * (.5 + (row.relevance_score ?? .5));
|
|
467
|
-
return {
|
|
468
|
-
chunkId: row.id,
|
|
469
|
-
projectId: row.project_id,
|
|
470
|
-
path: row.path,
|
|
471
|
-
startLine: row.start_line,
|
|
472
|
-
endLine: row.end_line,
|
|
473
|
-
snippet: row.text,
|
|
474
|
-
score,
|
|
475
|
-
tier: row.tier,
|
|
476
|
-
source: row.source,
|
|
477
|
-
updatedAt: row.updated_at,
|
|
478
|
-
lastAccessedAt: row.last_accessed_at ?? void 0
|
|
479
|
-
};
|
|
480
|
-
});
|
|
481
|
-
const minScore = opts?.minScore ?? -Infinity;
|
|
482
|
-
return scored.filter((r) => r.score >= minScore).sort((a, b) => b.score - a.score).slice(0, maxResults);
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Update last_accessed_at for a set of chunk IDs to the current timestamp.
|
|
486
|
-
*
|
|
487
|
-
* Best-effort: errors are silently ignored so search is never blocked.
|
|
488
|
-
*/
|
|
489
|
-
function touchChunksLastAccessed(db, chunkIds) {
|
|
490
|
-
if (chunkIds.length === 0) return;
|
|
491
|
-
try {
|
|
492
|
-
const now = Date.now();
|
|
493
|
-
const placeholders = chunkIds.map(() => "?").join(", ");
|
|
494
|
-
db.prepare(`UPDATE memory_chunks SET last_accessed_at = ? WHERE id IN (${placeholders})`).run(now, ...chunkIds);
|
|
495
|
-
} catch {}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
//#endregion
|
|
499
|
-
//#region src/storage/sqlite/kg-entity.ts
|
|
500
|
-
/**
|
|
501
|
-
* Upsert a KG entity in the federation SQLite database.
|
|
502
|
-
*
|
|
503
|
-
* If the entity already exists for this tenant:
|
|
504
|
-
* - Updates last_seen to now
|
|
505
|
-
* - Increments mention_count
|
|
506
|
-
* - Updates description if provided (overwrites older description)
|
|
507
|
-
*
|
|
508
|
-
* Returns the entity_id for use as a foreign key in kg_triples.
|
|
509
|
-
*/
|
|
510
|
-
function upsertKgEntitySqlite(db, params) {
|
|
511
|
-
const tenantId = params.tenantId ?? "default";
|
|
512
|
-
const entityId = entityContentId(params.name, tenantId);
|
|
513
|
-
const now = Date.now();
|
|
514
|
-
db.prepare(`
|
|
515
|
-
INSERT INTO kg_entities
|
|
516
|
-
(entity_id, tenant_id, name, type, description, first_seen, last_seen, mention_count, feedback_weight)
|
|
517
|
-
VALUES
|
|
518
|
-
(?, ?, ?, ?, ?, ?, ?, 1, 0.5)
|
|
519
|
-
ON CONFLICT(entity_id) DO UPDATE SET
|
|
520
|
-
last_seen = excluded.last_seen,
|
|
521
|
-
mention_count = mention_count + 1,
|
|
522
|
-
description = COALESCE(excluded.description, description),
|
|
523
|
-
type = CASE WHEN excluded.type != 'unknown' THEN excluded.type ELSE type END
|
|
524
|
-
`).run(entityId, tenantId, params.name, params.type ?? "unknown", params.description ?? null, now, now);
|
|
525
|
-
return entityId;
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* Look up a KG entity by name within a tenant.
|
|
529
|
-
* Returns null if the entity does not exist.
|
|
530
|
-
*/
|
|
531
|
-
function findKgEntitySqlite(db, name, tenantId = "default") {
|
|
532
|
-
const entityId = entityContentId(name, tenantId);
|
|
533
|
-
return db.prepare("SELECT * FROM kg_entities WHERE entity_id = ? AND tenant_id = ?").get(entityId, tenantId) ?? null;
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* List KG entities for a tenant, optionally filtered by type.
|
|
537
|
-
*/
|
|
538
|
-
function listKgEntitiesSqlite(db, tenantId = "default", type, limit = 100) {
|
|
539
|
-
if (type) return db.prepare("SELECT * FROM kg_entities WHERE tenant_id = ? AND type = ? ORDER BY mention_count DESC LIMIT ?").all(tenantId, type, limit);
|
|
540
|
-
return db.prepare("SELECT * FROM kg_entities WHERE tenant_id = ? ORDER BY mention_count DESC LIMIT ?").all(tenantId, limit);
|
|
541
|
-
}
|
|
542
|
-
/**
|
|
543
|
-
* Apply an EMA (Exponential Moving Average) feedback update to an entity's weight.
|
|
544
|
-
*
|
|
545
|
-
* EMA formula: new_weight = old_weight + alpha * (target - old_weight)
|
|
546
|
-
*/
|
|
547
|
-
function updateEntityFeedbackWeightSqlite(db, entityId, normalizedRating, alpha = .1) {
|
|
548
|
-
const row = db.prepare("SELECT feedback_weight FROM kg_entities WHERE entity_id = ?").get(entityId);
|
|
549
|
-
if (!row) return;
|
|
550
|
-
const newWeight = row.feedback_weight + alpha * (normalizedRating - row.feedback_weight);
|
|
551
|
-
db.prepare("UPDATE kg_entities SET feedback_weight = ? WHERE entity_id = ?").run(newWeight, entityId);
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
//#endregion
|
|
555
|
-
//#region src/storage/sqlite.ts
|
|
556
|
-
var SQLiteBackend = class {
|
|
557
|
-
backendType = "sqlite";
|
|
558
|
-
supportsPostgresFeatures = false;
|
|
559
|
-
db;
|
|
560
|
-
constructor(db) {
|
|
561
|
-
this.db = db;
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Expose the raw better-sqlite3 Database handle. Storage-internal only —
|
|
565
|
-
* used by db-admin.ts for `pai db query|tables|schema` (the one place
|
|
566
|
-
* outside this class allowed to reach for it). Every other operation must
|
|
567
|
-
* be a StorageBackend method; nothing outside src/storage/ may call this.
|
|
568
|
-
*/
|
|
569
|
-
getRawDb() {
|
|
570
|
-
return this.db;
|
|
571
|
-
}
|
|
572
|
-
async close() {
|
|
573
|
-
try {
|
|
574
|
-
this.db.close();
|
|
575
|
-
} catch {}
|
|
576
|
-
}
|
|
577
|
-
async getStats() {
|
|
578
|
-
return {
|
|
579
|
-
files: this.db.prepare("SELECT COUNT(*) AS n FROM memory_files").get().n,
|
|
580
|
-
chunks: this.db.prepare("SELECT COUNT(*) AS n FROM memory_chunks").get().n
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
async getProjectStats(projectId) {
|
|
584
|
-
return {
|
|
585
|
-
files: this.db.prepare("SELECT COUNT(*) AS n FROM memory_files WHERE project_id = ?").get(projectId).n,
|
|
586
|
-
chunks: this.db.prepare("SELECT COUNT(*) AS n FROM memory_chunks WHERE project_id = ?").get(projectId).n
|
|
587
|
-
};
|
|
588
|
-
}
|
|
589
|
-
async getFileHash(projectId, path) {
|
|
590
|
-
return this.db.prepare("SELECT hash FROM memory_files WHERE project_id = ? AND path = ?").get(projectId, path)?.hash;
|
|
591
|
-
}
|
|
592
|
-
async upsertFile(file) {
|
|
593
|
-
this.db.prepare(`INSERT INTO memory_files (project_id, path, source, tier, hash, mtime, size)
|
|
594
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
595
|
-
ON CONFLICT(project_id, path) DO UPDATE SET
|
|
596
|
-
source = excluded.source,
|
|
597
|
-
tier = excluded.tier,
|
|
598
|
-
hash = excluded.hash,
|
|
599
|
-
mtime = excluded.mtime,
|
|
600
|
-
size = excluded.size`).run(file.projectId, file.path, file.source, file.tier, file.hash, file.mtime, file.size);
|
|
601
|
-
}
|
|
602
|
-
async getChunkIds(projectId, path) {
|
|
603
|
-
return this.db.prepare("SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?").all(projectId, path).map((r) => r.id);
|
|
604
|
-
}
|
|
605
|
-
async deleteChunksForFile(projectId, path) {
|
|
606
|
-
const ids = await this.getChunkIds(projectId, path);
|
|
607
|
-
const deleteFts = this.db.prepare("DELETE FROM memory_fts WHERE id = ?");
|
|
608
|
-
const deleteChunks = this.db.prepare("DELETE FROM memory_chunks WHERE project_id = ? AND path = ?");
|
|
609
|
-
this.db.transaction(() => {
|
|
610
|
-
for (const id of ids) deleteFts.run(id);
|
|
611
|
-
deleteChunks.run(projectId, path);
|
|
612
|
-
})();
|
|
613
|
-
}
|
|
614
|
-
async insertChunks(chunks) {
|
|
615
|
-
if (chunks.length === 0) return;
|
|
616
|
-
const insertChunk = this.db.prepare(`INSERT INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)
|
|
617
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
618
|
-
const insertFts = this.db.prepare(`INSERT INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)
|
|
619
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
620
|
-
this.db.transaction(() => {
|
|
621
|
-
for (const c of chunks) {
|
|
622
|
-
insertChunk.run(c.id, c.projectId, c.source, c.tier, c.path, c.startLine, c.endLine, c.hash, c.text, c.updatedAt);
|
|
623
|
-
insertFts.run(c.text, c.id, c.projectId, c.path, c.source, c.tier, c.startLine, c.endLine);
|
|
624
|
-
}
|
|
625
|
-
})();
|
|
626
|
-
}
|
|
627
|
-
async getDistinctChunkPaths(projectId) {
|
|
628
|
-
return this.db.prepare("SELECT DISTINCT path FROM memory_chunks WHERE project_id = ?").all(projectId).map((r) => r.path);
|
|
629
|
-
}
|
|
630
|
-
async deletePaths(projectId, paths) {
|
|
631
|
-
if (paths.length === 0) return;
|
|
632
|
-
const deleteFts = this.db.prepare("DELETE FROM memory_fts WHERE id = ?");
|
|
633
|
-
const deleteChunks = this.db.prepare("DELETE FROM memory_chunks WHERE project_id = ? AND path = ?");
|
|
634
|
-
const deleteFile = this.db.prepare("DELETE FROM memory_files WHERE project_id = ? AND path = ?");
|
|
635
|
-
this.db.transaction(() => {
|
|
636
|
-
for (const path of paths) {
|
|
637
|
-
const ids = this.db.prepare("SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?").all(projectId, path);
|
|
638
|
-
for (const { id } of ids) deleteFts.run(id);
|
|
639
|
-
deleteChunks.run(projectId, path);
|
|
640
|
-
deleteFile.run(projectId, path);
|
|
641
|
-
}
|
|
642
|
-
})();
|
|
643
|
-
}
|
|
644
|
-
async getUnembeddedChunkIds(projectId, limit, after) {
|
|
645
|
-
const conditions = ["embedding IS NULL"];
|
|
646
|
-
const params = [];
|
|
647
|
-
if (projectId !== void 0) {
|
|
648
|
-
conditions.push("project_id = ?");
|
|
649
|
-
params.push(projectId);
|
|
650
|
-
}
|
|
651
|
-
const paginating = after !== void 0;
|
|
652
|
-
if (after) {
|
|
653
|
-
conditions.push("(project_id > ? OR (project_id = ? AND id > ?))");
|
|
654
|
-
params.push(after.projectId, after.projectId, after.id);
|
|
655
|
-
}
|
|
656
|
-
const where = "WHERE " + conditions.join(" AND ");
|
|
657
|
-
const orderBy = paginating ? "project_id, id" : `CASE
|
|
658
|
-
WHEN path LIKE '🧠 Ideaverse/%' THEN 0
|
|
659
|
-
WHEN path LIKE 'Z - Zettelkasten/%' THEN 0
|
|
660
|
-
WHEN path LIKE '💼 Business/%' THEN 0
|
|
661
|
-
WHEN path LIKE '📆 Meetings/%' THEN 1
|
|
662
|
-
WHEN path LIKE '💡 Insights/%' THEN 1
|
|
663
|
-
WHEN path LIKE '👨💻 People/%' THEN 1
|
|
664
|
-
WHEN path LIKE 'University/%' THEN 1
|
|
665
|
-
WHEN path LIKE 'Copilot/%' THEN 1
|
|
666
|
-
WHEN path LIKE '🗓️ Daily Notes/%' THEN 2
|
|
667
|
-
WHEN path LIKE 'PAI/%' THEN 3
|
|
668
|
-
WHEN path LIKE '09-job-search/%' THEN 4
|
|
669
|
-
WHEN path LIKE 'seriousletter/%' THEN 4
|
|
670
|
-
WHEN path LIKE 'Attachments/%' THEN 5
|
|
671
|
-
ELSE 2
|
|
672
|
-
END, id`;
|
|
673
|
-
return this.db.prepare(`SELECT id, text, project_id, path FROM memory_chunks ${where}
|
|
674
|
-
ORDER BY ${orderBy}${limit !== void 0 ? " LIMIT ?" : ""}`).all(...params, ...limit !== void 0 ? [limit] : []);
|
|
675
|
-
}
|
|
676
|
-
async updateEmbedding(chunkId, embedding) {
|
|
677
|
-
this.db.prepare("UPDATE memory_chunks SET embedding = ? WHERE id = ?").run(embedding, chunkId);
|
|
678
|
-
}
|
|
679
|
-
async searchKeyword(query, opts) {
|
|
680
|
-
return searchMemory(this.db, query, opts);
|
|
681
|
-
}
|
|
682
|
-
async searchSemantic(queryEmbedding, opts) {
|
|
683
|
-
return searchMemorySemantic(this.db, queryEmbedding, opts);
|
|
684
|
-
}
|
|
685
|
-
vaultNotSupported() {
|
|
686
|
-
throw new Error("Vault operations require the Postgres backend");
|
|
687
|
-
}
|
|
688
|
-
async upsertVaultFile() {
|
|
689
|
-
this.vaultNotSupported();
|
|
690
|
-
}
|
|
691
|
-
async deleteVaultFile() {
|
|
692
|
-
this.vaultNotSupported();
|
|
693
|
-
}
|
|
694
|
-
async getVaultFile() {
|
|
695
|
-
this.vaultNotSupported();
|
|
696
|
-
}
|
|
697
|
-
async getVaultFileByInode() {
|
|
698
|
-
this.vaultNotSupported();
|
|
699
|
-
}
|
|
700
|
-
async getAllVaultFiles() {
|
|
701
|
-
this.vaultNotSupported();
|
|
702
|
-
}
|
|
703
|
-
async getRecentVaultFiles() {
|
|
704
|
-
this.vaultNotSupported();
|
|
705
|
-
}
|
|
706
|
-
async countVaultFiles() {
|
|
707
|
-
this.vaultNotSupported();
|
|
708
|
-
}
|
|
709
|
-
async upsertVaultAliases() {
|
|
710
|
-
this.vaultNotSupported();
|
|
711
|
-
}
|
|
712
|
-
async deleteVaultAliases() {
|
|
713
|
-
this.vaultNotSupported();
|
|
714
|
-
}
|
|
715
|
-
async replaceLinksForSources() {
|
|
716
|
-
this.vaultNotSupported();
|
|
717
|
-
}
|
|
718
|
-
async getLinksFromSource() {
|
|
719
|
-
this.vaultNotSupported();
|
|
720
|
-
}
|
|
721
|
-
async getLinksToTarget() {
|
|
722
|
-
this.vaultNotSupported();
|
|
723
|
-
}
|
|
724
|
-
async getVaultLinkGraph() {
|
|
725
|
-
this.vaultNotSupported();
|
|
726
|
-
}
|
|
727
|
-
async upsertVaultHealth() {
|
|
728
|
-
this.vaultNotSupported();
|
|
729
|
-
}
|
|
730
|
-
async getVaultHealth() {
|
|
731
|
-
this.vaultNotSupported();
|
|
732
|
-
}
|
|
733
|
-
async getOrphans() {
|
|
734
|
-
this.vaultNotSupported();
|
|
735
|
-
}
|
|
736
|
-
async getDeadLinks() {
|
|
737
|
-
this.vaultNotSupported();
|
|
738
|
-
}
|
|
739
|
-
async upsertNameIndex() {
|
|
740
|
-
this.vaultNotSupported();
|
|
741
|
-
}
|
|
742
|
-
async replaceNameIndex() {
|
|
743
|
-
this.vaultNotSupported();
|
|
744
|
-
}
|
|
745
|
-
async resolveVaultName() {
|
|
746
|
-
this.vaultNotSupported();
|
|
747
|
-
}
|
|
748
|
-
async searchVaultNameIndex() {
|
|
749
|
-
this.vaultNotSupported();
|
|
750
|
-
}
|
|
751
|
-
async getVaultFilesByPaths() {
|
|
752
|
-
this.vaultNotSupported();
|
|
753
|
-
}
|
|
754
|
-
async getVaultFilesByPathsAfter() {
|
|
755
|
-
this.vaultNotSupported();
|
|
756
|
-
}
|
|
757
|
-
async getVaultLinksFromPaths() {
|
|
758
|
-
this.vaultNotSupported();
|
|
759
|
-
}
|
|
760
|
-
async getChunksWithEmbeddings() {
|
|
761
|
-
this.vaultNotSupported();
|
|
762
|
-
}
|
|
763
|
-
async getChunksForPath() {
|
|
764
|
-
this.vaultNotSupported();
|
|
765
|
-
}
|
|
766
|
-
async searchChunksByText() {
|
|
767
|
-
this.vaultNotSupported();
|
|
768
|
-
}
|
|
769
|
-
async countVaultFilesWithPrefix() {
|
|
770
|
-
this.vaultNotSupported();
|
|
771
|
-
}
|
|
772
|
-
async countVaultFilesAfter() {
|
|
773
|
-
this.vaultNotSupported();
|
|
774
|
-
}
|
|
775
|
-
async countVaultLinksWithPrefix() {
|
|
776
|
-
this.vaultNotSupported();
|
|
777
|
-
}
|
|
778
|
-
async countVaultLinksAfter() {
|
|
779
|
-
this.vaultNotSupported();
|
|
780
|
-
}
|
|
781
|
-
async getDeadLinksWithLineNumbers() {
|
|
782
|
-
this.vaultNotSupported();
|
|
783
|
-
}
|
|
784
|
-
async getDeadLinksWithPrefix() {
|
|
785
|
-
this.vaultNotSupported();
|
|
786
|
-
}
|
|
787
|
-
async getDeadLinksAfter() {
|
|
788
|
-
this.vaultNotSupported();
|
|
789
|
-
}
|
|
790
|
-
async getOrphansWithPrefix() {
|
|
791
|
-
this.vaultNotSupported();
|
|
792
|
-
}
|
|
793
|
-
async getOrphansAfter() {
|
|
794
|
-
this.vaultNotSupported();
|
|
795
|
-
}
|
|
796
|
-
async getLowConnectivity() {
|
|
797
|
-
this.vaultNotSupported();
|
|
798
|
-
}
|
|
799
|
-
async getLowConnectivityWithPrefix() {
|
|
800
|
-
this.vaultNotSupported();
|
|
801
|
-
}
|
|
802
|
-
async getLowConnectivityAfter() {
|
|
803
|
-
this.vaultNotSupported();
|
|
804
|
-
}
|
|
805
|
-
async getAllVaultFilePaths() {
|
|
806
|
-
this.vaultNotSupported();
|
|
807
|
-
}
|
|
808
|
-
async getVaultFilePathsWithPrefix() {
|
|
809
|
-
this.vaultNotSupported();
|
|
810
|
-
}
|
|
811
|
-
async getVaultFilePathsAfter() {
|
|
812
|
-
this.vaultNotSupported();
|
|
813
|
-
}
|
|
814
|
-
async getVaultLinkEdges() {
|
|
815
|
-
this.vaultNotSupported();
|
|
816
|
-
}
|
|
817
|
-
async getVaultLinkEdgesWithPrefix() {
|
|
818
|
-
this.vaultNotSupported();
|
|
819
|
-
}
|
|
820
|
-
async getVaultLinkEdgesAfter() {
|
|
821
|
-
this.vaultNotSupported();
|
|
822
|
-
}
|
|
823
|
-
async getVaultAlias() {
|
|
824
|
-
this.vaultNotSupported();
|
|
825
|
-
}
|
|
826
|
-
async upsertKgEntity(params) {
|
|
827
|
-
return upsertKgEntitySqlite(this.db, params);
|
|
828
|
-
}
|
|
829
|
-
async findKgEntity(name, tenantId = "default") {
|
|
830
|
-
return findKgEntitySqlite(this.db, name, tenantId);
|
|
831
|
-
}
|
|
832
|
-
async listKgEntities(tenantId = "default", type, limit = 100) {
|
|
833
|
-
return listKgEntitiesSqlite(this.db, tenantId, type, limit);
|
|
834
|
-
}
|
|
835
|
-
async updateEntityFeedbackWeight(entityId, normalizedRating, alpha = .1) {
|
|
836
|
-
return updateEntityFeedbackWeightSqlite(this.db, entityId, normalizedRating, alpha);
|
|
837
|
-
}
|
|
838
|
-
async getChunksForFeedback(chunkIds) {
|
|
839
|
-
if (chunkIds.length === 0) return [];
|
|
840
|
-
const placeholders = chunkIds.map(() => "?").join(", ");
|
|
841
|
-
return this.db.prepare(`SELECT id, text, relevance_score FROM memory_chunks WHERE id IN (${placeholders})`).all(...chunkIds).map((r) => ({
|
|
842
|
-
id: r.id,
|
|
843
|
-
text: r.text,
|
|
844
|
-
relevanceScore: r.relevance_score
|
|
845
|
-
}));
|
|
846
|
-
}
|
|
847
|
-
async updateChunkRelevanceScore(chunkId, score) {
|
|
848
|
-
this.db.prepare("UPDATE memory_chunks SET relevance_score = ? WHERE id = ?").run(score, chunkId);
|
|
849
|
-
}
|
|
850
|
-
async touchChunksLastAccessed(chunkIds) {
|
|
851
|
-
touchChunksLastAccessed(this.db, chunkIds);
|
|
852
|
-
}
|
|
853
|
-
async indexAll(registry) {
|
|
854
|
-
return indexAllSqlite(this.db, registry);
|
|
855
|
-
}
|
|
856
|
-
async findTunnels(registry, options) {
|
|
857
|
-
return findTunnelsSqlite(this.db, registry, options);
|
|
858
|
-
}
|
|
859
|
-
async getMemorySourcesReport() {
|
|
860
|
-
return getMemorySourcesReportSqlite(this.db);
|
|
861
|
-
}
|
|
862
|
-
postgresOnly(feature) {
|
|
863
|
-
throw new Error(`${feature} is not supported on the sqlite backend — set "storageBackend": "postgres" in the daemon config`);
|
|
864
|
-
}
|
|
865
|
-
async addKgTriple() {
|
|
866
|
-
this.postgresOnly("kg_triples");
|
|
867
|
-
}
|
|
868
|
-
async queryKgTriples() {
|
|
869
|
-
this.postgresOnly("kg_triples");
|
|
870
|
-
}
|
|
871
|
-
async invalidateKgTriple() {
|
|
872
|
-
this.postgresOnly("kg_triples");
|
|
873
|
-
}
|
|
874
|
-
async getKgContradictions() {
|
|
875
|
-
this.postgresOnly("kg_triples");
|
|
876
|
-
}
|
|
877
|
-
async getKgStats() {
|
|
878
|
-
this.postgresOnly("kg_triples");
|
|
879
|
-
}
|
|
880
|
-
async storeObservation() {
|
|
881
|
-
this.postgresOnly("observations");
|
|
882
|
-
}
|
|
883
|
-
async storeObservationWithProject() {
|
|
884
|
-
this.postgresOnly("observations");
|
|
885
|
-
}
|
|
886
|
-
async queryObservations() {
|
|
887
|
-
this.postgresOnly("observations");
|
|
888
|
-
}
|
|
889
|
-
async queryRecentObservations() {
|
|
890
|
-
this.postgresOnly("observations");
|
|
891
|
-
}
|
|
892
|
-
async querySessionObservations() {
|
|
893
|
-
this.postgresOnly("observations");
|
|
894
|
-
}
|
|
895
|
-
async getObservationStats() {
|
|
896
|
-
this.postgresOnly("observations");
|
|
897
|
-
}
|
|
898
|
-
async getObservationTypesForPaths() {
|
|
899
|
-
return /* @__PURE__ */ new Map();
|
|
900
|
-
}
|
|
901
|
-
async storeSessionSummary() {
|
|
902
|
-
this.postgresOnly("session summaries");
|
|
903
|
-
}
|
|
904
|
-
async queryRecentSummaries() {
|
|
905
|
-
this.postgresOnly("session summaries");
|
|
906
|
-
}
|
|
907
|
-
async recordSkillInvocation() {
|
|
908
|
-
this.postgresOnly("skill telemetry");
|
|
909
|
-
}
|
|
910
|
-
async querySkillTelemetry() {
|
|
911
|
-
this.postgresOnly("skill telemetry");
|
|
912
|
-
}
|
|
913
|
-
async countFilesWithPaths(paths) {
|
|
914
|
-
if (paths.length === 0) return 0;
|
|
915
|
-
const placeholders = paths.map(() => "?").join(", ");
|
|
916
|
-
return this.db.prepare(`SELECT COUNT(*) AS n FROM memory_files WHERE path IN (${placeholders})`).get(...paths).n;
|
|
917
|
-
}
|
|
918
|
-
async renameFilePaths(moves) {
|
|
919
|
-
if (moves.length === 0) return 0;
|
|
920
|
-
const updateFiles = this.db.prepare("UPDATE memory_files SET path = ? WHERE path = ?");
|
|
921
|
-
const updateChunks = this.db.prepare("UPDATE memory_chunks SET path = ? WHERE path = ?");
|
|
922
|
-
let filesUpdated = 0;
|
|
923
|
-
this.db.transaction(() => {
|
|
924
|
-
for (const { oldPath, newPath } of moves) {
|
|
925
|
-
const r = updateFiles.run(newPath, oldPath);
|
|
926
|
-
filesUpdated += r.changes;
|
|
927
|
-
updateChunks.run(newPath, oldPath);
|
|
928
|
-
}
|
|
929
|
-
})();
|
|
930
|
-
return filesUpdated;
|
|
931
|
-
}
|
|
932
|
-
};
|
|
933
|
-
|
|
934
|
-
//#endregion
|
|
935
|
-
export { SQLiteBackend };
|
|
936
|
-
//# sourceMappingURL=sqlite-CqsTy6xo.mjs.map
|