@tznthou/ccrecall 0.4.8 → 0.5.1

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.
Files changed (49) hide show
  1. package/README.md +28 -52
  2. package/README_ZH.md +28 -53
  3. package/dist/api/routes.d.ts +0 -10
  4. package/dist/api/routes.d.ts.map +1 -1
  5. package/dist/api/routes.js +4 -354
  6. package/dist/api/routes.js.map +1 -1
  7. package/dist/api/server.js +3 -3
  8. package/dist/api/server.js.map +1 -1
  9. package/dist/core/database.d.ts +14 -49
  10. package/dist/core/database.d.ts.map +1 -1
  11. package/dist/core/database.js +104 -225
  12. package/dist/core/database.js.map +1 -1
  13. package/dist/core/indexer.d.ts.map +1 -1
  14. package/dist/core/indexer.js +0 -1
  15. package/dist/core/indexer.js.map +1 -1
  16. package/dist/core/maintenance-coordinator.d.ts +3 -9
  17. package/dist/core/maintenance-coordinator.d.ts.map +1 -1
  18. package/dist/core/maintenance-coordinator.js +4 -24
  19. package/dist/core/maintenance-coordinator.js.map +1 -1
  20. package/dist/core/summarizer.d.ts.map +1 -1
  21. package/dist/core/summarizer.js +0 -22
  22. package/dist/core/summarizer.js.map +1 -1
  23. package/dist/core/types.d.ts +0 -87
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js.map +1 -1
  26. package/dist/index.js +0 -16
  27. package/dist/index.js.map +1 -1
  28. package/hooks/README.md +2 -2
  29. package/package.json +2 -1
  30. package/scripts/backfill-memory-topics.ts +29 -0
  31. package/scripts/extraction-prompt.md +96 -0
  32. package/scripts/post-session-extract.sh +290 -0
  33. package/scripts/recall-hit-rate-report.ts +91 -0
  34. package/dist/cli/journal.d.ts +0 -11
  35. package/dist/cli/journal.d.ts.map +0 -1
  36. package/dist/cli/journal.js +0 -111
  37. package/dist/cli/journal.js.map +0 -1
  38. package/dist/core/lint.d.ts +0 -17
  39. package/dist/core/lint.d.ts.map +0 -1
  40. package/dist/core/lint.js +0 -51
  41. package/dist/core/lint.js.map +0 -1
  42. package/dist/core/outcome-extractor.d.ts +0 -6
  43. package/dist/core/outcome-extractor.d.ts.map +0 -1
  44. package/dist/core/outcome-extractor.js +0 -39
  45. package/dist/core/outcome-extractor.js.map +0 -1
  46. package/dist/core/outcome-scorer.d.ts +0 -8
  47. package/dist/core/outcome-scorer.d.ts.map +0 -1
  48. package/dist/core/outcome-scorer.js +0 -103
  49. package/dist/core/outcome-scorer.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"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"}
@@ -4,10 +4,21 @@ import { createHash } from 'node:crypto';
4
4
  import { copyFileSync, existsSync, mkdirSync } from 'node:fs';
5
5
  import path from 'node:path';
6
6
  import { scrubErrorMessage } from './log-safe.js';
7
+ /** 64-bit signed hash for message_uuids (v24 dual-hash schema). First 8 bytes
8
+ * of SHA-256 read as a signed BigInt — exactly the SQLite INTEGER range, so
9
+ * uuid_hash doubles as the rowid (zero autoindex). Collision odds at ~400k
10
+ * rows ≈ 4e-9; a collision merely makes one replayed message skip indexing.
11
+ *
12
+ * ⚠️ Reading these values back REQUIRES `.safeIntegers(true)` on the statement:
13
+ * better-sqlite3 otherwise returns a lossy JS number for |v| > 2^53 and any
14
+ * Map<bigint, …> reverse lookup silently misses (dedup would silently die). */
15
+ export function hash64(s) {
16
+ return createHash('sha256').update(s).digest().readBigInt64BE(0);
17
+ }
7
18
  /** sessions 資料表的完整欄位 SELECT 子句(getSessions / getSessionById 共用) */
