@jigyasudham/veto 3.1.0 → 3.1.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/LICENSE +21 -21
- package/README.md +633 -627
- package/dist/agents/llm-runner.js +57 -57
- package/dist/cli/statusline.js +2 -2
- package/dist/cli.js +26 -26
- package/dist/memory/decisions.js +1 -1
- package/dist/memory/local.js +144 -144
- package/dist/memory/schema.js +228 -228
- package/dist/memory/sync.js +27 -27
- package/dist/memory/sync.js.map +1 -1
- package/dist/router/implicit-outcomes.js +4 -4
- package/dist/router/learning-updater.js +45 -45
- package/dist/server.js +1 -1
- package/dist/tools/compact.js +3 -3
- package/dist/transcripts/archive.js +5 -5
- package/dist/transcripts/ingest.js +6 -6
- package/dist/transcripts/mapping.js +5 -5
- package/dist/transcripts/pyramid.js +2 -2
- package/dist/transcripts/schema.js +104 -104
- package/dist/transcripts/search.js +3 -3
- package/dist/transcripts/vectors.js +2 -2
- package/package.json +70 -62
- package/server.json +23 -23
- package/dist/skills/development/skill-api-design.d.ts +0 -15
- package/dist/skills/development/skill-api-design.d.ts.map +0 -1
- package/dist/skills/development/skill-api-design.js +0 -313
- package/dist/skills/development/skill-api-design.js.map +0 -1
- package/dist/skills/development/skill-auth.d.ts +0 -15
- package/dist/skills/development/skill-auth.d.ts.map +0 -1
- package/dist/skills/development/skill-auth.js +0 -255
- package/dist/skills/development/skill-auth.js.map +0 -1
- package/dist/skills/development/skill-ci-cd.d.ts +0 -2
- package/dist/skills/development/skill-ci-cd.d.ts.map +0 -1
- package/dist/skills/development/skill-ci-cd.js +0 -2
- package/dist/skills/development/skill-ci-cd.js.map +0 -1
- package/dist/skills/development/skill-crud.d.ts +0 -15
- package/dist/skills/development/skill-crud.d.ts.map +0 -1
- package/dist/skills/development/skill-crud.js +0 -193
- package/dist/skills/development/skill-crud.js.map +0 -1
- package/dist/skills/development/skill-db-schema.d.ts +0 -2
- package/dist/skills/development/skill-db-schema.d.ts.map +0 -1
- package/dist/skills/development/skill-db-schema.js +0 -2
- package/dist/skills/development/skill-db-schema.js.map +0 -1
- package/dist/skills/development/skill-docker.d.ts +0 -2
- package/dist/skills/development/skill-docker.d.ts.map +0 -1
- package/dist/skills/development/skill-docker.js +0 -2
- package/dist/skills/development/skill-docker.js.map +0 -1
- package/dist/skills/development/skill-env-setup.d.ts +0 -2
- package/dist/skills/development/skill-env-setup.d.ts.map +0 -1
- package/dist/skills/development/skill-env-setup.js +0 -2
- package/dist/skills/development/skill-env-setup.js.map +0 -1
- package/dist/skills/development/skill-scaffold.d.ts +0 -15
- package/dist/skills/development/skill-scaffold.d.ts.map +0 -1
- package/dist/skills/development/skill-scaffold.js +0 -299
- package/dist/skills/development/skill-scaffold.js.map +0 -1
- package/dist/skills/intelligence/skill-complexity-score.d.ts +0 -15
- package/dist/skills/intelligence/skill-complexity-score.d.ts.map +0 -1
- package/dist/skills/intelligence/skill-complexity-score.js +0 -66
- package/dist/skills/intelligence/skill-complexity-score.js.map +0 -1
- package/dist/skills/intelligence/skill-cost-track.d.ts +0 -14
- package/dist/skills/intelligence/skill-cost-track.d.ts.map +0 -1
- package/dist/skills/intelligence/skill-cost-track.js +0 -36
- package/dist/skills/intelligence/skill-cost-track.js.map +0 -1
- package/dist/skills/intelligence/skill-learning-loop.d.ts +0 -15
- package/dist/skills/intelligence/skill-learning-loop.d.ts.map +0 -1
- package/dist/skills/intelligence/skill-learning-loop.js +0 -66
- package/dist/skills/intelligence/skill-learning-loop.js.map +0 -1
- package/dist/skills/intelligence/skill-pattern-detect.d.ts +0 -14
- package/dist/skills/intelligence/skill-pattern-detect.d.ts.map +0 -1
- package/dist/skills/intelligence/skill-pattern-detect.js +0 -35
- package/dist/skills/intelligence/skill-pattern-detect.js.map +0 -1
- package/dist/skills/intelligence/skill-rate-watch.d.ts +0 -15
- package/dist/skills/intelligence/skill-rate-watch.d.ts.map +0 -1
- package/dist/skills/intelligence/skill-rate-watch.js +0 -58
- package/dist/skills/intelligence/skill-rate-watch.js.map +0 -1
- package/dist/skills/memory/skill-context-compress.d.ts +0 -15
- package/dist/skills/memory/skill-context-compress.d.ts.map +0 -1
- package/dist/skills/memory/skill-context-compress.js +0 -82
- package/dist/skills/memory/skill-context-compress.js.map +0 -1
- package/dist/skills/memory/skill-cross-sync.d.ts +0 -15
- package/dist/skills/memory/skill-cross-sync.d.ts.map +0 -1
- package/dist/skills/memory/skill-cross-sync.js +0 -88
- package/dist/skills/memory/skill-cross-sync.js.map +0 -1
- package/dist/skills/memory/skill-decision-log.d.ts +0 -15
- package/dist/skills/memory/skill-decision-log.d.ts.map +0 -1
- package/dist/skills/memory/skill-decision-log.js +0 -103
- package/dist/skills/memory/skill-decision-log.js.map +0 -1
- package/dist/skills/memory/skill-session-restore.d.ts +0 -15
- package/dist/skills/memory/skill-session-restore.d.ts.map +0 -1
- package/dist/skills/memory/skill-session-restore.js +0 -39
- package/dist/skills/memory/skill-session-restore.js.map +0 -1
- package/dist/skills/memory/skill-session-save.d.ts +0 -15
- package/dist/skills/memory/skill-session-save.d.ts.map +0 -1
- package/dist/skills/memory/skill-session-save.js +0 -74
- package/dist/skills/memory/skill-session-save.js.map +0 -1
- package/dist/skills/quality/skill-accessibility.d.ts +0 -2
- package/dist/skills/quality/skill-accessibility.d.ts.map +0 -1
- package/dist/skills/quality/skill-accessibility.js +0 -2
- package/dist/skills/quality/skill-accessibility.js.map +0 -1
- package/dist/skills/quality/skill-code-review.d.ts +0 -15
- package/dist/skills/quality/skill-code-review.d.ts.map +0 -1
- package/dist/skills/quality/skill-code-review.js +0 -60
- package/dist/skills/quality/skill-code-review.js.map +0 -1
- package/dist/skills/quality/skill-docs-gen.d.ts +0 -2
- package/dist/skills/quality/skill-docs-gen.d.ts.map +0 -1
- package/dist/skills/quality/skill-docs-gen.js +0 -2
- package/dist/skills/quality/skill-docs-gen.js.map +0 -1
- package/dist/skills/quality/skill-perf-audit.d.ts +0 -2
- package/dist/skills/quality/skill-perf-audit.d.ts.map +0 -1
- package/dist/skills/quality/skill-perf-audit.js +0 -2
- package/dist/skills/quality/skill-perf-audit.js.map +0 -1
- package/dist/skills/quality/skill-security-scan.d.ts +0 -15
- package/dist/skills/quality/skill-security-scan.d.ts.map +0 -1
- package/dist/skills/quality/skill-security-scan.js +0 -67
- package/dist/skills/quality/skill-security-scan.js.map +0 -1
- package/dist/skills/quality/skill-test-suite.d.ts +0 -15
- package/dist/skills/quality/skill-test-suite.d.ts.map +0 -1
- package/dist/skills/quality/skill-test-suite.js +0 -274
- package/dist/skills/quality/skill-test-suite.js.map +0 -1
- package/dist/skills/workflow/skill-deploy.d.ts +0 -2
- package/dist/skills/workflow/skill-deploy.d.ts.map +0 -1
- package/dist/skills/workflow/skill-deploy.js +0 -2
- package/dist/skills/workflow/skill-deploy.js.map +0 -1
- package/dist/skills/workflow/skill-git-workflow.d.ts +0 -2
- package/dist/skills/workflow/skill-git-workflow.d.ts.map +0 -1
- package/dist/skills/workflow/skill-git-workflow.js +0 -2
- package/dist/skills/workflow/skill-git-workflow.js.map +0 -1
- package/dist/skills/workflow/skill-rollback.d.ts +0 -2
- package/dist/skills/workflow/skill-rollback.d.ts.map +0 -1
- package/dist/skills/workflow/skill-rollback.js +0 -2
- package/dist/skills/workflow/skill-rollback.js.map +0 -1
- package/dist/skills/workflow/skill-task-breakdown.d.ts +0 -2
- package/dist/skills/workflow/skill-task-breakdown.d.ts.map +0 -1
- package/dist/skills/workflow/skill-task-breakdown.js +0 -2
- package/dist/skills/workflow/skill-task-breakdown.js.map +0 -1
|
@@ -36,10 +36,10 @@ export function mineImplicitOutcomes() {
|
|
|
36
36
|
const wmRow = db.prepare('SELECT pattern_val FROM patterns WHERE pattern_key = ?').get(WATERMARK_KEY);
|
|
37
37
|
const since = wmRow?.pattern_val ?? '1970-01-01 00:00:00';
|
|
38
38
|
const placeholders = toolNames.map(() => '?').join(',');
|
|
39
|
-
const rows = db.prepare(`SELECT id, session_id, tool_name, result_status, recorded_at
|
|
40
|
-
FROM tool_call_trace_log
|
|
41
|
-
WHERE recorded_at > ? AND tool_name IN (${placeholders})
|
|
42
|
-
ORDER BY recorded_at ASC
|
|
39
|
+
const rows = db.prepare(`SELECT id, session_id, tool_name, result_status, recorded_at
|
|
40
|
+
FROM tool_call_trace_log
|
|
41
|
+
WHERE recorded_at > ? AND tool_name IN (${placeholders})
|
|
42
|
+
ORDER BY recorded_at ASC
|
|
43
43
|
LIMIT ${BATCH_LIMIT}`).all(since, ...toolNames);
|
|
44
44
|
if (rows.length === 0)
|
|
45
45
|
return { mined: 0, errors: 0, retries: 0 };
|
|
@@ -5,7 +5,7 @@ import { getConfig } from '../memory/config.js';
|
|
|
5
5
|
// ─── Record ───────────────────────────────────────────────────────────────────
|
|
6
6
|
export function recordOutcome(taskType, complexity, modelTier, agent, outputQuality, tokensUsed = 0, fileExt) {
|
|
7
7
|
const db = getDb();
|
|
8
|
-
db.prepare(`INSERT INTO learning_data (id, task_type, complexity, model_tier, output_quality, tokens_used, agent)
|
|
8
|
+
db.prepare(`INSERT INTO learning_data (id, task_type, complexity, model_tier, output_quality, tokens_used, agent)
|
|
9
9
|
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), taskType, complexity, modelTier, outputQuality, tokensUsed, agent);
|
|
10
10
|
// Predictive routing: track agent quality per file extension and task type keyword
|
|
11
11
|
if (agent && agent !== 'dynamic' && outputQuality > 0) {
|
|
@@ -50,8 +50,8 @@ export function getRecommendedAgent(taskType, fileExt) {
|
|
|
50
50
|
const db = getDb();
|
|
51
51
|
// Try file extension match first (more specific)
|
|
52
52
|
if (fileExt) {
|
|
53
|
-
const rows = db.prepare(`SELECT pattern_key, pattern_val, confidence, seen_count FROM patterns
|
|
54
|
-
WHERE pattern_key LIKE ? AND seen_count >= 3
|
|
53
|
+
const rows = db.prepare(`SELECT pattern_key, pattern_val, confidence, seen_count FROM patterns
|
|
54
|
+
WHERE pattern_key LIKE ? AND seen_count >= 3
|
|
55
55
|
ORDER BY confidence DESC LIMIT 1`).all(`file_agent:${fileExt.toLowerCase()}:%`);
|
|
56
56
|
if (rows.length > 0 && rows[0].confidence >= 0.7)
|
|
57
57
|
return rows[0].pattern_val;
|
|
@@ -59,8 +59,8 @@ export function getRecommendedAgent(taskType, fileExt) {
|
|
|
59
59
|
// Fall back to task keyword match
|
|
60
60
|
const keyword = taskType.toLowerCase().split(/[\s_-]/)[0];
|
|
61
61
|
if (keyword) {
|
|
62
|
-
const rows = db.prepare(`SELECT pattern_key, pattern_val, confidence, seen_count FROM patterns
|
|
63
|
-
WHERE pattern_key LIKE ? AND seen_count >= 3
|
|
62
|
+
const rows = db.prepare(`SELECT pattern_key, pattern_val, confidence, seen_count FROM patterns
|
|
63
|
+
WHERE pattern_key LIKE ? AND seen_count >= 3
|
|
64
64
|
ORDER BY confidence DESC LIMIT 1`).all(`task_agent:${keyword}:%`);
|
|
65
65
|
if (rows.length > 0 && rows[0].confidence >= 0.7)
|
|
66
66
|
return rows[0].pattern_val;
|
|
@@ -70,7 +70,7 @@ export function getRecommendedAgent(taskType, fileExt) {
|
|
|
70
70
|
// ─── Stats ────────────────────────────────────────────────────────────────────
|
|
71
71
|
export function getLearningStats() {
|
|
72
72
|
const db = getDb();
|
|
73
|
-
const rows = db.prepare(`SELECT model_tier, COUNT(*) as count, AVG(output_quality) as avg_quality
|
|
73
|
+
const rows = db.prepare(`SELECT model_tier, COUNT(*) as count, AVG(output_quality) as avg_quality
|
|
74
74
|
FROM learning_data GROUP BY model_tier`).all();
|
|
75
75
|
const total_tasks = rows.reduce((sum, r) => sum + r.count, 0);
|
|
76
76
|
const tier_breakdown = {};
|
|
@@ -85,16 +85,16 @@ export function getLearningStats() {
|
|
|
85
85
|
}
|
|
86
86
|
export function getAgentPerformanceStats() {
|
|
87
87
|
const db = getDb();
|
|
88
|
-
const rows = db.prepare(`
|
|
89
|
-
SELECT agent,
|
|
90
|
-
COUNT(*) as task_count,
|
|
91
|
-
AVG(output_quality) as avg_quality,
|
|
92
|
-
AVG(tokens_used) as avg_tokens,
|
|
93
|
-
SUM(CASE WHEN output_quality < 60 THEN 1 ELSE 0 END) as low_quality_count
|
|
94
|
-
FROM learning_data
|
|
95
|
-
WHERE agent IS NOT NULL
|
|
96
|
-
GROUP BY agent
|
|
97
|
-
ORDER BY avg_quality ASC
|
|
88
|
+
const rows = db.prepare(`
|
|
89
|
+
SELECT agent,
|
|
90
|
+
COUNT(*) as task_count,
|
|
91
|
+
AVG(output_quality) as avg_quality,
|
|
92
|
+
AVG(tokens_used) as avg_tokens,
|
|
93
|
+
SUM(CASE WHEN output_quality < 60 THEN 1 ELSE 0 END) as low_quality_count
|
|
94
|
+
FROM learning_data
|
|
95
|
+
WHERE agent IS NOT NULL
|
|
96
|
+
GROUP BY agent
|
|
97
|
+
ORDER BY avg_quality ASC
|
|
98
98
|
`).all();
|
|
99
99
|
return rows.map(r => ({
|
|
100
100
|
agent: r.agent,
|
|
@@ -107,23 +107,23 @@ export function getAgentPerformanceStats() {
|
|
|
107
107
|
}
|
|
108
108
|
export function getTaskTypeBreakdown() {
|
|
109
109
|
const db = getDb();
|
|
110
|
-
const rows = db.prepare(`
|
|
111
|
-
SELECT task_type,
|
|
112
|
-
COUNT(*) as count,
|
|
113
|
-
AVG(complexity) as avg_complexity,
|
|
114
|
-
AVG(output_quality) as avg_quality,
|
|
115
|
-
model_tier as dominant_tier
|
|
116
|
-
FROM learning_data
|
|
117
|
-
GROUP BY task_type, model_tier
|
|
118
|
-
HAVING COUNT(*) = (
|
|
119
|
-
SELECT MAX(c) FROM (
|
|
120
|
-
SELECT COUNT(*) as c FROM learning_data l2
|
|
121
|
-
WHERE l2.task_type = learning_data.task_type
|
|
122
|
-
GROUP BY l2.model_tier
|
|
123
|
-
)
|
|
124
|
-
)
|
|
125
|
-
ORDER BY count DESC
|
|
126
|
-
LIMIT 50
|
|
110
|
+
const rows = db.prepare(`
|
|
111
|
+
SELECT task_type,
|
|
112
|
+
COUNT(*) as count,
|
|
113
|
+
AVG(complexity) as avg_complexity,
|
|
114
|
+
AVG(output_quality) as avg_quality,
|
|
115
|
+
model_tier as dominant_tier
|
|
116
|
+
FROM learning_data
|
|
117
|
+
GROUP BY task_type, model_tier
|
|
118
|
+
HAVING COUNT(*) = (
|
|
119
|
+
SELECT MAX(c) FROM (
|
|
120
|
+
SELECT COUNT(*) as c FROM learning_data l2
|
|
121
|
+
WHERE l2.task_type = learning_data.task_type
|
|
122
|
+
GROUP BY l2.model_tier
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
ORDER BY count DESC
|
|
126
|
+
LIMIT 50
|
|
127
127
|
`).all();
|
|
128
128
|
return rows.map(r => {
|
|
129
129
|
// Under-tiered: task scored high quality but at a tier below what complexity suggests
|
|
@@ -142,15 +142,15 @@ export function getTaskTypeBreakdown() {
|
|
|
142
142
|
export function getCouncilInsights() {
|
|
143
143
|
const db = getDb();
|
|
144
144
|
// Correlate council approvals (GREEN/YELLOW) with debugging sessions that came after
|
|
145
|
-
const rows = db.prepare(`
|
|
146
|
-
SELECT co.verdict, co.task, co.debated_at,
|
|
147
|
-
d.decision as related_decision, d.made_at as decision_at
|
|
148
|
-
FROM council_outcomes co
|
|
149
|
-
LEFT JOIN decisions d ON d.session_id = co.session_id
|
|
150
|
-
AND d.decision LIKE '%debug%' OR d.decision LIKE '%fix%' OR d.decision LIKE '%bug%'
|
|
151
|
-
WHERE co.verdict IN ('GREEN', 'YELLOW')
|
|
152
|
-
ORDER BY co.debated_at DESC
|
|
153
|
-
LIMIT 30
|
|
145
|
+
const rows = db.prepare(`
|
|
146
|
+
SELECT co.verdict, co.task, co.debated_at,
|
|
147
|
+
d.decision as related_decision, d.made_at as decision_at
|
|
148
|
+
FROM council_outcomes co
|
|
149
|
+
LEFT JOIN decisions d ON d.session_id = co.session_id
|
|
150
|
+
AND d.decision LIKE '%debug%' OR d.decision LIKE '%fix%' OR d.decision LIKE '%bug%'
|
|
151
|
+
WHERE co.verdict IN ('GREEN', 'YELLOW')
|
|
152
|
+
ORDER BY co.debated_at DESC
|
|
153
|
+
LIMIT 30
|
|
154
154
|
`).all();
|
|
155
155
|
return rows.map(r => {
|
|
156
156
|
let days_to_debug = null;
|
|
@@ -273,9 +273,9 @@ export function recordRoutingFeedback(opts) {
|
|
|
273
273
|
const now = new Date().toISOString();
|
|
274
274
|
const expiresAt = new Date(Date.now() + FEEDBACK_TTL_DAYS * 86_400_000).toISOString();
|
|
275
275
|
const task_hash = opts.task.toLowerCase().replace(/\s+/g, '_').slice(0, 40);
|
|
276
|
-
db.prepare(`
|
|
277
|
-
INSERT INTO routing_feedback (id, task_hash, task_snippet, complexity, model_tier, agent, outcome, session_id, recorded_at, expires_at)
|
|
278
|
-
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)
|
|
276
|
+
db.prepare(`
|
|
277
|
+
INSERT INTO routing_feedback (id, task_hash, task_snippet, complexity, model_tier, agent, outcome, session_id, recorded_at, expires_at)
|
|
278
|
+
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)
|
|
279
279
|
`).run(id, task_hash, opts.task.slice(0, 100), opts.complexity, opts.model_tier, opts.agent ?? null, opts.session_id ?? null, now, expiresAt);
|
|
280
280
|
return id;
|
|
281
281
|
}
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Veto MCP Server —
|
|
2
|
+
// Veto MCP Server — 93 tools, LLM council + auto-learning router
|
|
3
3
|
// Suppress node:sqlite experimental warning — it would corrupt the MCP stdio protocol
|
|
4
4
|
process.removeAllListeners('warning');
|
|
5
5
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
package/dist/tools/compact.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Compact tool surface — progressive disclosure for clients where
|
|
2
|
-
// schemas would burn ~
|
|
1
|
+
// Compact tool surface — progressive disclosure for clients where 93 tool
|
|
2
|
+
// schemas would burn ~20K context tokens before the user types a word.
|
|
3
3
|
//
|
|
4
4
|
// In compact mode (VETO_COMPACT=1 or config compact_tools=true) ListTools
|
|
5
5
|
// exposes only a small first-class set plus two meta-tools: veto_find_tools
|
|
@@ -22,7 +22,7 @@ const FIRST_CLASS = [
|
|
|
22
22
|
export const META_TOOL_DEFINITIONS = [
|
|
23
23
|
{
|
|
24
24
|
name: 'veto_find_tools',
|
|
25
|
-
description: 'Searches the full Veto catalog (
|
|
25
|
+
description: 'Searches the full Veto catalog (93 tools: agents, reviews, advisors, generators, memory, learning, git, workflow) by keyword and returns matching tool schemas. Compact mode exposes only core tools up front — call this first to discover the right tool, then invoke it via veto_call.',
|
|
26
26
|
inputSchema: {
|
|
27
27
|
type: 'object',
|
|
28
28
|
properties: {
|
|
@@ -79,14 +79,14 @@ export async function captureSession(opts = {}) {
|
|
|
79
79
|
if (existing) {
|
|
80
80
|
// Content changed (grew): overwrite the row, reset the index watermark AND
|
|
81
81
|
// parser_version so Step 6 re-derives events from the new bytes.
|
|
82
|
-
db.prepare(`UPDATE archives SET archive_path=?, content_sha256=?, source_bytes=?, archive_bytes=?,
|
|
83
|
-
project_dir=COALESCE(?, project_dir), veto_session_id=COALESCE(?, veto_session_id),
|
|
82
|
+
db.prepare(`UPDATE archives SET archive_path=?, content_sha256=?, source_bytes=?, archive_bytes=?,
|
|
83
|
+
project_dir=COALESCE(?, project_dir), veto_session_id=COALESCE(?, veto_session_id),
|
|
84
84
|
updated_at=?, indexed_through_seq=0, parser_version=0 WHERE id=?`).run(archivePath, sha256, bytes, archiveBytes, proj, opts.vetoSessionId ?? null, now, id);
|
|
85
85
|
}
|
|
86
86
|
else {
|
|
87
|
-
db.prepare(`INSERT INTO archives (id, source, source_session_id, project_dir, veto_session_id, archive_path,
|
|
88
|
-
content_sha256, source_bytes, archive_bytes, source_format_hint, parser_version, indexed_through_seq,
|
|
89
|
-
captured_at, updated_at)
|
|
87
|
+
db.prepare(`INSERT INTO archives (id, source, source_session_id, project_dir, veto_session_id, archive_path,
|
|
88
|
+
content_sha256, source_bytes, archive_bytes, source_format_hint, parser_version, indexed_through_seq,
|
|
89
|
+
captured_at, updated_at)
|
|
90
90
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?)`).run(id, source, sessionId, proj, opts.vetoSessionId ?? null, archivePath, sha256, bytes, archiveBytes, formatHint, now, now);
|
|
91
91
|
}
|
|
92
92
|
return {
|
|
@@ -42,13 +42,13 @@ export function ingestArchive(archiveId) {
|
|
|
42
42
|
if (sessionIds.size > 0 && !sessionIds.has(row.source_session_id)) {
|
|
43
43
|
return { status: 'session_mismatch', reason: 'archive session id not present in transcript' };
|
|
44
44
|
}
|
|
45
|
-
const ins = db.prepare(`INSERT INTO events (id, archive_id, source_session_id, seq, line_index, block_index, kind,
|
|
46
|
-
source_type, role, tool_name, text, secret_count, event_uuid, parent_uuid, is_sidechain,
|
|
47
|
-
ts_source, ts_utc, raw_offset, raw_length)
|
|
45
|
+
const ins = db.prepare(`INSERT INTO events (id, archive_id, source_session_id, seq, line_index, block_index, kind,
|
|
46
|
+
source_type, role, tool_name, text, secret_count, event_uuid, parent_uuid, is_sidechain,
|
|
47
|
+
ts_source, ts_utc, raw_offset, raw_length)
|
|
48
48
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
49
|
-
const insDoc = db.prepare(`INSERT INTO search_docs (event_id, archive_id, source_session_id, project_dir, seq, kind, len)
|
|
49
|
+
const insDoc = db.prepare(`INSERT INTO search_docs (event_id, archive_id, source_session_id, project_dir, seq, kind, len)
|
|
50
50
|
VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING id`);
|
|
51
|
-
const insTerm = db.prepare(`INSERT INTO search_terms (term) VALUES (?)
|
|
51
|
+
const insTerm = db.prepare(`INSERT INTO search_terms (term) VALUES (?)
|
|
52
52
|
ON CONFLICT(term) DO UPDATE SET term = term RETURNING id`);
|
|
53
53
|
const insPosting = db.prepare(`INSERT INTO search_postings (term_id, doc_id, tf) VALUES (?, ?, ?)`);
|
|
54
54
|
// Term ids are cached per ingest run — a session reuses its vocabulary
|
|
@@ -86,7 +86,7 @@ export function ingestArchive(archiveId) {
|
|
|
86
86
|
}
|
|
87
87
|
// Vectors were just dropped with their docs; clearing the marker lets
|
|
88
88
|
// the lazy embed pass rebuild them without re-parsing the transcript.
|
|
89
|
-
db.prepare(`UPDATE archives SET indexed_through_seq = ?, parser_version = ?, chunker_version = 0, embed_model = NULL
|
|
89
|
+
db.prepare(`UPDATE archives SET indexed_through_seq = ?, parser_version = ?, chunker_version = 0, embed_model = NULL
|
|
90
90
|
WHERE id = ?`).run(events.length, PARSER_VERSION, archiveId);
|
|
91
91
|
db.exec('COMMIT');
|
|
92
92
|
resetCaches();
|
|
@@ -17,11 +17,11 @@ export function recordSessionMapping(m) {
|
|
|
17
17
|
const db = getTranscriptsDb();
|
|
18
18
|
const now = new Date().toISOString();
|
|
19
19
|
const proj = m.projectDir ? normalizeProjectDir(m.projectDir) : null;
|
|
20
|
-
db.prepare(`INSERT INTO session_map (source, source_session_id, transcript_path, project_dir, last_seen_at)
|
|
21
|
-
VALUES (?, ?, ?, ?, ?)
|
|
22
|
-
ON CONFLICT(source, source_session_id) DO UPDATE SET
|
|
23
|
-
transcript_path = excluded.transcript_path,
|
|
24
|
-
project_dir = excluded.project_dir,
|
|
20
|
+
db.prepare(`INSERT INTO session_map (source, source_session_id, transcript_path, project_dir, last_seen_at)
|
|
21
|
+
VALUES (?, ?, ?, ?, ?)
|
|
22
|
+
ON CONFLICT(source, source_session_id) DO UPDATE SET
|
|
23
|
+
transcript_path = excluded.transcript_path,
|
|
24
|
+
project_dir = excluded.project_dir,
|
|
25
25
|
last_seen_at = excluded.last_seen_at`).run(m.source ?? 'claude', m.sourceSessionId, m.transcriptPath, proj, now);
|
|
26
26
|
}
|
|
27
27
|
/** Look up the mapping for an exact session id. */
|
|
@@ -15,8 +15,8 @@ export const ERROR_RE = /\b(error|errno|failed|failure|exception|traceback|not f
|
|
|
15
15
|
/** L2 — the conversation spine (user + assistant text, in order). */
|
|
16
16
|
export function buildSpine(archiveId) {
|
|
17
17
|
const db = getTranscriptsDb();
|
|
18
|
-
const rows = db.prepare(`SELECT seq, role, kind, ts_utc, text FROM events
|
|
19
|
-
WHERE archive_id = ? AND kind IN ('user_message','assistant_message')
|
|
18
|
+
const rows = db.prepare(`SELECT seq, role, kind, ts_utc, text FROM events
|
|
19
|
+
WHERE archive_id = ? AND kind IN ('user_message','assistant_message')
|
|
20
20
|
ORDER BY seq`).all(archiveId);
|
|
21
21
|
return rows.map(r => ({
|
|
22
22
|
seq: r.seq,
|
|
@@ -17,39 +17,39 @@ export const MIGRATIONS = [
|
|
|
17
17
|
// v1 — the two foundational anchor tables. Events / segments / FTS get their
|
|
18
18
|
// own later migrations (Steps 6/9/10) so we don't big-bang the schema.
|
|
19
19
|
version: 1,
|
|
20
|
-
up: `
|
|
21
|
-
-- L0 archive index: one row per captured source-CLI session.
|
|
22
|
-
CREATE TABLE IF NOT EXISTS archives (
|
|
23
|
-
id TEXT PRIMARY KEY,
|
|
24
|
-
source TEXT NOT NULL DEFAULT 'claude', -- claude|codex|gemini
|
|
25
|
-
source_session_id TEXT NOT NULL,
|
|
26
|
-
project_dir TEXT, -- normalized (lowercase drive)
|
|
27
|
-
veto_session_id TEXT, -- link to veto.db sessions.id when known
|
|
28
|
-
archive_path TEXT NOT NULL, -- absolute path to the .gz on disk
|
|
29
|
-
content_sha256 TEXT NOT NULL, -- of raw source bytes (dedup key)
|
|
30
|
-
source_bytes INTEGER NOT NULL DEFAULT 0, -- uncompressed size
|
|
31
|
-
archive_bytes INTEGER NOT NULL DEFAULT 0, -- gz size on disk
|
|
32
|
-
source_format_hint TEXT, -- e.g. 'claude-jsonl'
|
|
33
|
-
parser_version INTEGER NOT NULL DEFAULT 0,
|
|
34
|
-
indexed_through_seq INTEGER NOT NULL DEFAULT 0, -- lazy-index watermark
|
|
35
|
-
captured_at TEXT NOT NULL,
|
|
36
|
-
updated_at TEXT NOT NULL,
|
|
37
|
-
UNIQUE(source, source_session_id)
|
|
38
|
-
);
|
|
39
|
-
CREATE INDEX IF NOT EXISTS idx_archives_project ON archives(project_dir);
|
|
40
|
-
CREATE INDEX IF NOT EXISTS idx_archives_sha ON archives(content_sha256);
|
|
41
|
-
|
|
42
|
-
-- Live host-session -> transcript-file mapping, UPSERTed fire-and-forget by
|
|
43
|
-
-- the statusline (Step 4) so save-time capture knows which file to read.
|
|
44
|
-
CREATE TABLE IF NOT EXISTS session_map (
|
|
45
|
-
source TEXT NOT NULL DEFAULT 'claude',
|
|
46
|
-
source_session_id TEXT NOT NULL,
|
|
47
|
-
transcript_path TEXT NOT NULL,
|
|
48
|
-
project_dir TEXT,
|
|
49
|
-
last_seen_at TEXT NOT NULL,
|
|
50
|
-
PRIMARY KEY (source, source_session_id)
|
|
51
|
-
);
|
|
52
|
-
CREATE INDEX IF NOT EXISTS idx_session_map_project ON session_map(project_dir);
|
|
20
|
+
up: `
|
|
21
|
+
-- L0 archive index: one row per captured source-CLI session.
|
|
22
|
+
CREATE TABLE IF NOT EXISTS archives (
|
|
23
|
+
id TEXT PRIMARY KEY,
|
|
24
|
+
source TEXT NOT NULL DEFAULT 'claude', -- claude|codex|gemini
|
|
25
|
+
source_session_id TEXT NOT NULL,
|
|
26
|
+
project_dir TEXT, -- normalized (lowercase drive)
|
|
27
|
+
veto_session_id TEXT, -- link to veto.db sessions.id when known
|
|
28
|
+
archive_path TEXT NOT NULL, -- absolute path to the .gz on disk
|
|
29
|
+
content_sha256 TEXT NOT NULL, -- of raw source bytes (dedup key)
|
|
30
|
+
source_bytes INTEGER NOT NULL DEFAULT 0, -- uncompressed size
|
|
31
|
+
archive_bytes INTEGER NOT NULL DEFAULT 0, -- gz size on disk
|
|
32
|
+
source_format_hint TEXT, -- e.g. 'claude-jsonl'
|
|
33
|
+
parser_version INTEGER NOT NULL DEFAULT 0,
|
|
34
|
+
indexed_through_seq INTEGER NOT NULL DEFAULT 0, -- lazy-index watermark
|
|
35
|
+
captured_at TEXT NOT NULL,
|
|
36
|
+
updated_at TEXT NOT NULL,
|
|
37
|
+
UNIQUE(source, source_session_id)
|
|
38
|
+
);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_archives_project ON archives(project_dir);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_archives_sha ON archives(content_sha256);
|
|
41
|
+
|
|
42
|
+
-- Live host-session -> transcript-file mapping, UPSERTed fire-and-forget by
|
|
43
|
+
-- the statusline (Step 4) so save-time capture knows which file to read.
|
|
44
|
+
CREATE TABLE IF NOT EXISTS session_map (
|
|
45
|
+
source TEXT NOT NULL DEFAULT 'claude',
|
|
46
|
+
source_session_id TEXT NOT NULL,
|
|
47
|
+
transcript_path TEXT NOT NULL,
|
|
48
|
+
project_dir TEXT,
|
|
49
|
+
last_seen_at TEXT NOT NULL,
|
|
50
|
+
PRIMARY KEY (source, source_session_id)
|
|
51
|
+
);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_session_map_project ON session_map(project_dir);
|
|
53
53
|
`,
|
|
54
54
|
},
|
|
55
55
|
{
|
|
@@ -58,33 +58,33 @@ export const MIGRATIONS = [
|
|
|
58
58
|
// UNCOMPRESSED L0 bytes for exact expansion (Step 11). Unknown/unparsed lines
|
|
59
59
|
// are stored+ordered (kind='unknown') so format drift never loses data.
|
|
60
60
|
version: 2,
|
|
61
|
-
up: `
|
|
62
|
-
CREATE TABLE IF NOT EXISTS events (
|
|
63
|
-
id TEXT PRIMARY KEY,
|
|
64
|
-
archive_id TEXT NOT NULL,
|
|
65
|
-
source_session_id TEXT NOT NULL,
|
|
66
|
-
seq INTEGER NOT NULL, -- monotonic per archive, per event
|
|
67
|
-
line_index INTEGER NOT NULL, -- source JSONL line (0-based)
|
|
68
|
-
block_index INTEGER NOT NULL DEFAULT 0,
|
|
69
|
-
kind TEXT NOT NULL, -- EVENT_KINDS
|
|
70
|
-
source_type TEXT, -- verbatim source 'type' (user/assistant/system/…)
|
|
71
|
-
role TEXT,
|
|
72
|
-
tool_name TEXT, -- for tool_call/tool_result
|
|
73
|
-
text TEXT, -- normalized + MASKED content for FTS
|
|
74
|
-
secret_count INTEGER NOT NULL DEFAULT 0,
|
|
75
|
-
event_uuid TEXT, -- native uuid (Claude)
|
|
76
|
-
parent_uuid TEXT, -- native parentUuid (tree)
|
|
77
|
-
is_sidechain INTEGER NOT NULL DEFAULT 0,
|
|
78
|
-
ts_source TEXT, -- verbatim timestamp string
|
|
79
|
-
ts_utc TEXT, -- normalized ISO
|
|
80
|
-
raw_offset INTEGER NOT NULL DEFAULT 0,
|
|
81
|
-
raw_length INTEGER NOT NULL DEFAULT 0,
|
|
82
|
-
UNIQUE(archive_id, seq),
|
|
83
|
-
FOREIGN KEY (archive_id) REFERENCES archives(id)
|
|
84
|
-
);
|
|
85
|
-
CREATE INDEX IF NOT EXISTS idx_events_archive ON events(archive_id, seq);
|
|
86
|
-
CREATE INDEX IF NOT EXISTS idx_events_session ON events(source_session_id);
|
|
87
|
-
CREATE INDEX IF NOT EXISTS idx_events_kind ON events(kind);
|
|
61
|
+
up: `
|
|
62
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
63
|
+
id TEXT PRIMARY KEY,
|
|
64
|
+
archive_id TEXT NOT NULL,
|
|
65
|
+
source_session_id TEXT NOT NULL,
|
|
66
|
+
seq INTEGER NOT NULL, -- monotonic per archive, per event
|
|
67
|
+
line_index INTEGER NOT NULL, -- source JSONL line (0-based)
|
|
68
|
+
block_index INTEGER NOT NULL DEFAULT 0,
|
|
69
|
+
kind TEXT NOT NULL, -- EVENT_KINDS
|
|
70
|
+
source_type TEXT, -- verbatim source 'type' (user/assistant/system/…)
|
|
71
|
+
role TEXT,
|
|
72
|
+
tool_name TEXT, -- for tool_call/tool_result
|
|
73
|
+
text TEXT, -- normalized + MASKED content for FTS
|
|
74
|
+
secret_count INTEGER NOT NULL DEFAULT 0,
|
|
75
|
+
event_uuid TEXT, -- native uuid (Claude)
|
|
76
|
+
parent_uuid TEXT, -- native parentUuid (tree)
|
|
77
|
+
is_sidechain INTEGER NOT NULL DEFAULT 0,
|
|
78
|
+
ts_source TEXT, -- verbatim timestamp string
|
|
79
|
+
ts_utc TEXT, -- normalized ISO
|
|
80
|
+
raw_offset INTEGER NOT NULL DEFAULT 0,
|
|
81
|
+
raw_length INTEGER NOT NULL DEFAULT 0,
|
|
82
|
+
UNIQUE(archive_id, seq),
|
|
83
|
+
FOREIGN KEY (archive_id) REFERENCES archives(id)
|
|
84
|
+
);
|
|
85
|
+
CREATE INDEX IF NOT EXISTS idx_events_archive ON events(archive_id, seq);
|
|
86
|
+
CREATE INDEX IF NOT EXISTS idx_events_session ON events(source_session_id);
|
|
87
|
+
CREATE INDEX IF NOT EXISTS idx_events_kind ON events(kind);
|
|
88
88
|
`,
|
|
89
89
|
},
|
|
90
90
|
{
|
|
@@ -104,39 +104,39 @@ export const MIGRATIONS = [
|
|
|
104
104
|
// loads its module, which throws on exactly the Nodes this redo supports.
|
|
105
105
|
// Databases that ran the old dev-only v3 keep an inert orphan table.
|
|
106
106
|
version: 3,
|
|
107
|
-
up: `
|
|
108
|
-
-- One row per indexed event. project_dir/session denormalized so the
|
|
109
|
-
-- hot query is a single join (same shape events_fts had). The INTEGER id
|
|
110
|
-
-- exists so postings never carry a 36-char uuid — measured, that uuid
|
|
111
|
-
-- (stored twice per posting: PK + purge index) was ~3x the entire
|
|
112
|
-
-- index's justified size.
|
|
113
|
-
CREATE TABLE IF NOT EXISTS search_docs (
|
|
114
|
-
id INTEGER PRIMARY KEY,
|
|
115
|
-
event_id TEXT NOT NULL UNIQUE,
|
|
116
|
-
archive_id TEXT NOT NULL,
|
|
117
|
-
source_session_id TEXT NOT NULL,
|
|
118
|
-
project_dir TEXT,
|
|
119
|
-
seq INTEGER NOT NULL,
|
|
120
|
-
kind TEXT NOT NULL,
|
|
121
|
-
len INTEGER NOT NULL -- token count, for BM25 length norm
|
|
122
|
-
);
|
|
123
|
-
CREATE INDEX IF NOT EXISTS idx_search_docs_archive ON search_docs(archive_id);
|
|
124
|
-
CREATE INDEX IF NOT EXISTS idx_search_docs_scope
|
|
125
|
-
ON search_docs(project_dir, source_session_id);
|
|
126
|
-
|
|
127
|
-
-- Term dictionary: term text stored once, compact integer join key.
|
|
128
|
-
CREATE TABLE IF NOT EXISTS search_terms (
|
|
129
|
-
id INTEGER PRIMARY KEY,
|
|
130
|
-
term TEXT NOT NULL UNIQUE
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
CREATE TABLE IF NOT EXISTS search_postings (
|
|
134
|
-
term_id INTEGER NOT NULL,
|
|
135
|
-
doc_id INTEGER NOT NULL,
|
|
136
|
-
tf INTEGER NOT NULL,
|
|
137
|
-
PRIMARY KEY (term_id, doc_id)
|
|
138
|
-
) WITHOUT ROWID;
|
|
139
|
-
CREATE INDEX IF NOT EXISTS idx_postings_doc ON search_postings(doc_id);
|
|
107
|
+
up: `
|
|
108
|
+
-- One row per indexed event. project_dir/session denormalized so the
|
|
109
|
+
-- hot query is a single join (same shape events_fts had). The INTEGER id
|
|
110
|
+
-- exists so postings never carry a 36-char uuid — measured, that uuid
|
|
111
|
+
-- (stored twice per posting: PK + purge index) was ~3x the entire
|
|
112
|
+
-- index's justified size.
|
|
113
|
+
CREATE TABLE IF NOT EXISTS search_docs (
|
|
114
|
+
id INTEGER PRIMARY KEY,
|
|
115
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
116
|
+
archive_id TEXT NOT NULL,
|
|
117
|
+
source_session_id TEXT NOT NULL,
|
|
118
|
+
project_dir TEXT,
|
|
119
|
+
seq INTEGER NOT NULL,
|
|
120
|
+
kind TEXT NOT NULL,
|
|
121
|
+
len INTEGER NOT NULL -- token count, for BM25 length norm
|
|
122
|
+
);
|
|
123
|
+
CREATE INDEX IF NOT EXISTS idx_search_docs_archive ON search_docs(archive_id);
|
|
124
|
+
CREATE INDEX IF NOT EXISTS idx_search_docs_scope
|
|
125
|
+
ON search_docs(project_dir, source_session_id);
|
|
126
|
+
|
|
127
|
+
-- Term dictionary: term text stored once, compact integer join key.
|
|
128
|
+
CREATE TABLE IF NOT EXISTS search_terms (
|
|
129
|
+
id INTEGER PRIMARY KEY,
|
|
130
|
+
term TEXT NOT NULL UNIQUE
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
CREATE TABLE IF NOT EXISTS search_postings (
|
|
134
|
+
term_id INTEGER NOT NULL,
|
|
135
|
+
doc_id INTEGER NOT NULL,
|
|
136
|
+
tf INTEGER NOT NULL,
|
|
137
|
+
PRIMARY KEY (term_id, doc_id)
|
|
138
|
+
) WITHOUT ROWID;
|
|
139
|
+
CREATE INDEX IF NOT EXISTS idx_postings_doc ON search_postings(doc_id);
|
|
140
140
|
`,
|
|
141
141
|
},
|
|
142
142
|
{
|
|
@@ -158,17 +158,17 @@ export const MIGRATIONS = [
|
|
|
158
158
|
// parser_version, so ingest never depends on the model being installed and
|
|
159
159
|
// installing it later backfills without re-parsing anything.
|
|
160
160
|
version: 4,
|
|
161
|
-
up: `
|
|
162
|
-
CREATE TABLE IF NOT EXISTS search_vectors (
|
|
163
|
-
doc_id INTEGER NOT NULL,
|
|
164
|
-
chunk_index INTEGER NOT NULL,
|
|
165
|
-
vec BLOB NOT NULL, -- int8, one byte per dimension
|
|
166
|
-
norm REAL NOT NULL, -- L2 norm of the int8 vector
|
|
167
|
-
PRIMARY KEY (doc_id, chunk_index)
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
ALTER TABLE archives ADD COLUMN chunker_version INTEGER NOT NULL DEFAULT 0;
|
|
171
|
-
ALTER TABLE archives ADD COLUMN embed_model TEXT;
|
|
161
|
+
up: `
|
|
162
|
+
CREATE TABLE IF NOT EXISTS search_vectors (
|
|
163
|
+
doc_id INTEGER NOT NULL,
|
|
164
|
+
chunk_index INTEGER NOT NULL,
|
|
165
|
+
vec BLOB NOT NULL, -- int8, one byte per dimension
|
|
166
|
+
norm REAL NOT NULL, -- L2 norm of the int8 vector
|
|
167
|
+
PRIMARY KEY (doc_id, chunk_index)
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
ALTER TABLE archives ADD COLUMN chunker_version INTEGER NOT NULL DEFAULT 0;
|
|
171
|
+
ALTER TABLE archives ADD COLUMN embed_model TEXT;
|
|
172
172
|
`,
|
|
173
173
|
},
|
|
174
174
|
];
|
|
@@ -119,8 +119,8 @@ export function searchEvents(query, opts = {}) {
|
|
|
119
119
|
// Metadata and text for the returned rows only.
|
|
120
120
|
const docIds = top.map(([id]) => id);
|
|
121
121
|
const marks2 = docIds.map(() => '?').join(', ');
|
|
122
|
-
const metaRows = db.prepare(`SELECT d.id, d.event_id, d.archive_id, d.source_session_id, d.seq, d.kind, e.text
|
|
123
|
-
FROM search_docs d JOIN events e ON e.id = d.event_id
|
|
122
|
+
const metaRows = db.prepare(`SELECT d.id, d.event_id, d.archive_id, d.source_session_id, d.seq, d.kind, e.text
|
|
123
|
+
FROM search_docs d JOIN events e ON e.id = d.event_id
|
|
124
124
|
WHERE d.id IN (${marks2})`).all(...docIds);
|
|
125
125
|
const metaById = new Map(metaRows.map(r => [r.id, r]));
|
|
126
126
|
const termSet = new Set(kept.map(t => t.term));
|
|
@@ -261,7 +261,7 @@ export function searchEventsHybrid(query, opts = {}) {
|
|
|
261
261
|
const db = getTranscriptsDb();
|
|
262
262
|
// Resolve the semantic hits' doc ids to event metadata in one round trip.
|
|
263
263
|
const docIds = semantic.map(s => s.docId);
|
|
264
|
-
const docRows = db.prepare(`SELECT id, event_id, archive_id, source_session_id, seq, kind
|
|
264
|
+
const docRows = db.prepare(`SELECT id, event_id, archive_id, source_session_id, seq, kind
|
|
265
265
|
FROM search_docs WHERE id IN (${docIds.map(() => '?').join(', ')})`).all(...docIds);
|
|
266
266
|
const docById = new Map(docRows.map(r => [r.id, r]));
|
|
267
267
|
const fused = new Map();
|
|
@@ -68,8 +68,8 @@ export function embedArchive(archiveId) {
|
|
|
68
68
|
return { status: 'already_embedded' };
|
|
69
69
|
// Only indexed, searchable docs get vectors — the same set BM25 covers,
|
|
70
70
|
// read with their masked text (C3: masked text is the only text there is).
|
|
71
|
-
const docs = db.prepare(`SELECT d.id AS doc_id, e.text AS text
|
|
72
|
-
FROM search_docs d JOIN events e ON e.id = d.event_id
|
|
71
|
+
const docs = db.prepare(`SELECT d.id AS doc_id, e.text AS text
|
|
72
|
+
FROM search_docs d JOIN events e ON e.id = d.event_id
|
|
73
73
|
WHERE d.archive_id = ?`).all(archiveId);
|
|
74
74
|
const insVec = db.prepare(`INSERT INTO search_vectors (doc_id, chunk_index, vec, norm) VALUES (?, ?, ?, ?)`);
|
|
75
75
|
let chunks = 0;
|