@tznthou/ccrecall 0.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.
Files changed (87) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +290 -0
  3. package/README_ZH.md +288 -0
  4. package/dist/api/routes.d.ts +13 -0
  5. package/dist/api/routes.d.ts.map +1 -0
  6. package/dist/api/routes.js +428 -0
  7. package/dist/api/routes.js.map +1 -0
  8. package/dist/api/server.d.ts +7 -0
  9. package/dist/api/server.d.ts.map +1 -0
  10. package/dist/api/server.js +44 -0
  11. package/dist/api/server.js.map +1 -0
  12. package/dist/cli/daemon.d.ts +35 -0
  13. package/dist/cli/daemon.d.ts.map +1 -0
  14. package/dist/cli/daemon.js +163 -0
  15. package/dist/cli/daemon.js.map +1 -0
  16. package/dist/core/compression.d.ts +37 -0
  17. package/dist/core/compression.d.ts.map +1 -0
  18. package/dist/core/compression.js +129 -0
  19. package/dist/core/compression.js.map +1 -0
  20. package/dist/core/database.d.ts +219 -0
  21. package/dist/core/database.d.ts.map +1 -0
  22. package/dist/core/database.js +1513 -0
  23. package/dist/core/database.js.map +1 -0
  24. package/dist/core/index.d.ts +9 -0
  25. package/dist/core/index.d.ts.map +1 -0
  26. package/dist/core/index.js +8 -0
  27. package/dist/core/index.js.map +1 -0
  28. package/dist/core/indexer.d.ts +15 -0
  29. package/dist/core/indexer.d.ts.map +1 -0
  30. package/dist/core/indexer.js +224 -0
  31. package/dist/core/indexer.js.map +1 -0
  32. package/dist/core/lint.d.ts +17 -0
  33. package/dist/core/lint.d.ts.map +1 -0
  34. package/dist/core/lint.js +51 -0
  35. package/dist/core/lint.js.map +1 -0
  36. package/dist/core/log-safe.d.ts +6 -0
  37. package/dist/core/log-safe.d.ts.map +1 -0
  38. package/dist/core/log-safe.js +11 -0
  39. package/dist/core/log-safe.js.map +1 -0
  40. package/dist/core/maintenance-coordinator.d.ts +26 -0
  41. package/dist/core/maintenance-coordinator.d.ts.map +1 -0
  42. package/dist/core/maintenance-coordinator.js +67 -0
  43. package/dist/core/maintenance-coordinator.js.map +1 -0
  44. package/dist/core/memory-service.d.ts +31 -0
  45. package/dist/core/memory-service.d.ts.map +1 -0
  46. package/dist/core/memory-service.js +47 -0
  47. package/dist/core/memory-service.js.map +1 -0
  48. package/dist/core/parser.d.ts +17 -0
  49. package/dist/core/parser.d.ts.map +1 -0
  50. package/dist/core/parser.js +205 -0
  51. package/dist/core/parser.js.map +1 -0
  52. package/dist/core/scanner.d.ts +8 -0
  53. package/dist/core/scanner.d.ts.map +1 -0
  54. package/dist/core/scanner.js +123 -0
  55. package/dist/core/scanner.js.map +1 -0
  56. package/dist/core/summarizer.d.ts +12 -0
  57. package/dist/core/summarizer.d.ts.map +1 -0
  58. package/dist/core/summarizer.js +430 -0
  59. package/dist/core/summarizer.js.map +1 -0
  60. package/dist/core/topic-extractor.d.ts +11 -0
  61. package/dist/core/topic-extractor.d.ts.map +1 -0
  62. package/dist/core/topic-extractor.js +51 -0
  63. package/dist/core/topic-extractor.js.map +1 -0
  64. package/dist/core/types.d.ts +329 -0
  65. package/dist/core/types.d.ts.map +1 -0
  66. package/dist/core/types.js +12 -0
  67. package/dist/core/types.js.map +1 -0
  68. package/dist/core/watcher.d.ts +30 -0
  69. package/dist/core/watcher.d.ts.map +1 -0
  70. package/dist/core/watcher.js +146 -0
  71. package/dist/core/watcher.js.map +1 -0
  72. package/dist/index.d.ts +3 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +116 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/mcp/server.d.ts +3 -0
  77. package/dist/mcp/server.d.ts.map +1 -0
  78. package/dist/mcp/server.js +32 -0
  79. package/dist/mcp/server.js.map +1 -0
  80. package/dist/mcp/tools.d.ts +39 -0
  81. package/dist/mcp/tools.d.ts.map +1 -0
  82. package/dist/mcp/tools.js +215 -0
  83. package/dist/mcp/tools.js.map +1 -0
  84. package/hooks/README.md +78 -0
  85. package/hooks/session-end.mjs +92 -0
  86. package/hooks/session-start.mjs +112 -0
  87. package/package.json +54 -0