8
19
  const SESSION_SELECT_COLUMNS = `id, project_id, title, message_count, started_at, ended_at, archived,
9
20
  summary_text, intent_text, outcome_status, duration_seconds, active_duration_seconds, summary_version,
10
- tags, files_touched, tools_used, total_input_tokens, total_output_tokens, harvest_text`;
21
+ tags, files_touched, tools_used, total_input_tokens, total_output_tokens`;
11
22
  function mapSessionRow(r) {
12
23
  return {
13
24
  id: r.id,
@@ -28,7 +39,6 @@ function mapSessionRow(r) {
28
39
  toolsUsed: r.tools_used,
29
40
  totalInputTokens: r.total_input_tokens,
30
41
  totalOutputTokens: r.total_output_tokens,
31
- harvestText: r.harvest_text,
32
42
  };
33
43
  }
34
44
  function mapMemoryRow(r) {
@@ -51,42 +61,6 @@ function mapTopicRow(r) {
51
61
  lastTouched: r.last_touched,
52
62
  };
53
63
  }
54
- function mapCheckpointRow(r) {
55
- return {
56
- id: r.id,
57
- sessionId: r.session_id,
58
- projectId: r.project_id,
59
- snapshotText: r.snapshot_text,
60
- createdAt: r.created_at,
61
- };
62
- }
63
- function mapJournalRow(r) {
64
- return {
65
- id: r.id,
66
- sessionId: r.session_id,
67
- messageId: r.message_id,
68
- content: r.content,
69
- contentHash: r.content_hash,
70
- score: r.score,
71
- reasonsJson: r.reasons_json,
72
- status: r.status,
73
- expiresAt: r.expires_at,
74
- promotedMemoryId: r.promoted_memory_id,
75
- projectId: r.project_id,
76
- createdAt: r.created_at,
77
- };
78
- }
79
- function mapJournalPreviewRow(r) {
80
- return {
81
- id: r.id,
82
- sessionId: r.session_id,
83
- score: r.score,
84
- reasonsJson: r.reasons_json,
85
- contentPreview: r.content_preview,
86
- projectId: r.project_id,
87
- createdAt: r.created_at,
88
- };
89
- }
90
64
  /** 重建 message_content / message_archive 表,修正 FK 指向 messages */
91
65
  function rebuildSideTables(db) {
92
66
  db.exec(`
