recallx 1.0.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 +205 -0
- package/app/cli/bin/recallx-mcp.js +2 -0
- package/app/cli/bin/recallx.js +8 -0
- package/app/cli/src/cli.js +808 -0
- package/app/cli/src/format.js +242 -0
- package/app/cli/src/http.js +35 -0
- package/app/mcp/api-client.js +101 -0
- package/app/mcp/index.js +128 -0
- package/app/mcp/server.js +786 -0
- package/app/server/app.js +2263 -0
- package/app/server/config.js +27 -0
- package/app/server/db.js +399 -0
- package/app/server/errors.js +17 -0
- package/app/server/governance.js +466 -0
- package/app/server/index.js +26 -0
- package/app/server/inferred-relations.js +247 -0
- package/app/server/observability.js +495 -0
- package/app/server/project-graph.js +199 -0
- package/app/server/relation-scoring.js +59 -0
- package/app/server/repositories.js +2992 -0
- package/app/server/retrieval.js +486 -0
- package/app/server/semantic/chunker.js +85 -0
- package/app/server/semantic/provider.js +124 -0
- package/app/server/semantic/types.js +1 -0
- package/app/server/semantic/vector-store.js +169 -0
- package/app/server/utils.js +43 -0
- package/app/server/workspace-session.js +128 -0
- package/app/server/workspace.js +79 -0
- package/app/shared/contracts.js +268 -0
- package/app/shared/request-runtime.js +30 -0
- package/app/shared/types.js +1 -0
- package/app/shared/version.js +1 -0
- package/dist/renderer/assets/ProjectGraphCanvas-BMvz9DmE.js +312 -0
- package/dist/renderer/assets/index-C2-KXqBO.css +1 -0
- package/dist/renderer/assets/index-CrDu22h7.js +76 -0
- package/dist/renderer/index.html +13 -0
- package/package.json +49 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { getSchemaVersion } from "./db.js";
|
|
3
|
+
import { defaultWorkspaceName } from "./workspace.js";
|
|
4
|
+
export function createServerConfig(workspaceRoot) {
|
|
5
|
+
return {
|
|
6
|
+
port: Number(process.env.RECALLX_PORT ?? 8787),
|
|
7
|
+
bindAddress: process.env.RECALLX_BIND ?? "127.0.0.1",
|
|
8
|
+
apiToken: process.env.RECALLX_API_TOKEN ?? null,
|
|
9
|
+
workspaceName: process.env.RECALLX_WORKSPACE_NAME ?? defaultWorkspaceName(workspaceRoot)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function ensureApiToken(config) {
|
|
13
|
+
if (config.apiToken) {
|
|
14
|
+
return config.apiToken;
|
|
15
|
+
}
|
|
16
|
+
return randomBytes(24).toString("hex");
|
|
17
|
+
}
|
|
18
|
+
export function workspaceInfo(rootPath, config, authMode) {
|
|
19
|
+
return {
|
|
20
|
+
rootPath,
|
|
21
|
+
workspaceName: config.workspaceName,
|
|
22
|
+
schemaVersion: getSchemaVersion(),
|
|
23
|
+
bindAddress: `${config.bindAddress}:${config.port}`,
|
|
24
|
+
enabledIntegrationModes: ["read-only", "append-only"],
|
|
25
|
+
authMode
|
|
26
|
+
};
|
|
27
|
+
}
|
package/app/server/db.js
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { load as loadSqliteVec } from "sqlite-vec";
|
|
3
|
+
const schemaVersion = 7;
|
|
4
|
+
const sqliteVecStateByDb = new WeakMap();
|
|
5
|
+
function execMigration(db) {
|
|
6
|
+
db.exec(`
|
|
7
|
+
PRAGMA foreign_keys = ON;
|
|
8
|
+
|
|
9
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
10
|
+
key TEXT PRIMARY KEY,
|
|
11
|
+
value_json TEXT NOT NULL
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS nodes (
|
|
15
|
+
id TEXT PRIMARY KEY,
|
|
16
|
+
type TEXT NOT NULL,
|
|
17
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
18
|
+
canonicality TEXT NOT NULL DEFAULT 'canonical',
|
|
19
|
+
visibility TEXT NOT NULL DEFAULT 'normal',
|
|
20
|
+
title TEXT,
|
|
21
|
+
body TEXT,
|
|
22
|
+
summary TEXT,
|
|
23
|
+
created_by TEXT,
|
|
24
|
+
source_type TEXT,
|
|
25
|
+
source_label TEXT,
|
|
26
|
+
created_at TEXT NOT NULL,
|
|
27
|
+
updated_at TEXT NOT NULL,
|
|
28
|
+
tags_json TEXT,
|
|
29
|
+
metadata_json TEXT
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS node_tags (
|
|
33
|
+
node_id TEXT NOT NULL,
|
|
34
|
+
tag TEXT NOT NULL,
|
|
35
|
+
PRIMARY KEY (node_id, tag),
|
|
36
|
+
FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE TABLE IF NOT EXISTS node_index_state (
|
|
40
|
+
node_id TEXT PRIMARY KEY,
|
|
41
|
+
content_hash TEXT,
|
|
42
|
+
embedding_status TEXT NOT NULL DEFAULT 'pending',
|
|
43
|
+
embedding_provider TEXT,
|
|
44
|
+
embedding_model TEXT,
|
|
45
|
+
embedding_version TEXT,
|
|
46
|
+
stale_reason TEXT,
|
|
47
|
+
updated_at TEXT NOT NULL,
|
|
48
|
+
FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
CREATE TABLE IF NOT EXISTS node_chunks (
|
|
52
|
+
node_id TEXT NOT NULL,
|
|
53
|
+
ordinal INTEGER NOT NULL,
|
|
54
|
+
chunk_hash TEXT NOT NULL,
|
|
55
|
+
chunk_text TEXT NOT NULL,
|
|
56
|
+
token_count INTEGER,
|
|
57
|
+
start_offset INTEGER,
|
|
58
|
+
end_offset INTEGER,
|
|
59
|
+
updated_at TEXT NOT NULL,
|
|
60
|
+
PRIMARY KEY (node_id, ordinal),
|
|
61
|
+
FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
CREATE TABLE IF NOT EXISTS node_embeddings (
|
|
65
|
+
owner_type TEXT NOT NULL,
|
|
66
|
+
owner_id TEXT NOT NULL,
|
|
67
|
+
chunk_ordinal INTEGER,
|
|
68
|
+
vector_ref TEXT,
|
|
69
|
+
vector_blob BLOB,
|
|
70
|
+
embedding_provider TEXT,
|
|
71
|
+
embedding_model TEXT,
|
|
72
|
+
embedding_version TEXT,
|
|
73
|
+
content_hash TEXT,
|
|
74
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
75
|
+
created_at TEXT NOT NULL,
|
|
76
|
+
updated_at TEXT NOT NULL,
|
|
77
|
+
PRIMARY KEY (owner_type, owner_id, chunk_ordinal)
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
CREATE TABLE IF NOT EXISTS relations (
|
|
81
|
+
id TEXT PRIMARY KEY,
|
|
82
|
+
from_node_id TEXT NOT NULL,
|
|
83
|
+
to_node_id TEXT NOT NULL,
|
|
84
|
+
relation_type TEXT NOT NULL,
|
|
85
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
86
|
+
created_by TEXT,
|
|
87
|
+
source_type TEXT,
|
|
88
|
+
source_label TEXT,
|
|
89
|
+
created_at TEXT NOT NULL,
|
|
90
|
+
metadata_json TEXT,
|
|
91
|
+
FOREIGN KEY (from_node_id) REFERENCES nodes(id),
|
|
92
|
+
FOREIGN KEY (to_node_id) REFERENCES nodes(id)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
CREATE TABLE IF NOT EXISTS activities (
|
|
96
|
+
id TEXT PRIMARY KEY,
|
|
97
|
+
target_node_id TEXT NOT NULL,
|
|
98
|
+
activity_type TEXT NOT NULL,
|
|
99
|
+
body TEXT,
|
|
100
|
+
created_by TEXT,
|
|
101
|
+
source_type TEXT,
|
|
102
|
+
source_label TEXT,
|
|
103
|
+
created_at TEXT NOT NULL,
|
|
104
|
+
metadata_json TEXT,
|
|
105
|
+
FOREIGN KEY (target_node_id) REFERENCES nodes(id)
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
CREATE TABLE IF NOT EXISTS inferred_relations (
|
|
109
|
+
id TEXT PRIMARY KEY,
|
|
110
|
+
from_node_id TEXT NOT NULL,
|
|
111
|
+
to_node_id TEXT NOT NULL,
|
|
112
|
+
relation_type TEXT NOT NULL,
|
|
113
|
+
base_score REAL NOT NULL,
|
|
114
|
+
usage_score REAL NOT NULL DEFAULT 0,
|
|
115
|
+
final_score REAL NOT NULL,
|
|
116
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
117
|
+
generator TEXT NOT NULL,
|
|
118
|
+
evidence_json TEXT NOT NULL,
|
|
119
|
+
last_computed_at TEXT NOT NULL,
|
|
120
|
+
expires_at TEXT,
|
|
121
|
+
metadata_json TEXT,
|
|
122
|
+
FOREIGN KEY (from_node_id) REFERENCES nodes(id),
|
|
123
|
+
FOREIGN KEY (to_node_id) REFERENCES nodes(id)
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
CREATE TABLE IF NOT EXISTS relation_usage_events (
|
|
127
|
+
id TEXT PRIMARY KEY,
|
|
128
|
+
relation_id TEXT NOT NULL,
|
|
129
|
+
relation_source TEXT NOT NULL,
|
|
130
|
+
event_type TEXT NOT NULL,
|
|
131
|
+
session_id TEXT,
|
|
132
|
+
run_id TEXT,
|
|
133
|
+
actor_type TEXT,
|
|
134
|
+
actor_label TEXT,
|
|
135
|
+
tool_name TEXT,
|
|
136
|
+
delta REAL NOT NULL,
|
|
137
|
+
created_at TEXT NOT NULL,
|
|
138
|
+
metadata_json TEXT
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
CREATE TABLE IF NOT EXISTS relation_usage_rollups (
|
|
142
|
+
relation_id TEXT PRIMARY KEY,
|
|
143
|
+
total_delta REAL NOT NULL DEFAULT 0,
|
|
144
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
145
|
+
last_event_at TEXT NOT NULL,
|
|
146
|
+
last_event_rowid INTEGER NOT NULL DEFAULT 0,
|
|
147
|
+
updated_at TEXT NOT NULL
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
CREATE TABLE IF NOT EXISTS relation_usage_rollup_state (
|
|
151
|
+
id TEXT PRIMARY KEY,
|
|
152
|
+
last_event_rowid INTEGER NOT NULL DEFAULT 0,
|
|
153
|
+
updated_at TEXT NOT NULL
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
CREATE TABLE IF NOT EXISTS search_feedback_events (
|
|
157
|
+
id TEXT PRIMARY KEY,
|
|
158
|
+
result_type TEXT NOT NULL,
|
|
159
|
+
result_id TEXT NOT NULL,
|
|
160
|
+
verdict TEXT NOT NULL,
|
|
161
|
+
query TEXT,
|
|
162
|
+
session_id TEXT,
|
|
163
|
+
run_id TEXT,
|
|
164
|
+
actor_type TEXT,
|
|
165
|
+
actor_label TEXT,
|
|
166
|
+
tool_name TEXT,
|
|
167
|
+
confidence REAL NOT NULL,
|
|
168
|
+
delta REAL NOT NULL,
|
|
169
|
+
created_at TEXT NOT NULL,
|
|
170
|
+
metadata_json TEXT
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
CREATE TABLE IF NOT EXISTS search_feedback_rollups (
|
|
174
|
+
result_type TEXT NOT NULL,
|
|
175
|
+
result_id TEXT NOT NULL,
|
|
176
|
+
total_delta REAL NOT NULL DEFAULT 0,
|
|
177
|
+
event_count INTEGER NOT NULL DEFAULT 0,
|
|
178
|
+
useful_count INTEGER NOT NULL DEFAULT 0,
|
|
179
|
+
not_useful_count INTEGER NOT NULL DEFAULT 0,
|
|
180
|
+
uncertain_count INTEGER NOT NULL DEFAULT 0,
|
|
181
|
+
last_event_at TEXT NOT NULL,
|
|
182
|
+
updated_at TEXT NOT NULL,
|
|
183
|
+
PRIMARY KEY (result_type, result_id)
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
CREATE TABLE IF NOT EXISTS governance_events (
|
|
187
|
+
id TEXT PRIMARY KEY,
|
|
188
|
+
entity_type TEXT NOT NULL,
|
|
189
|
+
entity_id TEXT NOT NULL,
|
|
190
|
+
event_type TEXT NOT NULL,
|
|
191
|
+
previous_state TEXT,
|
|
192
|
+
next_state TEXT NOT NULL,
|
|
193
|
+
confidence REAL NOT NULL,
|
|
194
|
+
reason TEXT NOT NULL,
|
|
195
|
+
created_at TEXT NOT NULL,
|
|
196
|
+
metadata_json TEXT
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
CREATE TABLE IF NOT EXISTS governance_state (
|
|
200
|
+
entity_type TEXT NOT NULL,
|
|
201
|
+
entity_id TEXT NOT NULL,
|
|
202
|
+
state TEXT NOT NULL,
|
|
203
|
+
confidence REAL NOT NULL,
|
|
204
|
+
reasons_json TEXT NOT NULL,
|
|
205
|
+
last_evaluated_at TEXT NOT NULL,
|
|
206
|
+
last_transition_at TEXT NOT NULL,
|
|
207
|
+
metadata_json TEXT NOT NULL,
|
|
208
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
CREATE TABLE IF NOT EXISTS artifacts (
|
|
212
|
+
id TEXT PRIMARY KEY,
|
|
213
|
+
node_id TEXT NOT NULL,
|
|
214
|
+
path TEXT NOT NULL,
|
|
215
|
+
mime_type TEXT,
|
|
216
|
+
size_bytes INTEGER,
|
|
217
|
+
checksum TEXT,
|
|
218
|
+
created_by TEXT,
|
|
219
|
+
source_label TEXT,
|
|
220
|
+
created_at TEXT NOT NULL,
|
|
221
|
+
metadata_json TEXT,
|
|
222
|
+
FOREIGN KEY (node_id) REFERENCES nodes(id)
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
CREATE TABLE IF NOT EXISTS provenance_events (
|
|
226
|
+
id TEXT PRIMARY KEY,
|
|
227
|
+
entity_type TEXT NOT NULL,
|
|
228
|
+
entity_id TEXT NOT NULL,
|
|
229
|
+
operation_type TEXT NOT NULL,
|
|
230
|
+
actor_type TEXT NOT NULL,
|
|
231
|
+
actor_label TEXT,
|
|
232
|
+
tool_name TEXT,
|
|
233
|
+
tool_version TEXT,
|
|
234
|
+
timestamp TEXT NOT NULL,
|
|
235
|
+
input_ref TEXT,
|
|
236
|
+
metadata_json TEXT
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
CREATE TABLE IF NOT EXISTS integrations (
|
|
240
|
+
id TEXT PRIMARY KEY,
|
|
241
|
+
name TEXT NOT NULL,
|
|
242
|
+
kind TEXT NOT NULL,
|
|
243
|
+
status TEXT NOT NULL,
|
|
244
|
+
capabilities_json TEXT,
|
|
245
|
+
config_json TEXT,
|
|
246
|
+
created_at TEXT NOT NULL,
|
|
247
|
+
updated_at TEXT NOT NULL
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
|
|
251
|
+
id UNINDEXED,
|
|
252
|
+
title,
|
|
253
|
+
body,
|
|
254
|
+
summary,
|
|
255
|
+
content=''
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS activities_fts USING fts5(
|
|
259
|
+
id UNINDEXED,
|
|
260
|
+
body,
|
|
261
|
+
content=''
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
CREATE TRIGGER IF NOT EXISTS nodes_ai AFTER INSERT ON nodes BEGIN
|
|
265
|
+
INSERT INTO nodes_fts(rowid, id, title, body, summary)
|
|
266
|
+
VALUES (new.rowid, new.id, coalesce(new.title, ''), coalesce(new.body, ''), coalesce(new.summary, ''));
|
|
267
|
+
END;
|
|
268
|
+
|
|
269
|
+
CREATE TRIGGER IF NOT EXISTS nodes_ad AFTER DELETE ON nodes BEGIN
|
|
270
|
+
INSERT INTO nodes_fts(nodes_fts, rowid, id, title, body, summary)
|
|
271
|
+
VALUES ('delete', old.rowid, old.id, old.title, old.body, old.summary);
|
|
272
|
+
END;
|
|
273
|
+
|
|
274
|
+
CREATE TRIGGER IF NOT EXISTS nodes_au AFTER UPDATE ON nodes BEGIN
|
|
275
|
+
INSERT INTO nodes_fts(nodes_fts, rowid, id, title, body, summary)
|
|
276
|
+
VALUES ('delete', old.rowid, old.id, old.title, old.body, old.summary);
|
|
277
|
+
INSERT INTO nodes_fts(rowid, id, title, body, summary)
|
|
278
|
+
VALUES (new.rowid, new.id, coalesce(new.title, ''), coalesce(new.body, ''), coalesce(new.summary, ''));
|
|
279
|
+
END;
|
|
280
|
+
|
|
281
|
+
CREATE TRIGGER IF NOT EXISTS activities_ai AFTER INSERT ON activities BEGIN
|
|
282
|
+
INSERT INTO activities_fts(rowid, id, body)
|
|
283
|
+
VALUES (new.rowid, new.id, coalesce(new.body, ''));
|
|
284
|
+
END;
|
|
285
|
+
|
|
286
|
+
CREATE TRIGGER IF NOT EXISTS activities_ad AFTER DELETE ON activities BEGIN
|
|
287
|
+
INSERT INTO activities_fts(activities_fts, rowid, id, body)
|
|
288
|
+
VALUES ('delete', old.rowid, old.id, old.body);
|
|
289
|
+
END;
|
|
290
|
+
|
|
291
|
+
CREATE TRIGGER IF NOT EXISTS activities_au AFTER UPDATE ON activities BEGIN
|
|
292
|
+
INSERT INTO activities_fts(activities_fts, rowid, id, body)
|
|
293
|
+
VALUES ('delete', old.rowid, old.id, old.body);
|
|
294
|
+
INSERT INTO activities_fts(rowid, id, body)
|
|
295
|
+
VALUES (new.rowid, new.id, coalesce(new.body, ''));
|
|
296
|
+
END;
|
|
297
|
+
|
|
298
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type);
|
|
299
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_status ON nodes(status);
|
|
300
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_updated_at ON nodes(updated_at);
|
|
301
|
+
CREATE INDEX IF NOT EXISTS idx_node_tags_tag_node
|
|
302
|
+
ON node_tags(tag, node_id);
|
|
303
|
+
CREATE INDEX IF NOT EXISTS idx_node_index_state_status
|
|
304
|
+
ON node_index_state(embedding_status, updated_at DESC);
|
|
305
|
+
CREATE INDEX IF NOT EXISTS idx_node_chunks_hash
|
|
306
|
+
ON node_chunks(chunk_hash);
|
|
307
|
+
CREATE INDEX IF NOT EXISTS idx_node_embeddings_status
|
|
308
|
+
ON node_embeddings(status, updated_at DESC);
|
|
309
|
+
CREATE INDEX IF NOT EXISTS idx_node_embeddings_owner
|
|
310
|
+
ON node_embeddings(owner_type, owner_id);
|
|
311
|
+
CREATE INDEX IF NOT EXISTS idx_relations_from ON relations(from_node_id);
|
|
312
|
+
CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_node_id);
|
|
313
|
+
CREATE INDEX IF NOT EXISTS idx_relations_from_status_created_at
|
|
314
|
+
ON relations(from_node_id, status, created_at DESC);
|
|
315
|
+
CREATE INDEX IF NOT EXISTS idx_relations_to_status_created_at
|
|
316
|
+
ON relations(to_node_id, status, created_at DESC);
|
|
317
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_inferred_relations_identity
|
|
318
|
+
ON inferred_relations(from_node_id, to_node_id, relation_type, generator);
|
|
319
|
+
CREATE INDEX IF NOT EXISTS idx_inferred_relations_from_score
|
|
320
|
+
ON inferred_relations(from_node_id, final_score DESC);
|
|
321
|
+
CREATE INDEX IF NOT EXISTS idx_inferred_relations_to_score
|
|
322
|
+
ON inferred_relations(to_node_id, final_score DESC);
|
|
323
|
+
CREATE INDEX IF NOT EXISTS idx_inferred_relations_status
|
|
324
|
+
ON inferred_relations(status);
|
|
325
|
+
CREATE INDEX IF NOT EXISTS idx_relation_usage_relation
|
|
326
|
+
ON relation_usage_events(relation_id);
|
|
327
|
+
CREATE INDEX IF NOT EXISTS idx_relation_usage_created_at
|
|
328
|
+
ON relation_usage_events(created_at);
|
|
329
|
+
CREATE INDEX IF NOT EXISTS idx_relation_usage_rollups_last_event_at
|
|
330
|
+
ON relation_usage_rollups(last_event_at);
|
|
331
|
+
CREATE INDEX IF NOT EXISTS idx_search_feedback_result
|
|
332
|
+
ON search_feedback_events(result_type, result_id, created_at DESC);
|
|
333
|
+
CREATE INDEX IF NOT EXISTS idx_search_feedback_created_at
|
|
334
|
+
ON search_feedback_events(created_at DESC);
|
|
335
|
+
CREATE INDEX IF NOT EXISTS idx_search_feedback_rollups_result
|
|
336
|
+
ON search_feedback_rollups(result_type, total_delta DESC, last_event_at DESC);
|
|
337
|
+
CREATE INDEX IF NOT EXISTS idx_governance_events_entity
|
|
338
|
+
ON governance_events(entity_type, entity_id, created_at DESC);
|
|
339
|
+
CREATE INDEX IF NOT EXISTS idx_governance_state_state
|
|
340
|
+
ON governance_state(state, confidence ASC, last_transition_at DESC);
|
|
341
|
+
CREATE INDEX IF NOT EXISTS idx_activities_target ON activities(target_node_id);
|
|
342
|
+
CREATE INDEX IF NOT EXISTS idx_activities_target_created_at
|
|
343
|
+
ON activities(target_node_id, created_at DESC);
|
|
344
|
+
CREATE INDEX IF NOT EXISTS idx_artifacts_node ON artifacts(node_id);
|
|
345
|
+
CREATE INDEX IF NOT EXISTS idx_artifacts_node_created_at
|
|
346
|
+
ON artifacts(node_id, created_at DESC);
|
|
347
|
+
CREATE INDEX IF NOT EXISTS idx_artifacts_path_created_at
|
|
348
|
+
ON artifacts(path, created_at DESC, node_id);
|
|
349
|
+
CREATE INDEX IF NOT EXISTS idx_provenance_entity ON provenance_events(entity_type, entity_id);
|
|
350
|
+
CREATE INDEX IF NOT EXISTS idx_provenance_entity_timestamp
|
|
351
|
+
ON provenance_events(entity_type, entity_id, timestamp DESC);
|
|
352
|
+
`);
|
|
353
|
+
}
|
|
354
|
+
function detectSqliteVecVersion(db) {
|
|
355
|
+
try {
|
|
356
|
+
const row = db.prepare(`SELECT vec_version() AS version`).get();
|
|
357
|
+
return typeof row?.version === "string" ? row.version : null;
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
export function getSqliteVecExtensionRuntime(db) {
|
|
364
|
+
return sqliteVecStateByDb.get(db) ?? {
|
|
365
|
+
isLoaded: false,
|
|
366
|
+
version: null,
|
|
367
|
+
loadError: null
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
export function openDatabase(paths, options = {}) {
|
|
371
|
+
const db = new DatabaseSync(paths.dbPath, { allowExtension: true });
|
|
372
|
+
const runtime = {
|
|
373
|
+
isLoaded: false,
|
|
374
|
+
version: null,
|
|
375
|
+
loadError: null
|
|
376
|
+
};
|
|
377
|
+
try {
|
|
378
|
+
(options.sqliteVecLoader ?? loadSqliteVec)(db);
|
|
379
|
+
runtime.isLoaded = true;
|
|
380
|
+
runtime.version = detectSqliteVecVersion(db);
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
runtime.loadError = error instanceof Error ? error.message : String(error);
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
try {
|
|
387
|
+
db.enableLoadExtension(false);
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
// Keep startup resilient even if the runtime does not expose toggling.
|
|
391
|
+
}
|
|
392
|
+
sqliteVecStateByDb.set(db, runtime);
|
|
393
|
+
}
|
|
394
|
+
execMigration(db);
|
|
395
|
+
return db;
|
|
396
|
+
}
|
|
397
|
+
export function getSchemaVersion() {
|
|
398
|
+
return schemaVersion;
|
|
399
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class AppError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
statusCode;
|
|
4
|
+
details;
|
|
5
|
+
constructor(statusCode, code, message, details) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.statusCode = statusCode;
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.details = details;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function assertPresent(value, message, details) {
|
|
13
|
+
if (value === null || value === undefined) {
|
|
14
|
+
throw new AppError(404, "NOT_FOUND", message, details);
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|