context-vault 3.13.0 → 3.16.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 (94) hide show
  1. package/bin/cli.js +263 -414
  2. package/dist/error-log.d.ts +2 -0
  3. package/dist/error-log.d.ts.map +1 -1
  4. package/dist/error-log.js +31 -1
  5. package/dist/error-log.js.map +1 -1
  6. package/dist/register-tools.d.ts.map +1 -1
  7. package/dist/register-tools.js +4 -0
  8. package/dist/register-tools.js.map +1 -1
  9. package/dist/server.js +23 -426
  10. package/dist/server.js.map +1 -1
  11. package/dist/status.d.ts.map +1 -1
  12. package/dist/status.js +17 -0
  13. package/dist/status.js.map +1 -1
  14. package/dist/tools/context-status.d.ts.map +1 -1
  15. package/dist/tools/context-status.js +26 -1
  16. package/dist/tools/context-status.js.map +1 -1
  17. package/dist/tools/delete-context.d.ts +1 -1
  18. package/dist/tools/delete-context.d.ts.map +1 -1
  19. package/dist/tools/delete-context.js +15 -2
  20. package/dist/tools/delete-context.js.map +1 -1
  21. package/dist/tools/get-context.d.ts.map +1 -1
  22. package/dist/tools/get-context.js +3 -2
  23. package/dist/tools/get-context.js.map +1 -1
  24. package/dist/tools/list-context.d.ts +7 -15
  25. package/dist/tools/list-context.d.ts.map +1 -1
  26. package/dist/tools/list-context.js +570 -111
  27. package/dist/tools/list-context.js.map +1 -1
  28. package/dist/tools/publish-to-team.js +1 -1
  29. package/dist/tools/publish-to-team.js.map +1 -1
  30. package/dist/tools/save-context.js +2 -2
  31. package/dist/tools/save-context.js.map +1 -1
  32. package/dist/tools/session-start.d.ts +20 -7
  33. package/dist/tools/session-start.d.ts.map +1 -1
  34. package/dist/tools/session-start.js +406 -439
  35. package/dist/tools/session-start.js.map +1 -1
  36. package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -1
  37. package/node_modules/@context-vault/core/dist/capture.js +4 -0
  38. package/node_modules/@context-vault/core/dist/capture.js.map +1 -1
  39. package/node_modules/@context-vault/core/dist/categories.d.ts.map +1 -1
  40. package/node_modules/@context-vault/core/dist/categories.js +8 -0
  41. package/node_modules/@context-vault/core/dist/categories.js.map +1 -1
  42. package/node_modules/@context-vault/core/dist/compact.d.ts +38 -0
  43. package/node_modules/@context-vault/core/dist/compact.d.ts.map +1 -0
  44. package/node_modules/@context-vault/core/dist/compact.js +127 -0
  45. package/node_modules/@context-vault/core/dist/compact.js.map +1 -0
  46. package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -1
  47. package/node_modules/@context-vault/core/dist/config.js +12 -0
  48. package/node_modules/@context-vault/core/dist/config.js.map +1 -1
  49. package/node_modules/@context-vault/core/dist/db.d.ts +1 -1
  50. package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -1
  51. package/node_modules/@context-vault/core/dist/db.js +40 -4
  52. package/node_modules/@context-vault/core/dist/db.js.map +1 -1
  53. package/node_modules/@context-vault/core/dist/main.d.ts +6 -2
  54. package/node_modules/@context-vault/core/dist/main.d.ts.map +1 -1
  55. package/node_modules/@context-vault/core/dist/main.js +5 -1
  56. package/node_modules/@context-vault/core/dist/main.js.map +1 -1
  57. package/node_modules/@context-vault/core/dist/search.d.ts +13 -1
  58. package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -1
  59. package/node_modules/@context-vault/core/dist/search.js +50 -5
  60. package/node_modules/@context-vault/core/dist/search.js.map +1 -1
  61. package/node_modules/@context-vault/core/dist/tier-analysis.d.ts +36 -0
  62. package/node_modules/@context-vault/core/dist/tier-analysis.d.ts.map +1 -0
  63. package/node_modules/@context-vault/core/dist/tier-analysis.js +227 -0
  64. package/node_modules/@context-vault/core/dist/tier-analysis.js.map +1 -0
  65. package/node_modules/@context-vault/core/dist/types.d.ts +12 -0
  66. package/node_modules/@context-vault/core/dist/types.d.ts.map +1 -1
  67. package/node_modules/@context-vault/core/dist/watch.d.ts +21 -0
  68. package/node_modules/@context-vault/core/dist/watch.d.ts.map +1 -0
  69. package/node_modules/@context-vault/core/dist/watch.js +230 -0
  70. package/node_modules/@context-vault/core/dist/watch.js.map +1 -0
  71. package/node_modules/@context-vault/core/package.json +13 -1
  72. package/node_modules/@context-vault/core/src/capture.ts +4 -0
  73. package/node_modules/@context-vault/core/src/categories.ts +8 -0
  74. package/node_modules/@context-vault/core/src/compact.ts +183 -0
  75. package/node_modules/@context-vault/core/src/config.ts +8 -0
  76. package/node_modules/@context-vault/core/src/db.ts +40 -4
  77. package/node_modules/@context-vault/core/src/main.ts +10 -0
  78. package/node_modules/@context-vault/core/src/search.ts +55 -4
  79. package/node_modules/@context-vault/core/src/tier-analysis.ts +299 -0
  80. package/node_modules/@context-vault/core/src/types.ts +10 -0
  81. package/node_modules/@context-vault/core/src/watch.ts +269 -0
  82. package/package.json +2 -2
  83. package/scripts/postinstall.js +26 -1
  84. package/src/error-log.ts +30 -0
  85. package/src/register-tools.ts +4 -0
  86. package/src/server.ts +23 -423
  87. package/src/status.ts +17 -0
  88. package/src/tools/context-status.ts +30 -1
  89. package/src/tools/delete-context.ts +10 -5
  90. package/src/tools/get-context.ts +3 -2
  91. package/src/tools/list-context.ts +620 -119
  92. package/src/tools/publish-to-team.ts +1 -1
  93. package/src/tools/save-context.ts +2 -2
  94. package/src/tools/session-start.ts +444 -484