@@ -655,6 +629,48 @@ const migrations = [
655
629
  `);
656
630
  },
657
631
  },
632
+ {
633
+ version: 24,
634
+ description: 'knife-field (v0.5.0): drop session_journal/session_checkpoints/sessions.harvest_text; rebuild message_uuids as dual 64-bit hash (73.6MB → ~14MB)',
635
+ up: (db) => {
636
+ // Dead-pipeline tables — the journal DLQ recorded 0 promotions in its
637
+ // entire history (2026-06-10 audit) and /session/checkpoint had no caller.
638
+ db.exec(`
639
+ DROP TABLE IF EXISTS session_journal;
640
+ DROP TABLE IF EXISTS session_checkpoints;
641
+ `);
642
+ const sessionCols = db.prepare('PRAGMA table_info(sessions)').all();
643
+ if (sessionCols.some(c => c.name === 'harvest_text')) {
644
+ db.exec('ALTER TABLE sessions DROP COLUMN harvest_text;');
645
+ }
646
+ // message_uuids: TEXT uuid PK (36 chars ×3 b-trees) → dual 64-bit hash.
647
+ // uuid_hash INTEGER PRIMARY KEY is the rowid alias — zero autoindex.
648
+ // Skip when already dual-hash (idempotency; also covers a repaired DB
649
+ // where initSchema recreated the table in its final shape).
650
+ const uuidCols = db.prepare('PRAGMA table_info(message_uuids)').all();
651
+ if (uuidCols.some(c => c.name === 'uuid_hash'))
652
+ return;
653
+ db.exec(`
654
+ CREATE TABLE message_uuids_new (
655
+ uuid_hash INTEGER PRIMARY KEY,
656
+ session_hash INTEGER NOT NULL
657
+ );
658
+ `);
659
+ // JS-side rehash: hash64 lives here, not in SQLite. ~400k rows over the
660
+ // sync API completes in seconds. INSERT OR IGNORE keeps first-writer-wins
661
+ // on the astronomically unlikely 64-bit collision.
662
+ const insert = db.prepare('INSERT OR IGNORE INTO message_uuids_new (uuid_hash, session_hash) VALUES (?, ?)');
663
+ const rows = db.prepare('SELECT uuid, session_id FROM message_uuids').all();
664
+ for (const r of rows) {
665
+ insert.run(hash64(r.uuid), hash64(r.session_id));
666
+ }
667
+ db.exec(`
668
+ DROP TABLE message_uuids;
669
+ ALTER TABLE message_uuids_new RENAME TO message_uuids;
670
+ CREATE INDEX idx_message_uuids_session ON message_uuids(session_hash);
671
+ `);
672
+ },
673
+ },
658
674
  ];
659
675
  export class Database {
660
676
  db;
@@ -872,14 +888,24 @@ export class Database {
872
888
  }
873
889
  }
874
890
  else {
875
- // v20+ baseline — message_uuids carries the UUID dedup set.
891
+ // v20+ baseline — message_uuids carries the UUID dedup set. DDL is the
892
+ // v24 dual-hash shape: on a v20–v23 DB the table already exists so this
893
+ // is a no-op (v24 migration handles the rebuild); it only materialises
894
+ // when repairing a DB whose table went missing, and then the v24
895
+ // migration's shape check skips the rebuild.
876
896
  this.db.exec(`
877
897
  CREATE TABLE IF NOT EXISTS message_uuids (
878
- uuid TEXT PRIMARY KEY,
879
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE
898
+ uuid_hash INTEGER PRIMARY KEY,
899
+ session_hash INTEGER NOT NULL
880
900
  );
881
- CREATE INDEX IF NOT EXISTS idx_message_uuids_session ON message_uuids(session_id);
882
901
  `);
902
+ // Index only when the table is dual-hash: a v20–v23 table awaiting the
903
+ // v24 rebuild has no session_hash column, and indexing it here would
904
+ // throw. The rebuild recreates the index itself.
905
+ const uuidCols = this.db.prepare('PRAGMA table_info(message_uuids)').all();
906
+ if (uuidCols.some(c => c.name === 'session_hash')) {
907
+ this.db.exec('CREATE INDEX IF NOT EXISTS idx_message_uuids_session ON message_uuids(session_hash);');
908
+ }
883
909
  }
884
910
  }
885
911
  /** 依序執行尚未套用的 migrations */
@@ -956,10 +982,14 @@ export class Database {
956
982
  return row?.file_mtime ?? null;
957
983
  }
958
984
  getSessions(projectId) {
985
+ // instr(id,'/') mirrors getLastSession's registry-timing guard (v0.4.8):
986
+ // a subagent row can momentarily lack its subagent_sessions registration,
987
+ // and composite "<parent>/agent-…" ids are never main sessions.
959
988
  const rows = this.db.prepare(`SELECT ${SESSION_SELECT_COLUMNS}
960
989
  FROM sessions
961
990
  WHERE project_id = ?
962
991
  AND id ${Database.EXCLUDE_SUBAGENTS}
992
+ AND instr(id, '/') = 0
963
993
  ORDER BY started_at DESC`).all(projectId);
964
994
  return rows.map(mapSessionRow);
965
995
  }
@@ -1046,11 +1076,12 @@ export class Database {
1046
1076
  deleteSubagentSessions(parentSessionId) {
1047
1077
  this.db.prepare('DELETE FROM subagent_sessions WHERE parent_session_id = ?').run(parentSessionId);
1048
1078
  }
1049
- /** 刪除單一 subagent session(session_files / message_uuids 會經 FK CASCADE 清除) */
1079
+ /** 刪除單一 subagent session。v24: message_uuids 無 FK CASCADE,手動清除。 */
1050
1080
  deleteSubagentSession(subagentId) {
1051
1081
  const doDelete = this.db.transaction(() => {
1052
1082
  this.db.prepare('DELETE FROM session_files WHERE session_id = ?').run(subagentId);
1053
1083
  this.db.prepare('DELETE FROM sessions WHERE id = ?').run(subagentId);
1084
+ this.db.prepare('DELETE FROM message_uuids WHERE session_hash = ?').run(hash64(subagentId));
1054
1085
  this.db.prepare('DELETE FROM subagent_sessions WHERE id = ?').run(subagentId);
1055
1086
  });
1056
1087
  doDelete();
@@ -1076,15 +1107,27 @@ export class Database {
1076
1107
  return map;
1077
1108
  }
1078
1109
  // ── UUID dedup helper ──
1079
- /** 查詢 DB 中已存在的 uuid(用於跨 session 去重 resumed session replay) */
1110
+ /** 查詢 DB 中已存在的 uuid(用於跨 session 去重 resumed session replay)。
1111
+ * v24: message_uuids 只存 64-bit hash,查詢以 hash 比對、以 Map 反查還原
1112
+ * 原 uuid — 簽名與回傳不變,indexer 端零改動。 */
1080
1113
  getExistingUuids(uuids, excludeSessionId) {
1081
1114
  const result = new Set();
1115
+ const excludeHash = hash64(excludeSessionId);
1082
1116
  for (let i = 0; i < uuids.length; i += 500) {
1083
1117
  const chunk = uuids.slice(i, i + 500);
1084
- const placeholders = chunk.map(() => '?').join(',');
1085
- const rows = this.db.prepare(`SELECT uuid FROM message_uuids WHERE session_id != ? AND uuid IN (${placeholders})`).all(excludeSessionId, ...chunk);
1086
- for (const r of rows)
1087
- result.add(r.uuid);
1118
+ const byHash = new Map();
1119
+ for (const u of chunk)
1120
+ byHash.set(hash64(u), u);
1121
+ const hashes = [...byHash.keys()];
1122
+ const placeholders = hashes.map(() => '?').join(',');
1123
+ // safeIntegers is load-bearing: without it uuid_hash values beyond 2^53
1124
+ // come back as lossy numbers and the Map lookup misses every row.
1125
+ const rows = this.db.prepare(`SELECT uuid_hash FROM message_uuids WHERE session_hash != ? AND uuid_hash IN (${placeholders})`).safeIntegers(true).all(excludeHash, ...hashes);
1126
+ for (const r of rows) {
1127
+ const uuid = byHash.get(r.uuid_hash);
1128
+ if (uuid !== undefined)
1129
+ result.add(uuid);
1130
+ }
1088
1131
  }
1089
1132
  return result;
1090
1133
  }
@@ -1098,6 +1141,10 @@ export class Database {
1098
1141
  }
1099
1142
  this.db.prepare('DELETE FROM session_files WHERE session_id = ?').run(params.sessionId);
1100
1143
  this.db.prepare('DELETE FROM sessions WHERE id = ?').run(params.sessionId);
1144
+ // v24: message_uuids no longer has an FK to sessions (hash columns can't
1145
+ // reference TEXT ids) — clear this session's registrations manually so a
1146
+ // reindex re-registers from scratch instead of stacking stale hashes.
1147
+ this.db.prepare('DELETE FROM message_uuids WHERE session_hash = ?').run(hash64(params.sessionId));
1101
1148
  this.upsertProject(params.projectId, params.projectDisplayName);
1102
1149
  // 計算 token 彙總
1103
1150
  let totalInput = 0;
@@ -1111,9 +1158,9 @@ export class Database {
1111
1158
  const insertResult = this.db.prepare(`
1112
1159
  INSERT INTO sessions (id, project_id, title, message_count, file_path, file_size, file_mtime, started_at, ended_at,
1113
1160
  summary_text, intent_text, outcome_status, outcome_signals, duration_seconds, active_duration_seconds, summary_version,
1114
- tags, files_touched, tools_used, total_input_tokens, total_output_tokens, harvest_text)
1115
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1116
- `).run(params.sessionId, params.projectId, params.title, params.messageCount, params.filePath, params.fileSize, params.fileMtime, params.startedAt, params.endedAt, params.summaryText ?? null, params.intentText ?? null, params.outcomeStatus ?? null, params.outcomeSignals ?? null, params.durationSeconds ?? null, params.activeDurationSeconds ?? null, params.summaryVersion ?? null, params.tags ?? null, params.filesTouched ?? null, params.toolsUsed ?? null, totalInput || null, totalOutput || null, params.harvestText ?? null);
1161
+ tags, files_touched, tools_used, total_input_tokens, total_output_tokens)
1162
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1163
+ `).run(params.sessionId, params.projectId, params.title, params.messageCount, params.filePath, params.fileSize, params.fileMtime, params.startedAt, params.endedAt, params.summaryText ?? null, params.intentText ?? null, params.outcomeStatus ?? null, params.outcomeSignals ?? null, params.durationSeconds ?? null, params.activeDurationSeconds ?? null, params.summaryVersion ?? null, params.tags ?? null, params.filesTouched ?? null, params.toolsUsed ?? null, totalInput || null, totalOutput || null);
1117
1164
  // 新增 sessions_fts 條目(用 INSERT 回傳的 rowid 避免多餘查詢)