@@ -0,0 +1,163 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import { spawn } from 'node:child_process';
5
+ import { mkdir, writeFile, rm, realpath, lstat, readFile } from 'node:fs/promises';
6
+ import { fileURLToPath } from 'node:url';
7
+ /**
8
+ * macOS launchd installer for ccRecall.
9
+ *
10
+ * Produces a per-user LaunchAgent plist under ~/Library/LaunchAgents that
11
+ * keeps ccRecall running across reboots + auto-restarts on crash. Logs land
12
+ * in ~/Library/Logs/ccrecall/. Linux/Windows equivalents are Phase 5.
13
+ */
14
+ const LABEL = 'com.tznthou.ccrecall';
15
+ const DEFAULT_PORT = 7749;
16
+ /** Resolve absolute paths for plist generation. `scriptPath` defaults to the
17
+ * directory-relative dist/index.js so `node dist/index.js install-daemon`
18
+ * plants a plist that re-executes the same binary. */
19
+ export function resolveDaemonPaths(overrides = {}) {
20
+ const home = overrides.home ?? os.homedir();
21
+ const launchAgentsDir = path.join(home, 'Library', 'LaunchAgents');
22
+ const logsDir = path.join(home, 'Library', 'Logs', 'ccrecall');
23
+ const plistPath = path.join(launchAgentsDir, `${LABEL}.plist`);
24
+ const nodeBin = overrides.nodeBin ?? process.execPath;
25
+ const ccrecallJs = overrides.scriptPath ?? defaultScriptPath();
26
+ return { launchAgentsDir, logsDir, plistPath, nodeBin, ccrecallJs };
27
+ }
28
+ function defaultScriptPath() {
29
+ // When bundled as an npm bin, process.argv[1] points at the symlinked bin
30
+ // file. Resolve to the real dist/index.js so launchd spawns the actual file.
31
+ const argv1 = process.argv[1];
32
+ if (argv1 && argv1.endsWith('.js'))
33
+ return argv1;
34
+ // Fallback: compute relative to this module (e.g. during `pnpm dev`).
35
+ const here = fileURLToPath(import.meta.url);
36
+ return path.resolve(path.dirname(here), '..', 'index.js');
37
+ }
38
+ /** Escape text for safe embedding inside a <string> XML element. */
39
+ function escapeXml(s) {
40
+ return s
41
+ .replace(/&/g, '&amp;')
42
+ .replace(/</g, '&lt;')
43
+ .replace(/>/g, '&gt;')
44
+ .replace(/"/g, '&quot;');
45
+ }
46
+ export function generatePlist(paths, opts = {}) {
47
+ const port = opts.port ?? DEFAULT_PORT;
48
+ const outLog = path.join(paths.logsDir, 'ccrecall.out.log');
49
+ const errLog = path.join(paths.logsDir, 'ccrecall.err.log');
50
+ const envEntries = [
51
+ ['CCRECALL_PORT', String(port)],
52
+ ];
53
+ if (opts.dbPath)
54
+ envEntries.push(['CCRECALL_DB_PATH', opts.dbPath]);
55
+ const envXml = envEntries
56
+ .map(([k, v]) => ` <key>${escapeXml(k)}</key>\n <string>${escapeXml(v)}</string>`)
57
+ .join('\n');
58
+ return `<?xml version="1.0" encoding="UTF-8"?>
59
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
60
+ <plist version="1.0">
61
+ <dict>
62
+ <key>Label</key>
63
+ <string>${escapeXml(LABEL)}</string>
64
+ <key>ProgramArguments</key>
65
+ <array>
66
+ <string>${escapeXml(paths.nodeBin)}</string>
67
+ <string>${escapeXml(paths.ccrecallJs)}</string>
68
+ </array>
69
+ <key>RunAtLoad</key>
70
+ <true/>
71
+ <key>KeepAlive</key>
72
+ <true/>
73
+ <key>StandardOutPath</key>
74
+ <string>${escapeXml(outLog)}</string>
75
+ <key>StandardErrorPath</key>
76
+ <string>${escapeXml(errLog)}</string>
77
+ <key>EnvironmentVariables</key>
78
+ <dict>
79
+ ${envXml}
80
+ </dict>
81
+ </dict>
82
+ </plist>
83
+ `;
84
+ }
85
+ /** Refuse to touch a plist that isn't ours. Protects against clobbering an
86
+ * unrelated LaunchAgent when `$HOME` points somewhere unexpected (sudo, test
87
+ * harness, compromised env) and against following a symlink out of the user's
88
+ * LaunchAgents directory. "Managed" = regular file whose content contains
89
+ * our Label. Non-existent path is OK (first install). */
90
+ async function assertManagedPlist(plistPath) {
91
+ const stats = await lstat(plistPath).catch(() => null);
92
+ if (!stats)
93
+ return;
94
+ if (stats.isSymbolicLink()) {
95
+ throw new Error(`refuse to touch symlink at ${plistPath} — remove it manually if you're sure it's ccRecall's`);
96
+ }
97
+ if (!stats.isFile()) {
98
+ throw new Error(`refuse to touch non-regular file at ${plistPath}`);
99
+ }
100
+ const content = await readFile(plistPath, 'utf8').catch(() => '');
101
+ if (!content.includes(`<string>${LABEL}</string>`)) {
102
+ throw new Error(`refuse to overwrite unmanaged plist at ${plistPath} (Label does not match ${LABEL})`);
103
+ }
104
+ }
105
+ export async function installDaemon(opts = {}) {
106
+ if (process.platform !== 'darwin') {
107
+ throw new Error(`install-daemon currently supports macOS only (platform: ${process.platform}). See docs/launchd.md for manual setup.`);
108
+ }
109
+ const paths = resolveDaemonPaths(opts.overrides);
110
+ // dry-run must be side-effect free: skip realpath() (can block on broken
111
+ // symlinks or stale network mounts) and all filesystem writes. Generate the
112
+ // plist from paths as-given and print.
113
+ if (opts.dryRun) {
114
+ process.stdout.write(generatePlist(paths, { port: opts.port, dbPath: opts.dbPath }));
115
+ return;
116
+ }
117
+ // If ccrecallJs came from process.argv[1], resolve symlinks so the plist
118
+ // points at the real dist/index.js, not an npm bin symlink that may move.
119
+ try {
120
+ paths.ccrecallJs = await realpath(paths.ccrecallJs);
121
+ }
122
+ catch {
123
+ // If realpath fails (file doesn't exist yet?), keep the original path —
124
+ // the user will get a clear launchd error rather than silent install.
125
+ }
126
+ const plist = generatePlist(paths, { port: opts.port, dbPath: opts.dbPath });
127
+ await assertManagedPlist(paths.plistPath);
128
+ await mkdir(paths.launchAgentsDir, { recursive: true });
129
+ await mkdir(paths.logsDir, { recursive: true });
130
+ await writeFile(paths.plistPath, plist, 'utf8');
131
+ if (!opts.skipLaunchctl) {
132
+ await runLaunchctl(['unload', paths.plistPath]).catch(() => { });
133
+ await runLaunchctl(['load', '-w', paths.plistPath]);
134
+ }
135
+ console.log(`ccRecall LaunchAgent installed: ${paths.plistPath}`);
136
+ console.log(`Logs: ${paths.logsDir}/ccrecall.{out,err}.log`);
137
+ console.log('Verify with: launchctl list | grep ccrecall');
138
+ }
139
+ export async function uninstallDaemon(opts = {}) {
140
+ if (process.platform !== 'darwin') {
141
+ throw new Error(`uninstall-daemon currently supports macOS only (platform: ${process.platform}).`);
142
+ }
143
+ const paths = resolveDaemonPaths(opts.overrides);
144
+ await assertManagedPlist(paths.plistPath);
145
+ if (!opts.skipLaunchctl) {
146
+ await runLaunchctl(['unload', paths.plistPath]).catch(() => { });
147
+ }
148
+ await rm(paths.plistPath, { force: true });
149
+ console.log(`ccRecall LaunchAgent removed: ${paths.plistPath}`);
150
+ }
151
+ function runLaunchctl(args) {
152
+ return new Promise((resolve, reject) => {
153
+ const child = spawn('launchctl', args, { stdio: 'ignore' });
154
+ child.on('error', reject);
155
+ child.on('exit', (code) => {
156
+ if (code === 0)
157
+ resolve();
158
+ else
159
+ reject(new Error(`launchctl ${args.join(' ')} exited with code ${code}`));
160
+ });
161
+ });
162
+ }
163
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/cli/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { Database, CompressionCandidate } from './database.js';
2
+ import type { MemoryService } from './memory-service.js';
3
+ export type CompressionAction = {
4
+ kind: 'skip';
5
+ reason: string;
6
+ } | {
7
+ kind: 'compress';
8
+ toLevel: 1 | 2;
9
+ newContent: string;
10
+ } | {
11
+ kind: 'delete';
12
+ };
13
+ export interface CompressionStats {
14
+ scanned: number;
15
+ compressed: number;
16
+ deleted: number;
17
+ skipped: number;
18
+ errors: number;
19
+ }
20
+ export interface CompressionOptions {
21
+ batchSize?: number;
22
+ }
23
+ /** Truncate to a character budget with a trailing ellipsis marker. Trims
24
+ * trailing whitespace before appending so the cut point does not look ragged. */
25
+ export declare function truncate(text: string, maxChars: number): string;
26
+ export declare function planTransition(c: CompressionCandidate): CompressionAction;
27
+ export declare class CompressionPipeline {
28
+ private readonly db;
29
+ private readonly svc;
30
+ constructor(db: Database, svc: MemoryService);
31
+ /** Scan up to batchSize candidates (oldest-id first) and apply one transition
32
+ * per memory. Designed to be called repeatedly by MaintenanceCoordinator —
33
+ * advancing L0→L1→L2→delete across consecutive passes keeps any single tick
34
+ * bounded and avoids starving MCP queries under WAL. */
35
+ runOnce(opts?: CompressionOptions): CompressionStats;
36
+ }
37
+ //# sourceMappingURL=compression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compression.d.ts","sourceRoot":"","sources":["../../src/core/compression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAiCxD,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AAEtB,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;kFACkF;AAClF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG/D;AAuBD,wBAAgB,cAAc,CAAC,CAAC,EAAE,oBAAoB,GAAG,iBAAiB,CAiCzE;AAED,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,EAAE,EAAE,QAAQ,EACZ,GAAG,EAAE,aAAa;IAGrC;;;6DAGyD;IACzD,OAAO,CAAC,IAAI,GAAE,kBAAuB,GAAG,gBAAgB;CA2BzD"}
@@ -0,0 +1,129 @@
1
+ import { scrubErrorMessage } from './log-safe.js';
2
+ /**
3
+ * Phase 4d: mutable-memory compression pipeline.
4
+ *
5
+ * Stages a memory through three levels (L0 raw → L1 summary → L2 conclusion)
6
+ * based on age, access, and effective confidence gates. Session-backed memories
7
+ * sample `sessions.summary_text`/`intent_text` so L1/L2 content is derived from
8
+ * the original source rather than repeatedly truncating a truncated string.
9
+ * Manual memories and orphaned rows fall back to syntactic truncation.
10
+ *
11
+ * Auto-delete at L2 applies only to session-backed memories (`session_id IS NOT NULL`)
12
+ * — a user's manual memory is treated as an explicit intent and capped at L2.
13
+ */
14
+ // Gate thresholds — kept as module-scope constants so decay-curve tuning lives
15
+ // in one place and tests can reference the same values the pipeline uses.
16
+ const L1_AGE_DAYS = 7;
17
+ const L1_ACCESS_MAX = 2; // compress when access_count < this
18
+ const L1_EFFECTIVE_CONFIDENCE_MAX = 0.5;
19
+ const L2_AGE_DAYS = 30;
20
+ const L2_ACCESS_MAX = 4;
21
+ const DELETE_AGE_DAYS = 60;
22
+ // Syntactic truncation budgets (chars, not tokens — kept deliberately small so
23
+ // even multi-byte languages stay well under 1KB of storage per row).
24
+ const L1_MANUAL_TRUNCATE = 150;
25
+ const L2_MANUAL_TRUNCATE = 80;
26
+ const L2_SUMMARY_TRUNCATE = 100;
27
+ const DEFAULT_BATCH_SIZE = 50;
28
+ /** Truncate to a character budget with a trailing ellipsis marker. Trims
29
+ * trailing whitespace before appending so the cut point does not look ragged. */
30
+ export function truncate(text, maxChars) {
31
+ if (text.length <= maxChars)
32
+ return text;
33
+ return text.slice(0, maxChars).trimEnd() + '...';
34
+ }
35
+ function deriveL1Content(c) {
36
+ // Session-backed: prefer the indexer-written summary as the compressed form.
37
+ // When the session row is missing (deleted manually, or pre-index race) fall
38
+ // through to syntactic truncation — matching the manual-memory path.
39
+ if (c.sessionId) {
40
+ const summary = c.summaryText?.trim();
41
+ if (summary)
42
+ return summary;
43
+ }
44
+ return truncate(c.content, L1_MANUAL_TRUNCATE);
45
+ }
46
+ function deriveL2Content(c) {
47
+ if (c.sessionId) {
48
+ const intent = c.intentText?.trim();
49
+ if (intent)
50
+ return intent;
51
+ const summary = c.summaryText?.trim();
52
+ if (summary)
53
+ return truncate(summary, L2_SUMMARY_TRUNCATE);
54
+ }
55
+ return truncate(c.content, L2_MANUAL_TRUNCATE);
56
+ }
57
+ export function planTransition(c) {
58
+ const level = c.compressionLevel;
59
+ if (level === 0) {
60
+ if (c.ageDays >= L1_AGE_DAYS
61
+ && c.accessCount < L1_ACCESS_MAX
62
+ && c.effectiveConfidence < L1_EFFECTIVE_CONFIDENCE_MAX) {
63
+ return { kind: 'compress', toLevel: 1, newContent: deriveL1Content(c) };
64
+ }
65
+ return { kind: 'skip', reason: 'L0 gates not met' };
66
+ }
67
+ if (level === 1) {
68
+ if (c.ageDays >= L2_AGE_DAYS && c.accessCount < L2_ACCESS_MAX) {
69
+ return { kind: 'compress', toLevel: 2, newContent: deriveL2Content(c) };
70
+ }
71
+ return { kind: 'skip', reason: 'L1 gates not met' };
72
+ }
73
+ if (level === 2) {
74
+ if (c.ageDays >= DELETE_AGE_DAYS
75
+ && c.sessionId !== null
76
+ && c.sessionExists
77
+ && c.accessCount === 0) {
78
+ return { kind: 'delete' };
79
+ }
80
+ // Orphan (sessionId present but sessions row gone) is deliberately skipped —
81
+ // the source JSONL is already lost, so auto-delete would erase the last
82
+ // surviving copy. Lint surfaces orphans for the user to purge explicitly.
83
+ return { kind: 'skip', reason: 'L2 gates not met' };
84
+ }
85
+ return { kind: 'skip', reason: `unknown compression_level=${level}` };
86
+ }
87
+ export class CompressionPipeline {
88
+ db;
89
+ svc;
90
+ constructor(db, svc) {
91
+ this.db = db;
92
+ this.svc = svc;
93
+ }
94
+ /** Scan up to batchSize candidates (oldest-id first) and apply one transition
95
+ * per memory. Designed to be called repeatedly by MaintenanceCoordinator —
96
+ * advancing L0→L1→L2→delete across consecutive passes keeps any single tick
97
+ * bounded and avoids starving MCP queries under WAL. */
98
+ runOnce(opts = {}) {
99
+ const batchSize = opts.batchSize ?? DEFAULT_BATCH_SIZE;
100
+ const stats = {
101
+ scanned: 0, compressed: 0, deleted: 0, skipped: 0, errors: 0,
102
+ };
103
+ const candidates = this.db.getCompressionCandidates(batchSize);
104
+ stats.scanned = candidates.length;
105
+ for (const c of candidates) {
106
+ try {
107
+ const action = planTransition(c);
108
+ if (action.kind === 'compress') {
109
+ this.svc.updateContent(c.id, action.newContent, action.toLevel);
110
+ stats.compressed++;
111
+ }
112
+ else if (action.kind === 'delete') {
113
+ this.svc.delete(c.id);
114
+ stats.deleted++;
115
+ }
116
+ else {
117
+ stats.skipped++;
118
+ }
119
+ }
120
+ catch (err) {
121
+ // Swallow and count — one corrupt row must not abort the whole batch.
122
+ console.warn('[compression] error on memory', c.id, scrubErrorMessage(err));
123
+ stats.errors++;
124
+ }
125
+ }
126
+ return stats;
127
+ }
128
+ }
129
+ //# sourceMappingURL=compression.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compression.js","sourceRoot":"","sources":["../../src/core/compression.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,219 @@
1
+ import type { Project, SessionMeta, Message, MessageContext, SearchPage, SearchOptions, SessionSearchPage, SessionTokenStats, SessionFile, OutcomeStatus, FileHistoryEntry, SubagentSession, SessionFileInput, Memory, MemoryType, Topic, SessionCheckpoint } from './types.js';
2
+ /** 寫入 memories 時使用的參數型別 */
3
+ export interface MemoryInput {
4
+ sessionId: string | null;
5
+ messageId: string | null;
6
+ content: string;
7
+ type: MemoryType;
8
+ confidence?: number;
9
+ /** Phase 4b: denormalized project scope. Session-backed memories auto-derive
10
+ * this from sessions.project_id if omitted. Manual memories should set this
11
+ * to avoid cross-project leakage. */
12
+ projectId?: string | null;
13
+ }
14
+ /** 寫入 messages 時使用的參數型別 */
15
+ export interface MessageInput {
16
+ type: string;
17
+ uuid: string | null;
18
+ role: string | null;
19
+ contentText: string | null;
20
+ contentJson: string | null;
21
+ hasToolUse: boolean;
22
+ hasToolResult: boolean;
23
+ toolNames: string[];
24
+ timestamp: string | null;
25
+ sequence: number;
26
+ rawJson: string | null;
27
+ inputTokens: number | null;
28
+ outputTokens: number | null;
29
+ cacheReadTokens: number | null;
30
+ cacheCreationTokens: number | null;
31
+ model: string | null;
32
+ }
33
+ /** indexSession 的參數型別 */
34
+ export interface IndexSessionParams {
35
+ sessionId: string;
36
+ projectId: string;
37
+ projectDisplayName: string;
38
+ title: string | null;
39
+ messageCount: number;
40
+ filePath: string;
41
+ fileSize: number;
42
+ fileMtime: string;
43
+ startedAt: string | null;
44
+ endedAt: string | null;
45
+ summaryText?: string | null;
46
+ intentText?: string | null;
47
+ outcomeStatus?: OutcomeStatus;
48
+ outcomeSignals?: string | null;
49
+ durationSeconds?: number | null;
50
+ activeDurationSeconds?: number | null;
51
+ summaryVersion?: number | null;
52
+ tags?: string | null;
53
+ filesTouched?: string | null;
54
+ toolsUsed?: string | null;
55
+ sessionFiles?: SessionFileInput[];
56
+ messages: MessageInput[];
57
+ }
58
+ /** Phase 4d: normalised shape consumed by the compression pipeline. */
59
+ export interface CompressionCandidate {
60
+ id: number;
61
+ sessionId: string | null;
62
+ content: string;
63
+ compressionLevel: number;
64
+ accessCount: number;
65
+ ageDays: number;
66
+ effectiveConfidence: number;
67
+ summaryText: string | null;
68
+ intentText: string | null;
69
+ /** True when the session row that `sessionId` points at still exists.
70
+ * False for manual memories (sessionId is null) or orphaned rows. Used by
71
+ * the compression pipeline to block irreversible auto-delete of orphans. */
72
+ sessionExists: boolean;
73
+ }
74
+ export declare class Database {
75
+ private db;
76
+ /** Subagent 排除子查詢:用於所有面向使用者的 query,只顯示主 session */
77
+ private static readonly EXCLUDE_SUBAGENTS;
78
+ /** Phase 4b: SQL snippet for effective confidence with exponential decay and
79
+ * access-extended half-life. Assumes `m` is the memories alias.
80
+ * age_days = julianday(now) − julianday(COALESCE(last_accessed, created_at))
81
+ * half_life = 7 + 7 · min(access_count, 4) days
82
+ * effective = confidence · exp(−ln 2 · age_days / half_life)
83
+ * Use in ORDER BY; requires exp() user function registered in the constructor. */
84
+ private static readonly EFFECTIVE_CONFIDENCE;
85
+ constructor(dbPath: string);
86
+ close(): void;
87
+ /** ⚠️ 測試專用:接受任意 SQL,禁止接到 IPC handler */
88
+ rawAll<T>(sql: string): T[];
89
+ /** ⚠️ 測試專用:執行 DDL/DML,禁止接到 IPC handler */
90
+ rawExec(sql: string): void;
91
+ private initSchema;
92
+ /** 依序執行尚未套用的 migrations */
93
+ private runMigrations;
94
+ /** 取得目前 schema 版本 */
95
+ getSchemaVersion(): number;
96
+ upsertProject(id: string, displayName: string): void;
97
+ getMainSessionCount(): number;
98
+ updateProjectStats(projectId: string): void;
99
+ getProjects(): Project[];
100
+ getSessionMtime(sessionId: string): string | null;
101
+ getSessions(projectId: string): SessionMeta[];
102
+ getSessionById(sessionId: string): SessionMeta | null;
103
+ /** 將 DB 中不在 keepIds 集合的 session 標記為 archived(JSONL 已從磁碟消失),排除 subagent sessions */
104
+ archiveStaleSessionsExcept(keepIds: Set<string>): void;
105
+ /** 一次取得所有 session 的 file_mtime + archived 狀態(增量索引批次比對用) */
106
+ getAllSessionMtimes(): Map<string, {
107
+ mtime: string;
108
+ archived: boolean;
109
+ }>;
110
+ /** 寫入 subagent session 記錄(upsert:重複 id 更新 mtime/count) */
111
+ indexSubagentSession(params: {
112
+ id: string;
113
+ parentSessionId: string;
114
+ agentType: string | null;
115
+ filePath: string;
116
+ fileSize: number | null;
117
+ fileMtime: string | null;
118
+ messageCount: number;
119
+ startedAt: string | null;
120
+ endedAt: string | null;
121
+ }): void;
122
+ /** 取得指定 parent session 下的所有 subagent sessions */
123
+ getSubagentSessions(parentSessionId: string): SubagentSession[];
124
+ /** 刪除指定 parent session 的所有 subagent sessions */
125
+ deleteSubagentSessions(parentSessionId: string): void;
126
+ /** 刪除單一 subagent session(含對應的 sessions/messages 資料) */
127
+ deleteSubagentSession(subagentId: string): void;
128
+ /** 取得指定 parent session 的所有 subagent ID */
129
+ getSubagentSessionIds(parentSessionId: string): string[];
130
+ /** 在單一 transaction 中執行多個 DB 操作 */
131
+ runTransaction(fn: () => void): void;
132
+ /** 一次取得所有 subagent sessions 的 file_mtime(增量比對用) */
133
+ getAllSubagentMtimes(): Map<string, string>;
134
+ /** 查詢 DB 中已存在的 uuid(用於跨 session 去重 resumed session replay) */
135
+ getExistingUuids(uuids: string[], excludeSessionId: string): Set<string>;
136
+ indexSession(params: IndexSessionParams): void;
137
+ getMessages(sessionId: string): Message[];
138
+ /** 取得指定訊息及其前後 range 則訊息(搜尋結果上下文預覽) */
139
+ getMessageContext(messageId: number, range?: number): MessageContext;
140
+ static readonly SEARCH_PAGE_SIZE = 30;
141
+ private static readonly VALID_OUTCOMES;
142
+ private static parseOutcomeStatus;
143
+ /** FTS5 安全引號包裹:所有 token 無條件包引號,防止 operator injection */
144
+ private static fts5QuoteIfNeeded;
145
+ search(query: string, projectId?: string | null, offset?: number, limit?: number, options?: SearchOptions): SearchPage;
146
+ /** 搜尋 session 標題 / 標籤 / 檔案路徑 / 摘要 / 意圖 */
147
+ searchSessions(query: string, projectId?: string | null, offset?: number, limit?: number, options?: SearchOptions): SessionSearchPage;
148
+ getSessionTokenStats(sessionId: string): SessionTokenStats;
149
+ /** 反向查詢:某檔案出現在哪些 session(按時間倒序) */
150
+ getFileHistory(filePath: string): FileHistoryEntry[];
151
+ /** 正向查詢:某 session 操作了哪些檔案 */
152
+ getSessionFiles(sessionId: string): SessionFile[];
153
+ saveMemory(input: MemoryInput): number;
154
+ queryMemories(query: string, limit: number, projectId?: string | null): Memory[];
155
+ /** Phase 4b: Increment access_count + stamp last_accessed for each id.
156
+ * Caller is responsible for dedup (use MemoryService.touch for that). Runs
157
+ * inside a transaction so partial failure does not leave half-applied state. */
158
+ touchMemory(ids: number[]): void;
159
+ /** Phase 4b: Delete a memory by id. memories_ad trigger syncs memories_fts. */
160
+ deleteMemory(id: number): boolean;
161
+ /** Phase 4b: Update memory content and stamp compression metadata atomically.
162
+ * memories_au trigger syncs memories_fts. Used by the compression pipeline. */
163
+ updateMemoryContent(id: number, content: string, level: number, compressedAt: string): boolean;
164
+ getMemoriesBySessionId(sessionId: string): Memory[];
165
+ getMemoryCount(): number;
166
+ /** Phase 4d lint: memories whose session_id references a session row that no
167
+ * longer exists (user scrubbed ~/.claude/projects, or pre-index race). Manual
168
+ * memories (session_id IS NULL) are not orphan candidates by definition. */
169
+ getOrphanMemoryIds(limit?: number): Array<{
170
+ memoryId: number;
171
+ sessionId: string;
172
+ }>;
173
+ /** Phase 4d lint: memories that have fully decayed — effective confidence under
174
+ * threshold AND never accessed AND older than `ageDays`. Strict `>` on age so
175
+ * callers can use 90 as "more than 90 days" without fencepost ambiguity. */
176
+ getStaleMemoryIds(opts: {
177
+ effectiveConfidence: number;
178
+ ageDays: number;
179
+ }): Array<{
180
+ memoryId: number;
181
+ ageDays: number;
182
+ effectiveConfidence: number;
183
+ }>;
184
+ /** Phase 4d: fetch a batch of memories with everything the compression pipeline
185
+ * needs to plan a transition — current level / access / age / effective confidence
186
+ * plus the owning session's summary_text / intent_text for content rewrite.
187
+ *
188
+ * LEFT JOIN sessions so session-backed memories whose session row was deleted
189
+ * (manual scrub of ~/.claude) still surface; the pipeline detects the NULL
190
+ * summary and falls back to syntactic truncation, matching manual memories. */
191
+ getCompressionCandidates(limit: number): CompressionCandidate[];
192
+ saveSessionTopics(sessionId: string, projectId: string, topicKeys: string[]): void;
193
+ saveMemoryTopics(memoryId: number, projectId: string, topicKeys: string[]): void;
194
+ /** Full rebuild of knowledge_map for a project. project_id for memory_topics is derived
195
+ from sessions.project_id (not the denormalized column) so repo renames / reindex won't
196
+ leave stale cross-project associations. */
197
+ rebuildKnowledgeMap(projectId: string): void;
198
+ private static readonly TOPIC_ORDER_BY;
199
+ getKnowledgeMap(projectId: string, opts?: {
200
+ limit?: number;
201
+ sortBy?: 'mention' | 'recent' | 'stale';
202
+ }): Topic[];
203
+ getMemoriesByTopics(projectId: string, topicKeys: string[], limit: number): Memory[];
204
+ getTopicCount(projectId?: string): number;
205
+ getSessionTopicKeys(sessionId: string): string[];
206
+ getTopic(topicKey: string, projectId: string): Topic | null;
207
+ /** 共現 topics(與目標共享 session 或 memory),按共現次數排序。
208
+ project_id 透過 JOIN sessions 決定,避免 memory_topics 過時的 project_id 汙染。 */
209
+ getRelatedTopics(topicKey: string, projectId: string, limit: number): string[];
210
+ getKnowledgeMapCounts(projectId: string): {
211
+ totalTopics: number;
212
+ totalMemories: number;
213
+ totalSessions: number;
214
+ };
215
+ saveCheckpoint(sessionId: string, projectId: string, snapshotText: string): number;
216
+ getCheckpointById(id: number): SessionCheckpoint | null;
217
+ getCheckpointsBySessionId(sessionId: string): SessionCheckpoint[];
218
+ }
219
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/core/database.ts"],"names":[],"mappings":"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"}