@@ -1,6 +1,7 @@
1
1
  // Types
2
2
  export type {
3
3
  VaultConfig,
4
+ WatchConfig,
4
5
  RemoteConfig,
5
6
  RecallConfig,
6
7
  ConsolidationConfig,
@@ -97,6 +98,7 @@ export {
97
98
  recencyDecayScore,
98
99
  dotProduct,
99
100
  reciprocalRankFusion,
101
+ computeHeatForEntry,
100
102
  } from './search.js';
101
103
 
102
104
  // Capture
@@ -105,5 +107,13 @@ export { writeEntry, updateEntryFile, captureAndIndex } from './capture.js';
105
107
  // Indexing
106
108
  export { shouldIndex } from './indexing.js';
107
109
 
110
+ // Compact
111
+ export { compact, restoreCompactedBody } from './compact.js';
112
+ export type { CompactCtx, CompactOptions, CompactResult } from './compact.js';
113
+
108
114
  // Ingest URL
109
115
  export { htmlToMarkdown, extractHtmlContent, ingestUrl } from './ingest-url.js';
116
+
117
+ // Watch
118
+ export { startWatcher } from './watch.js';
119
+ export type { WatcherOptions, VaultWatcher } from './watch.js';
@@ -124,6 +124,7 @@ export async function hybridSearch(
124
124
  includeSuperseeded = false,
125
125
  includeEphemeral = false,
126
126
  contextEmbedding = null,
127
+ trackMeta,
127
128
  } = opts;
128
129
 
129
130
  const rowMap = new Map<string, VaultEntry>();
@@ -339,7 +340,8 @@ export async function hybridSearch(
339
340
  entry.recall_count ?? 0,
340
341
  entry.last_recalled_at ?? null
341
342
  );
342
- rrfScores.set(id, (rrfScores.get(id) ?? 0) * boost * recall);
343
+ const durable = entry.tier === 'durable' ? 1.3 : 1.0;
344
+ rrfScores.set(id, (rrfScores.get(id) ?? 0) * boost * recall * durable);
343
345
  }
344
346
 