1118
1165
  this.db.prepare('INSERT INTO sessions_fts(rowid, title, tags, files_touched, summary_text, intent_text) VALUES (?, ?, ?, ?, ?, ?)').run(insertResult.lastInsertRowid, params.title ?? '', params.tags ?? '', params.filesTouched ?? '', params.summaryText ?? '', params.intentText ?? '');
1119
1166
  // 寫入 session_files
@@ -1126,12 +1173,14 @@ export class Database {
1126
1173
  insertFile.run(params.sessionId, f.filePath, f.operation, f.count, f.firstSeenSeq, f.lastSeenSeq);
1127
1174
  }
1128
1175
  }
1129
- // message_uuids: 寫入 uuid 登記表,供跨 session replay dedup 查詢。
1130
- // 舊 session row 已由上面的 DELETE FROM sessions 透過 FK CASCADE 自動清除。
1131
- const insertUuid = this.db.prepare('INSERT OR IGNORE INTO message_uuids (uuid, session_id) VALUES (?, ?)');
1176
+ // message_uuids: 寫入 uuid 登記表(雙 hash),供跨 session replay dedup 查詢。
1177
+ // 舊登記已在本 transaction 開頭手動 DELETE。INSERT OR IGNORE 保持「先寫者
1178
+ // 擁有 uuid」語義(uuid_hash 為 PK;他 session 已登記或本 session 重複皆略過)。
1179
+ const insertUuid = this.db.prepare('INSERT OR IGNORE INTO message_uuids (uuid_hash, session_hash) VALUES (?, ?)');
1180
+ const sessionHash = hash64(params.sessionId);
1132
1181
  for (const m of params.messages) {
1133
1182
  if (m.uuid != null) {
1134
- insertUuid.run(m.uuid, params.sessionId);
1183
+ insertUuid.run(hash64(m.uuid), sessionHash);
1135
1184
  }
1136
1185
  }
1137
1186
  });
@@ -1395,101 +1444,6 @@ export class Database {
1395
1444
  `).run(input.sessionId, input.messageId, input.content, input.type, input.confidence ?? 0.8, projectId, key);
1396
1445
  return Number(info.lastInsertRowid);
1397
1446
  }
1398
- // ─────────────────────────────────────────────────────────────
1399
- // session_journal DAO (issue #21 P1)
1400
- // 低信任 harvest 候選的寫入入口; manual recall_save 仍走 saveMemory()。
1401
- //
1402
- // Trust boundary: queryMemories / getMemoriesByTopics 故意只查 memories table,
1403
- // 不 union session_journal — unpromoted journal entries 是 low-trust,不該污染
1404
- // recall 結果。promote 路徑 (C4) 把 journal entry 搬到 memories 後才會被 recall
1405
- // 看到。若未來修改 query 邏輯需動到 journal,務必保持這條 invariant。
1406
- // ─────────────────────────────────────────────────────────────
1407
- /** 寫入 journal 條目。content_hash 內部以 SHA-256 計算; 同 hash 第二次 INSERT
1408
- * 會被 UNIQUE INDEX 擋下並回傳 0 (idempotency)。
1409
- *
1410
- * 注意: better-sqlite3 的 INSERT OR IGNORE 在 IGNORE'd 時, lastInsertRowid
1411
- * 不會歸零 (保留前次成功 insert 的 rowid)。所以用 info.changes 判 dedup。 */
1412
- saveJournalEntry(input) {
1413
- const hash = createHash('sha256').update(input.content).digest('hex');
1414
- const info = this.db.prepare(`
1415
- INSERT OR IGNORE INTO session_journal
1416
- (session_id, message_id, content, content_hash, score, reasons_json, project_id)
1417
- VALUES (?, ?, ?, ?, ?, ?, ?)
1418
- `).run(input.sessionId ?? null, input.messageId ?? null, input.content, hash, input.score, input.reasonsJson ?? null, input.projectId ?? null);
1419
- return info.changes === 0 ? 0 : Number(info.lastInsertRowid);
1420
- }
1421
- /** 給 /health endpoint 用; promotion path manual-only,user 看 pending 數決定是否 invoke。 */
1422
- getJournalPendingCount() {
1423
- const row = this.db.prepare("SELECT COUNT(*) AS c FROM session_journal WHERE status = 'pending'").get();
1424
- return row.c;
1425
- }
1426
- getJournalEntry(id) {
1427
- const row = this.db.prepare(`
1428
- SELECT id, session_id, message_id, content, content_hash, score,
1429
- reasons_json, status, expires_at, promoted_memory_id,
1430
- project_id, created_at
1431
- FROM session_journal WHERE id = ?
1432
- `).get(id);
1433
- return row ? mapJournalRow(row) : null;
1434
- }
1435
- /** 升級 journal entry 為 promoted; caller 已透過 saveMemory 寫入 memories table
1436
- * 並拿到 memoryId,本方法只更新 journal 的 status + 回填 promoted_memory_id。
1437
- * 回 false 表示 entry 不存在或已不在 'pending' 狀態 (idempotent)。 */
1438
- promoteJournalEntry(id, memoryId) {
1439
- const info = this.db.prepare(`
1440
- UPDATE session_journal
1441
- SET status = 'promoted', promoted_memory_id = ?
1442
- WHERE id = ? AND status = 'pending'
1443
- `).run(memoryId, id);
1444
- return info.changes > 0;
1445
- }
1446
- /** List pending journal entries (newest first) with content preview for
1447
- * surfacing on the manual promotion path. content 截 200 chars 避免 response
1448
- * blob 過大; caller 想看完整內容可走 promote 看 memories 或直接 sqlite3。 */
1449
- getPendingJournalEntries(limit) {
1450
- const cappedLimit = Math.min(Math.max(limit, 1), 100);
1451
- const rows = this.db.prepare(`
1452
- SELECT id, session_id, score, reasons_json,
1453
- substr(content, 1, 200) AS content_preview,
1454
- project_id, created_at
1455
- FROM session_journal
1456
- WHERE status = 'pending'
1457
- ORDER BY created_at DESC, id DESC
1458
- LIMIT ?
1459
- `).all(cappedLimit);
1460
- return rows.map(mapJournalPreviewRow);
1461
- }
1462
- /** 標記 entry 為 rejected, expires_at 設為 now+7d (decay sweep 才真的刪除)。
1463
- * caller 傳 expiresAt ISO string; 回 false 表示 entry 不存在或非 pending。 */
1464
- rejectJournalEntry(id, expiresAt) {
1465
- const info = this.db.prepare(`
1466
- UPDATE session_journal
1467
- SET status = 'rejected', expires_at = ?
1468
- WHERE id = ? AND status = 'pending'
1469
- `).run(expiresAt, id);
1470
- return info.changes > 0;
1471
- }
1472
- /** Decay sweep (P1 step 7):
1473
- * - rejected entries past expires_at → DELETE
1474
- * - pending entries older than 30 days → DELETE
1475
- * promoted entries 留著作 audit trail (有 promoted_memory_id 指 memories.id)。
1476
- * manual memories 在 memories table,本方法完全不碰。 */
1477
- sweepJournal(now = new Date()) {
1478
- const nowIso = now.toISOString();
1479
- const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString();
1480
- const rejectedInfo = this.db.prepare(`
1481
- DELETE FROM session_journal
1482
- WHERE status = 'rejected' AND expires_at IS NOT NULL AND expires_at < ?
1483
- `).run(nowIso);
1484
- const pendingInfo = this.db.prepare(`
1485
- DELETE FROM session_journal
1486
- WHERE status = 'pending' AND created_at < ?
1487
- `).run(thirtyDaysAgo);
1488
- return {
1489
- rejectedDeleted: rejectedInfo.changes,
1490
- pendingDeleted: pendingInfo.changes,
1491
- };
1492
- }
1493
1447
  queryMemories(query, limit, projectId) {
1494
1448
  const rawQuery = query;
1495
1449
  const q = Database.fts5QuoteIfNeeded(query);
@@ -1910,23 +1864,6 @@ export class Database {
1910
1864
  });
1911
1865
  run();
1912
1866
  }
1913
- static TOPIC_ORDER_BY = {
1914
- mention: 'mention_count DESC, last_touched DESC',
1915
- recent: 'last_touched DESC',
1916
- stale: 'last_touched ASC',
1917
- };
1918
- getKnowledgeMap(projectId, opts = {}) {
1919
- const { limit = 50, sortBy = 'mention' } = opts;
1920
- const cappedLimit = Math.min(limit, 500);
1921
- const rows = this.db.prepare(`
1922
- SELECT topic_key, project_id, mention_count, last_touched
1923
- FROM knowledge_map
1924
- WHERE project_id = ?
1925
- ORDER BY ${Database.TOPIC_ORDER_BY[sortBy]}
1926
- LIMIT ?
1927
- `).all(projectId, cappedLimit);
1928
- return rows.map(mapTopicRow);
1929
- }
1930
1867
  getMemoriesByTopics(projectId, topicKeys, limit) {
1931
1868
  if (topicKeys.length === 0)
1932
1869
  return [];
@@ -1963,63 +1900,5 @@ export class Database {
1963
1900
  `).get(topicKey, projectId);