345
347
  const candidates: SearchResult[] = [...rowMap.values()].map((entry) => ({
@@ -389,13 +391,13 @@ export async function hybridSearch(
389
391
  if (vec) selectedVecs.push(vec);
390
392
  }
391
393
  const dedupedPage = injectDiscoverySlots(selected.slice(offset, offset + limit), candidates);
392
- trackAccess(ctx, dedupedPage);
394
+ trackAccess(ctx, dedupedPage, trackMeta);
393
395
  return dedupedPage;
394
396
  }
395
397
 
396
398
  const page = candidates.slice(offset, offset + limit);
397
399
  const finalPage = injectDiscoverySlots(page, candidates);
398
- trackAccess(ctx, finalPage);
400
+ trackAccess(ctx, finalPage, trackMeta);
399
401
  return finalPage;
400
402
  }
401
403
 
@@ -428,7 +430,7 @@ export function setSessionId(id: string): void {
428
430
  _sessionId = id;
429
431
  }
430
432
 
431
- export function trackAccess(ctx: BaseCtx, entries: SearchResult[]): void {
433
+ export function trackAccess(ctx: BaseCtx, entries: SearchResult[], meta?: { query?: string; sessionGoal?: string }): void {
432
434
  if (!entries.length) return;
433
435
 
434
436
  const ids = entries.map((e) => e.id);
@@ -478,4 +480,53 @@ export function trackAccess(ctx: BaseCtx, entries: SearchResult[]): void {
478
480
  // Non-fatal
479
481
  }
480
482
  }
483
+
484
+ // Write per-access rows to access_log for adaptive tiering
485
+ try {
486
+ const insertLog = ctx.db.prepare(
487
+ `INSERT INTO access_log (entry_id, query, session_id, session_goal, accessed_at) VALUES (?, ?, ?, ?, ?)`
488
+ );
489
+ const sid = _sessionId || 'default';
490
+ for (const entry of entries) {
491
+ insertLog.run(entry.id, meta?.query ?? null, sid, meta?.sessionGoal ?? null, now);
492
+ }
493
+ } catch {
494
+ // Non-fatal
495
+ }
496
+
497
+ // Compute and write heat_tier for each accessed entry
498
+ try {
499
+ const updateHeat = ctx.db.prepare(`UPDATE vault SET heat_tier = ? WHERE id = ?`);
500
+ for (const entry of entries) {
501
+ const recallCount = (entry.recall_count ?? 0) + 1;
502
+ const recallSessions = entry.recall_sessions ?? 0;
503
+ // Just recalled, so recency bonus is 10 (0 days since last recall)
504
+ const heat = (recallCount * 3) + (recallSessions * 2) + 10;
505
+ const tier = heat > 10 ? 'hot' : heat >= 1 ? 'warm' : null;
506
+ updateHeat.run(tier, entry.id);
507
+ }
508
+ } catch {
509
+ // Non-fatal
510
+ }
511
+ }
512
+
513
+ export function computeHeatForEntry(entry: { recall_count: number; recall_sessions: number; last_recalled_at: string | null; created_at: string }): { heat: number; tier: 'hot' | 'warm' | 'cold' | null } {
514
+ const recallCount = entry.recall_count ?? 0;
515
+ const recallSessions = entry.recall_sessions ?? 0;
516
+
517
+ let recencyBonus = 0;
518
+ if (entry.last_recalled_at) {
519
+ const daysSinceRecall = (Date.now() - new Date(entry.last_recalled_at).getTime()) / 86400000;
520
+ recencyBonus = Math.max(0, 10 - daysSinceRecall);
521
+ }
522
+
523
+ const heat = (recallCount * 3) + (recallSessions * 2) + recencyBonus;
524
+
525
+ if (heat > 10) return { heat, tier: 'hot' };
526
+ if (heat >= 1) return { heat, tier: 'warm' };
527
+
528
+ const ageDays = (Date.now() - new Date(entry.created_at).getTime()) / 86400000;
529
+ if (ageDays > 30) return { heat, tier: 'cold' };
530
+
531
+ return { heat, tier: null };
481
532
  }
@@ -0,0 +1,299 @@
1
+ import type { BaseCtx, CaptureResult } from './types.js';
2
+ import { captureAndIndex } from './capture.js';
3
+
4
+ export interface TierAnalysisOptions {
5
+ hotThreshold?: number;
6
+ hotDays?: number;
7
+ coldDays?: number;
8
+ coAccessThreshold?: number;
9
+ dryRun?: boolean;
10
+ }
11
+
12
+ export interface HotEntry {
13
+ id: string;
14
+ title: string | null;
15
+ accessCount: number;
16
+ }
17
+
18
+ export interface ColdEntry {
19
+ id: string;
20
+ title: string | null;
21
+ lastAccessed: string | null;
22
+ coldSummary: string;
23
+ }
24
+
25
+ export interface CoAccessBundle {
26
+ entries: Array<{ id: string; title: string | null }>;
27
+ totalWeight: number;
28
+ briefId?: string;
29
+ }
30
+
31
+ export interface TierAnalysisReport {
32
+ hotEntries: HotEntry[];
33
+ coldEntries: ColdEntry[];
34
+ warmReset: number;
35
+ bundles: CoAccessBundle[];
36
+ accessLogPruned: number;
37
+ }
38
+
39
+ function computeColdSummary(body: string): string {
40
+ const firstSentence = body.split(/[.!?]\s/)[0];
41
+ return (firstSentence || body).slice(0, 200).trim();
42
+ }
43
+
44
+ function findClusters(pairs: Array<{ a: string; b: string; count: number }>): Map<string, Set<string>> {
45
+ const parent = new Map<string, string>();
46
+ function find(x: string): string {
47
+ if (!parent.has(x)) parent.set(x, x);
48
+ if (parent.get(x) !== x) parent.set(x, find(parent.get(x)!));
49
+ return parent.get(x)!;
50
+ }
51
+ function union(a: string, b: string) {
52
+ const ra = find(a);
53
+ const rb = find(b);
54
+ if (ra !== rb) parent.set(ra, rb);
55
+ }
56
+
57
+ for (const { a, b } of pairs) {
58
+ union(a, b);
59
+ }
60
+
61
+ const clusters = new Map<string, Set<string>>();
62
+ for (const node of parent.keys()) {
63
+ const root = find(node);
64
+ if (!clusters.has(root)) clusters.set(root, new Set());
65
+ clusters.get(root)!.add(node);
66
+ }
67
+ return clusters;
68
+ }
69
+
70
+ export async function runTierAnalysis(
71
+ ctx: BaseCtx,
72
+ opts: TierAnalysisOptions = {}
73
+ ): Promise<TierAnalysisReport> {
74
+ const hotThreshold = opts.hotThreshold ?? 5;
75
+ const hotDays = opts.hotDays ?? 7;
76
+ const coldDays = opts.coldDays ?? 30;
77
+ const coAccessThreshold = opts.coAccessThreshold ?? 3;
78
+ const dryRun = opts.dryRun ?? false;
79
+
80
+ const report: TierAnalysisReport = {
81
+ hotEntries: [],
82
+ coldEntries: [],
83
+ warmReset: 0,
84
+ bundles: [],
85
+ accessLogPruned: 0,
86
+ };
87
+
88
+ // --- Hot detection ---
89
+ const hotRows = ctx.db
90
+ .prepare(
91
+ `SELECT v.id, v.title, COUNT(a.id) as access_count
92
+ FROM vault v
93
+ JOIN access_log a ON a.entry_id = v.id
94
+ WHERE a.accessed_at >= datetime('now', ? || ' days')
95
+ AND v.tier != 'ephemeral'
96
+ AND v.superseded_by IS NULL
97
+ GROUP BY v.id
98
+ HAVING COUNT(a.id) >= ?`
99
+ )
100
+ .all(`-${hotDays}`, hotThreshold) as Array<{ id: string; title: string | null; access_count: number }>;
101
+
102
+ for (const row of hotRows) {
103
+ report.hotEntries.push({
104
+ id: row.id,
105
+ title: row.title,
106
+ accessCount: row.access_count,
107
+ });
108
+ if (!dryRun) {
109
+ ctx.db.prepare(`UPDATE vault SET heat_tier = 'hot' WHERE id = ?`).run(row.id);
110
+ }
111
+ }
112
+
113
+ // --- Cold detection ---
114
+ const coldDayStr = `-${coldDays}`;
115
+ const coldRows = ctx.db
116
+ .prepare(
117
+ `SELECT v.id, v.title, v.body, v.last_accessed_at
118
+ FROM vault v
119
+ WHERE v.tier != 'durable'
120
+ AND v.superseded_by IS NULL
121
+ AND v.category != 'event'
122
+ AND (v.last_accessed_at IS NULL OR v.last_accessed_at < datetime('now', ? || ' days'))
123
+ AND NOT EXISTS (
124
+ SELECT 1 FROM access_log a
125
+ WHERE a.entry_id = v.id
126
+ AND a.accessed_at >= datetime('now', ? || ' days')
127
+ )`
128
+ )
129
+ .all(coldDayStr, coldDayStr) as Array<{ id: string; title: string | null; body: string; last_accessed_at: string | null }>;
130
+
131
+ for (const row of coldRows) {
132
+ const summary = computeColdSummary(row.body);
133
+ report.coldEntries.push({
134
+ id: row.id,
135
+ title: row.title,
136
+ lastAccessed: row.last_accessed_at,
137
+ coldSummary: summary,
138
+ });
139
+ if (!dryRun) {
140
+ ctx.db
141
+ .prepare(
142
+ `UPDATE vault SET heat_tier = 'cold', meta = json_set(COALESCE(meta, '{}'), '$.cold_summary', ?) WHERE id = ?`
143
+ )
144
+ .run(summary, row.id);
145
+ }
146
+ }
147
+
148
+ // --- Warm reset: clear stale heat_tier from entries that no longer qualify ---
149
+ const hotIds = new Set(report.hotEntries.map((e) => e.id));
150
+ const coldIds = new Set(report.coldEntries.map((e) => e.id));
151
+ if (!dryRun) {
152
+ // Clear heat_tier from entries that are no longer hot or cold
153
+ const staleHeat = ctx.db
154
+ .prepare(
155
+ `SELECT id, heat_tier FROM vault WHERE heat_tier IS NOT NULL AND superseded_by IS NULL`
156
+ )
157
+ .all() as Array<{ id: string; heat_tier: string }>;
158
+
159
+ const clearStmt = ctx.db.prepare(`UPDATE vault SET heat_tier = NULL WHERE id = ?`);
160
+ for (const row of staleHeat) {
161
+ if ((row.heat_tier === 'hot' && !hotIds.has(row.id)) ||
162
+ (row.heat_tier === 'cold' && !coldIds.has(row.id))) {
163
+ clearStmt.run(row.id);
164
+ report.warmReset++;
165
+ }
166
+ }
167
+ }
168
+
169
+ // --- Co-access bundle detection ---
170
+ const coAccessPairs = ctx.db
171
+ .prepare(
172
+ `SELECT c.entry_a, c.entry_b, c.count,
173
+ a.title as title_a, b.title as title_b
174
+ FROM co_retrievals c
175
+ JOIN vault a ON a.id = c.entry_a
176
+ JOIN vault b ON b.id = c.entry_b
177
+ WHERE c.count >= ?
178
+ AND a.superseded_by IS NULL
179
+ AND b.superseded_by IS NULL
180
+ ORDER BY c.count DESC
181
+ LIMIT 50`
182
+ )
183
+ .all(coAccessThreshold) as Array<{
184
+ entry_a: string;
185
+ entry_b: string;
186
+ count: number;
187
+ title_a: string | null;
188
+ title_b: string | null;
189
+ }>;
190
+
191
+ if (coAccessPairs.length > 0) {
192
+ const titleMap = new Map<string, string | null>();
193
+ const pairsForClustering: Array<{ a: string; b: string; count: number }> = [];
194
+ for (const p of coAccessPairs) {
195
+ titleMap.set(p.entry_a, p.title_a);
196
+ titleMap.set(p.entry_b, p.title_b);
197
+ pairsForClustering.push({ a: p.entry_a, b: p.entry_b, count: p.count });
198
+ }
199
+
200
+ const clusters = findClusters(pairsForClustering);
201
+
202
+ for (const [, members] of clusters) {
203
+ if (members.size < 3) continue;
204
+
205
+ const entries = [...members].map((id) => ({
206
+ id,
207
+ title: titleMap.get(id) ?? null,
208
+ }));
209
+ const totalWeight = pairsForClustering
210
+ .filter((p) => members.has(p.a) && members.has(p.b))
211
+ .reduce((sum, p) => sum + p.count, 0);
212
+
213
+ const bundle: CoAccessBundle = { entries, totalWeight };
214
+
215
+ if (!dryRun) {
216
+ // Check if a bundle brief already exists for this cluster
217
+ const entryIds = entries.map((e) => e.id).sort();
218
+ const bundleTag = `co-access-bundle:${entryIds.slice(0, 3).join('-')}`;
219
+ const existing = ctx.db
220
+ .prepare(`SELECT id FROM vault WHERE kind = 'brief' AND tags LIKE ?`)
221
+ .get(`%${bundleTag}%`);
222
+
223
+ if (!existing) {
224
+ const body = [
225
+ `Co-access bundle (auto-generated by adaptive tiering).`,
226
+ `These entries are frequently retrieved together (total weight: ${totalWeight}).`,
227
+ '',
228
+ ...entries.map((e) => `- **${e.title || '(untitled)'}** (\`${e.id}\`)`),
229
+ ].join('\n');
230
+
231
+ try {
232
+ const result: CaptureResult = await captureAndIndex(ctx, {
233
+ kind: 'brief',
234
+ title: `Co-access bundle: ${entries
235
+ .slice(0, 3)
236
+ .map((e) => e.title || e.id.slice(-8))
237
+ .join(', ')}`,
238
+ body,
239
+ tags: ['co-access-bundle', bundleTag, 'auto-tiering'],
240
+ tier: 'working',
241
+ related_to: entryIds,
242
+ });
243
+ bundle.briefId = result.id;
244
+ } catch {
245
+ // Non-fatal
246
+ }
247
+ }
248
+ }
249
+
250
+ report.bundles.push(bundle);
251
+ }
252
+ }
253
+
254
+ // --- Housekeeping: prune old access_log rows ---
255
+ if (!dryRun) {
256
+ try {
257
+ const pruneResult = ctx.db
258
+ .prepare(`DELETE FROM access_log WHERE accessed_at < datetime('now', '-90 days')`)
259
+ .run();
260
+ report.accessLogPruned = (pruneResult as any)?.changes ?? 0;
261
+ } catch {
262
+ // Non-fatal
263
+ }
264
+ }
265
+
266
+ // --- Persist tiering config as vault entry ---
267
+ if (!dryRun) {
268
+ try {
269
+ const configBody = [
270
+ `Adaptive tiering parameters last applied: ${new Date().toISOString()}`,
271
+ '',
272
+ `- Hot threshold: ${hotThreshold} accesses / ${hotDays} days`,
273
+ `- Cold threshold: ${coldDays} days no access`,
274
+ `- Co-access bundle threshold: ${coAccessThreshold}`,
275
+ `- Access log retention: 90 days`,
276
+ '',
277
+ `## Last Run Results`,
278
+ `- Hot entries: ${report.hotEntries.length}`,
279
+ `- Cold entries: ${report.coldEntries.length}`,
280
+ `- Warm resets: ${report.warmReset}`,
281
+ `- Bundles created: ${report.bundles.filter((b) => b.briefId).length}`,
282
+ `- Access log pruned: ${report.accessLogPruned} rows`,
283
+ ].join('\n');
284
+
285
+ await captureAndIndex(ctx, {
286
+ kind: 'reference',
287
+ title: 'Tiering Config',
288
+ body: configBody,
289
+ identity_key: 'tiering-config',
290
+ tier: 'durable',
291
+ tags: ['tiering', 'config'],
292
+ });
293
+ } catch {
294
+ // Non-fatal
295
+ }
296
+ }
297
+
298
+ return report;
299
+ }
@@ -22,6 +22,12 @@ export interface RemoteConfig {
22
22
  teamId?: string;
23
23
  }
24
24
 
25
+ export interface WatchConfig {
26
+ enabled: boolean;
27
+ path?: string;
28
+ debounceMs?: number;
29
+ }
30
+
25
31
  export interface VaultConfig {
26
32
  vaultDir: string;
27
33
  dataDir: string;
@@ -39,6 +45,7 @@ export interface VaultConfig {
39
45
  autoInsights: AutoInsightsConfig;
40
46
  indexing: IndexingConfig;
41
47
  remote?: RemoteConfig;
48
+ watch?: WatchConfig;
42
49
  }
43
50
 
44
51
  export interface RecallConfig {
@@ -106,6 +113,7 @@ export interface VaultEntry {
106
113
  recall_count: number;
107
114
  recall_sessions: number;
108
115
  last_recalled_at: string | null;
116
+ heat_tier: string | null;
109
117
  rowid?: number;
110
118
  }
111
119
 
@@ -204,4 +212,6 @@ export interface SearchOptions {
204
212
  includeEphemeral?: boolean;
205
213
  /** Pre-computed context embedding for contextual reinstatement boosting. */
206
214
  contextEmbedding?: Float32Array | null;
215
+ /** Metadata forwarded to access_log rows written by trackAccess. */
216
+ trackMeta?: { query?: string; sessionGoal?: string };
207
217
  }