1964
1901
  return row ? mapTopicRow(row) : null;
1965
1902
  }
1966
- /** 共現 topics(與目標共享 session 或 memory),按共現次數排序。
1967
- project_id 透過 JOIN sessions 決定,避免 memory_topics 過時的 project_id 汙染。 */
1968
- getRelatedTopics(topicKey, projectId, limit) {
1969
- const cappedLimit = Math.min(limit, 50);
1970
- const rows = this.db.prepare(`
1971
- SELECT k, COUNT(*) AS c FROM (
1972
- SELECT st2.topic_key AS k
1973
- FROM session_topics st1
1974
- JOIN session_topics st2 ON st1.session_id = st2.session_id AND st2.topic_key != st1.topic_key
1975
- JOIN sessions s ON s.id = st1.session_id
1976
- WHERE st1.topic_key = ? AND s.project_id = ? AND s.id ${Database.EXCLUDE_SUBAGENTS}
1977
- UNION ALL
1978
- SELECT mt2.topic_key AS k
1979
- FROM memory_topics mt1
1980
- JOIN memory_topics mt2 ON mt1.memory_id = mt2.memory_id AND mt2.topic_key != mt1.topic_key
1981
- JOIN memories m ON m.id = mt1.memory_id
1982
- JOIN sessions s ON s.id = m.session_id
1983
- WHERE mt1.topic_key = ? AND s.project_id = ? AND s.id ${Database.EXCLUDE_SUBAGENTS}
1984
- )
1985
- GROUP BY k ORDER BY c DESC, k ASC LIMIT ?
1986
- `).all(topicKey, projectId, topicKey, projectId, cappedLimit);
1987
- return rows.map(r => r.k);
1988
- }
1989
- getKnowledgeMapCounts(projectId) {
1990
- const topics = this.db.prepare('SELECT COUNT(*) AS c FROM knowledge_map WHERE project_id = ?').get(projectId);
1991
- const mems = this.db.prepare(`
1992
- SELECT COUNT(*) AS c FROM memories m
1993
- JOIN sessions s ON s.id = m.session_id
1994
- WHERE s.project_id = ? AND s.id ${Database.EXCLUDE_SUBAGENTS}
1995
- `).get(projectId);
1996
- const sess = this.db.prepare(`
1997
- SELECT COUNT(*) AS c FROM sessions WHERE project_id = ? AND id ${Database.EXCLUDE_SUBAGENTS}
1998
- `).get(projectId);
1999
- return { totalTopics: topics.c, totalMemories: mems.c, totalSessions: sess.c };
2000
- }
2001
- // ── Session Checkpoints (Phase 3d) ──
2002
- saveCheckpoint(sessionId, projectId, snapshotText) {
2003
- const info = this.db.prepare(`
2004
- INSERT INTO session_checkpoints (session_id, project_id, snapshot_text)
2005
- VALUES (?, ?, ?)
2006
- `).run(sessionId, projectId, snapshotText);
2007
- return Number(info.lastInsertRowid);
2008
- }
2009
- getCheckpointById(id) {
2010
- const row = this.db.prepare(`
2011
- SELECT id, session_id, project_id, snapshot_text, created_at
2012
- FROM session_checkpoints WHERE id = ?
2013
- `).get(id);
2014
- return row ? mapCheckpointRow(row) : null;
2015
- }
2016
- getCheckpointsBySessionId(sessionId) {
2017
- const rows = this.db.prepare(`
2018
- SELECT id, session_id, project_id, snapshot_text, created_at
2019
- FROM session_checkpoints WHERE session_id = ?
2020
- ORDER BY created_at DESC, id DESC
2021
- `).all(sessionId);
2022
- return rows.map(mapCheckpointRow);
2023
- }
2024
1903
  }
2025
1904
  //# sourceMappingURL=database.js.map