opencodekit 0.17.13 → 0.18.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 (44) hide show
  1. package/dist/index.js +4 -6
  2. package/dist/template/.opencode/AGENTS.md +57 -0
  3. package/dist/template/.opencode/agent/scout.md +0 -37
  4. package/dist/template/.opencode/command/resume.md +1 -1
  5. package/dist/template/.opencode/command/status.md +7 -14
  6. package/dist/template/.opencode/dcp.jsonc +81 -81
  7. package/dist/template/.opencode/memory/memory.db +0 -0
  8. package/dist/template/.opencode/memory.db +0 -0
  9. package/dist/template/.opencode/memory.db-shm +0 -0
  10. package/dist/template/.opencode/memory.db-wal +0 -0
  11. package/dist/template/.opencode/opencode.json +199 -23
  12. package/dist/template/.opencode/opencode.json.tui-migration.bak +1380 -0
  13. package/dist/template/.opencode/package.json +1 -1
  14. package/dist/template/.opencode/plugin/README.md +37 -25
  15. package/dist/template/.opencode/plugin/lib/capture.ts +177 -0
  16. package/dist/template/.opencode/plugin/lib/context.ts +194 -0
  17. package/dist/template/.opencode/plugin/lib/curator.ts +234 -0
  18. package/dist/template/.opencode/plugin/lib/db/maintenance.ts +312 -0
  19. package/dist/template/.opencode/plugin/lib/db/observations.ts +299 -0
  20. package/dist/template/.opencode/plugin/lib/db/pipeline.ts +520 -0
  21. package/dist/template/.opencode/plugin/lib/db/schema.ts +356 -0
  22. package/dist/template/.opencode/plugin/lib/db/types.ts +211 -0
  23. package/dist/template/.opencode/plugin/lib/distill.ts +376 -0
  24. package/dist/template/.opencode/plugin/lib/inject.ts +126 -0
  25. package/dist/template/.opencode/plugin/lib/memory-admin-tools.ts +188 -0
  26. package/dist/template/.opencode/plugin/lib/memory-db.ts +54 -936
  27. package/dist/template/.opencode/plugin/lib/memory-helpers.ts +202 -0
  28. package/dist/template/.opencode/plugin/lib/memory-hooks.ts +240 -0
  29. package/dist/template/.opencode/plugin/lib/memory-tools.ts +341 -0
  30. package/dist/template/.opencode/plugin/memory.ts +56 -60
  31. package/dist/template/.opencode/plugin/sessions.ts +372 -93
  32. package/dist/template/.opencode/skill/memory-system/SKILL.md +103 -60
  33. package/dist/template/.opencode/skill/session-management/SKILL.md +22 -35
  34. package/dist/template/.opencode/tui.json +15 -0
  35. package/package.json +1 -1
  36. package/dist/template/.opencode/plugin/compaction.ts +0 -190
  37. package/dist/template/.opencode/tool/action-queue.ts +0 -313
  38. package/dist/template/.opencode/tool/memory-admin.ts +0 -445
  39. package/dist/template/.opencode/tool/memory-get.ts +0 -143
  40. package/dist/template/.opencode/tool/memory-read.ts +0 -45
  41. package/dist/template/.opencode/tool/memory-search.ts +0 -264
  42. package/dist/template/.opencode/tool/memory-timeline.ts +0 -105
  43. package/dist/template/.opencode/tool/memory-update.ts +0 -63
  44. package/dist/template/.opencode/tool/observation.ts +0 -357
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Database Schema & Manager
3
+ *
4
+ * Contains all SQL schema definitions, migration logic, and the
5
+ * singleton database connection manager.
6
+ */
7
+
8
+ import { Database } from "bun:sqlite";
9
+ import path from "node:path";
10
+
11
+ // ============================================================================
12
+ // Schema v2
13
+ // ============================================================================
14
+
15
+ const SCHEMA_VERSION = 2;
16
+
17
+ const SCHEMA_SQL = `
18
+ -- Schema versioning for migrations
19
+ CREATE TABLE IF NOT EXISTS schema_versions (
20
+ id INTEGER PRIMARY KEY,
21
+ version INTEGER UNIQUE NOT NULL,
22
+ applied_at TEXT NOT NULL
23
+ );
24
+
25
+ -- Observations table (v2: added source column)
26
+ CREATE TABLE IF NOT EXISTS observations (
27
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
28
+ type TEXT NOT NULL CHECK(type IN ('decision','bugfix','feature','pattern','discovery','learning','warning')),
29
+ title TEXT NOT NULL,
30
+ subtitle TEXT,
31
+ facts TEXT,
32
+ narrative TEXT,
33
+ concepts TEXT,
34
+ files_read TEXT,
35
+ files_modified TEXT,
36
+ confidence TEXT CHECK(confidence IN ('high','medium','low')) DEFAULT 'high',
37
+ bead_id TEXT,
38
+ supersedes INTEGER,
39
+ superseded_by INTEGER,
40
+ valid_until TEXT,
41
+ markdown_file TEXT,
42
+ source TEXT CHECK(source IN ('manual','curator','imported')) DEFAULT 'manual',
43
+ created_at TEXT NOT NULL,
44
+ created_at_epoch INTEGER NOT NULL,
45
+ updated_at TEXT,
46
+ FOREIGN KEY(supersedes) REFERENCES observations(id) ON DELETE SET NULL,
47
+ FOREIGN KEY(superseded_by) REFERENCES observations(id) ON DELETE SET NULL
48
+ );
49
+
50
+ -- FTS5 with porter stemming (v2 upgrade)
51
+ CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
52
+ title,
53
+ subtitle,
54
+ narrative,
55
+ facts,
56
+ concepts,
57
+ content='observations',
58
+ content_rowid='id',
59
+ tokenize='porter unicode61'
60
+ );
61
+
62
+ -- Indexes for common queries
63
+ CREATE INDEX IF NOT EXISTS idx_observations_type ON observations(type);
64
+ CREATE INDEX IF NOT EXISTS idx_observations_created ON observations(created_at_epoch DESC);
65
+ CREATE INDEX IF NOT EXISTS idx_observations_bead_id ON observations(bead_id);
66
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by) WHERE superseded_by IS NOT NULL;
67
+ CREATE INDEX IF NOT EXISTS idx_observations_source ON observations(source);
68
+
69
+ -- Memory files table
70
+ CREATE TABLE IF NOT EXISTS memory_files (
71
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
72
+ file_path TEXT UNIQUE NOT NULL,
73
+ content TEXT NOT NULL,
74
+ mode TEXT CHECK(mode IN ('replace', 'append')) DEFAULT 'replace',
75
+ created_at TEXT NOT NULL,
76
+ created_at_epoch INTEGER NOT NULL,
77
+ updated_at TEXT,
78
+ updated_at_epoch INTEGER
79
+ );
80
+
81
+ CREATE INDEX IF NOT EXISTS idx_memory_files_path ON memory_files(file_path);
82
+
83
+
84
+
85
+
86
+ -- Temporal messages table (v2: raw message capture)
87
+ CREATE TABLE IF NOT EXISTS temporal_messages (
88
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
89
+ session_id TEXT NOT NULL,
90
+ message_id TEXT UNIQUE NOT NULL,
91
+ role TEXT NOT NULL,
92
+ content TEXT NOT NULL,
93
+ token_estimate INTEGER NOT NULL DEFAULT 0,
94
+ time_created INTEGER NOT NULL,
95
+ distillation_id INTEGER,
96
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
97
+ FOREIGN KEY(distillation_id) REFERENCES distillations(id) ON DELETE SET NULL
98
+ );
99
+
100
+ CREATE INDEX IF NOT EXISTS idx_temporal_session ON temporal_messages(session_id, time_created);
101
+ CREATE INDEX IF NOT EXISTS idx_temporal_undistilled ON temporal_messages(session_id) WHERE distillation_id IS NULL;
102
+ CREATE INDEX IF NOT EXISTS idx_temporal_time ON temporal_messages(time_created DESC);
103
+
104
+ -- Distillations table (v2: compressed message summaries)
105
+ CREATE TABLE IF NOT EXISTS distillations (
106
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
107
+ session_id TEXT NOT NULL,
108
+ content TEXT NOT NULL,
109
+ terms TEXT NOT NULL DEFAULT '[]',
110
+ message_count INTEGER NOT NULL DEFAULT 0,
111
+ compression_ratio REAL NOT NULL DEFAULT 0.0,
112
+ time_start INTEGER NOT NULL,
113
+ time_end INTEGER NOT NULL,
114
+ time_created INTEGER NOT NULL,
115
+ meta_distillation_id INTEGER,
116
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
117
+ FOREIGN KEY(meta_distillation_id) REFERENCES distillations(id) ON DELETE SET NULL
118
+ );
119
+
120
+ CREATE INDEX IF NOT EXISTS idx_distillations_session ON distillations(session_id, time_created DESC);
121
+ CREATE INDEX IF NOT EXISTS idx_distillations_time ON distillations(time_created DESC);
122
+
123
+ -- FTS5 for distillations (v2)
124
+ CREATE VIRTUAL TABLE IF NOT EXISTS distillations_fts USING fts5(
125
+ content,
126
+ terms,
127
+ content='distillations',
128
+ content_rowid='id',
129
+ tokenize='porter unicode61'
130
+ );
131
+ `;
132
+
133
+ // FTS5 sync triggers
134
+ const FTS_TRIGGERS_SQL = `
135
+ -- Observations FTS sync triggers
136
+ CREATE TRIGGER IF NOT EXISTS observations_fts_ai AFTER INSERT ON observations BEGIN
137
+ INSERT INTO observations_fts(rowid, title, subtitle, narrative, facts, concepts)
138
+ VALUES (new.id, new.title, new.subtitle, new.narrative, new.facts, new.concepts);
139
+ END;
140
+
141
+ CREATE TRIGGER IF NOT EXISTS observations_fts_ad AFTER DELETE ON observations BEGIN
142
+ INSERT INTO observations_fts(observations_fts, rowid, title, subtitle, narrative, facts, concepts)
143
+ VALUES('delete', old.id, old.title, old.subtitle, old.narrative, old.facts, old.concepts);
144
+ END;
145
+
146
+ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BEGIN
147
+ INSERT INTO observations_fts(observations_fts, rowid, title, subtitle, narrative, facts, concepts)
148
+ VALUES('delete', old.id, old.title, old.subtitle, old.narrative, old.facts, old.concepts);
149
+ INSERT INTO observations_fts(rowid, title, subtitle, narrative, facts, concepts)
150
+ VALUES (new.id, new.title, new.subtitle, new.narrative, new.facts, new.concepts);
151
+ END;
152
+
153
+ -- Distillations FTS sync triggers (v2)
154
+ CREATE TRIGGER IF NOT EXISTS distillations_fts_ai AFTER INSERT ON distillations BEGIN
155
+ INSERT INTO distillations_fts(rowid, content, terms)
156
+ VALUES (new.id, new.content, new.terms);
157
+ END;
158
+
159
+ CREATE TRIGGER IF NOT EXISTS distillations_fts_ad AFTER DELETE ON distillations BEGIN
160
+ INSERT INTO distillations_fts(distillations_fts, rowid, content, terms)
161
+ VALUES('delete', old.id, old.content, old.terms);
162
+ END;
163
+
164
+ CREATE TRIGGER IF NOT EXISTS distillations_fts_au AFTER UPDATE ON distillations BEGIN
165
+ INSERT INTO distillations_fts(distillations_fts, rowid, content, terms)
166
+ VALUES('delete', old.id, old.content, old.terms);
167
+ INSERT INTO distillations_fts(rowid, content, terms)
168
+ VALUES (new.id, new.content, new.terms);
169
+ END;
170
+ `;
171
+
172
+ // Migration from v1 to v2
173
+ const MIGRATION_V1_TO_V2 = `
174
+ -- Add source column to observations
175
+ ALTER TABLE observations ADD COLUMN source TEXT CHECK(source IN ('manual','curator','imported')) DEFAULT 'manual';
176
+
177
+ -- Add source index
178
+ CREATE INDEX IF NOT EXISTS idx_observations_source ON observations(source);
179
+
180
+ -- Create distillations table (before temporal_messages due to FK dependency)
181
+ CREATE TABLE IF NOT EXISTS distillations (
182
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
183
+ session_id TEXT NOT NULL,
184
+ content TEXT NOT NULL,
185
+ terms TEXT NOT NULL DEFAULT '[]',
186
+ message_count INTEGER NOT NULL DEFAULT 0,
187
+ compression_ratio REAL NOT NULL DEFAULT 0.0,
188
+ time_start INTEGER NOT NULL,
189
+ time_end INTEGER NOT NULL,
190
+ time_created INTEGER NOT NULL,
191
+ meta_distillation_id INTEGER,
192
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
193
+ FOREIGN KEY(meta_distillation_id) REFERENCES distillations(id) ON DELETE SET NULL
194
+ );
195
+
196
+ CREATE INDEX IF NOT EXISTS idx_distillations_session ON distillations(session_id, time_created DESC);
197
+ CREATE INDEX IF NOT EXISTS idx_distillations_time ON distillations(time_created DESC);
198
+
199
+ -- Create distillations FTS5
200
+ CREATE VIRTUAL TABLE IF NOT EXISTS distillations_fts USING fts5(
201
+ content,
202
+ terms,
203
+ content='distillations',
204
+ content_rowid='id',
205
+ tokenize='porter unicode61'
206
+ );
207
+
208
+ -- Create temporal_messages table
209
+ CREATE TABLE IF NOT EXISTS temporal_messages (
210
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
211
+ session_id TEXT NOT NULL,
212
+ message_id TEXT UNIQUE NOT NULL,
213
+ role TEXT NOT NULL,
214
+ content TEXT NOT NULL,
215
+ token_estimate INTEGER NOT NULL DEFAULT 0,
216
+ time_created INTEGER NOT NULL,
217
+ distillation_id INTEGER,
218
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
219
+ FOREIGN KEY(distillation_id) REFERENCES distillations(id) ON DELETE SET NULL
220
+ );
221
+
222
+ CREATE INDEX IF NOT EXISTS idx_temporal_session ON temporal_messages(session_id, time_created);
223
+ CREATE INDEX IF NOT EXISTS idx_temporal_undistilled ON temporal_messages(session_id) WHERE distillation_id IS NULL;
224
+ CREATE INDEX IF NOT EXISTS idx_temporal_time ON temporal_messages(time_created DESC);
225
+ `;
226
+
227
+ // ============================================================================
228
+ // Database Manager
229
+ // ============================================================================
230
+
231
+ let dbInstance: Database | null = null;
232
+
233
+ /**
234
+ * Get or create the memory database instance.
235
+ * Uses singleton pattern to reuse connection.
236
+ */
237
+ export function getMemoryDB(): Database {
238
+ if (dbInstance) return dbInstance;
239
+
240
+ const dbPath = path.join(process.cwd(), ".opencode/memory.db");
241
+ dbInstance = new Database(dbPath, { create: true });
242
+
243
+ // Enable WAL mode for better concurrency
244
+ dbInstance.run("PRAGMA journal_mode = WAL");
245
+ dbInstance.run("PRAGMA foreign_keys = ON");
246
+
247
+ // Initialize schema
248
+ initializeSchema(dbInstance);
249
+
250
+ return dbInstance;
251
+ }
252
+
253
+ /**
254
+ * Close the database connection (for cleanup).
255
+ */
256
+ export function closeMemoryDB(): void {
257
+ if (dbInstance) {
258
+ dbInstance.close();
259
+ dbInstance = null;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Initialize database schema with migration support.
265
+ */
266
+ function initializeSchema(db: Database): void {
267
+ let currentVersion = 0;
268
+
269
+ try {
270
+ const versionRow = db
271
+ .query("SELECT MAX(version) as version FROM schema_versions")
272
+ .get() as {
273
+ version: number | null;
274
+ } | null;
275
+ currentVersion = versionRow?.version ?? 0;
276
+ } catch {
277
+ // schema_versions table doesn't exist, need full init
278
+ }
279
+
280
+ if (currentVersion >= SCHEMA_VERSION) {
281
+ return; // Schema is up to date
282
+ }
283
+
284
+ if (currentVersion === 0) {
285
+ // Fresh install — run full v2 schema
286
+ db.exec(SCHEMA_SQL);
287
+
288
+ // Run FTS triggers
289
+ try {
290
+ db.exec(FTS_TRIGGERS_SQL);
291
+ } catch {
292
+ // Triggers may already exist
293
+ }
294
+ } else {
295
+ // Run incremental migrations
296
+ if (currentVersion < 2) {
297
+ migrateV1ToV2(db);
298
+ }
299
+ }
300
+
301
+ // Record schema version
302
+ db.run(
303
+ "INSERT OR REPLACE INTO schema_versions (id, version, applied_at) VALUES (1, ?, ?)",
304
+ [SCHEMA_VERSION, new Date().toISOString()],
305
+ );
306
+ }
307
+
308
+ /**
309
+ * Migrate from schema v1 to v2.
310
+ * Adds: source column, temporal_messages, distillations, porter FTS5.
311
+ */
312
+ function migrateV1ToV2(db: Database): void {
313
+ // Run structural changes (new tables, columns)
314
+ for (const stmt of MIGRATION_V1_TO_V2.split(";")) {
315
+ const trimmed = stmt.trim();
316
+ if (trimmed) {
317
+ try {
318
+ db.run(trimmed);
319
+ } catch {
320
+ // Statement may fail if already applied (e.g. column exists)
321
+ }
322
+ }
323
+ }
324
+
325
+ // Upgrade observations_fts to porter stemming
326
+ try {
327
+ // Drop old triggers first
328
+ db.run("DROP TRIGGER IF EXISTS observations_fts_ai");
329
+ db.run("DROP TRIGGER IF EXISTS observations_fts_ad");
330
+ db.run("DROP TRIGGER IF EXISTS observations_fts_au");
331
+
332
+ // Drop old FTS table
333
+ db.run("DROP TABLE IF EXISTS observations_fts");
334
+
335
+ // Recreate with porter tokenizer
336
+ db.run(`
337
+ CREATE VIRTUAL TABLE observations_fts USING fts5(
338
+ title, subtitle, narrative, facts, concepts,
339
+ content='observations', content_rowid='id',
340
+ tokenize='porter unicode61'
341
+ )
342
+ `);
343
+
344
+ // Rebuild FTS index from existing data
345
+ db.run("INSERT INTO observations_fts(observations_fts) VALUES('rebuild')");
346
+ } catch {
347
+ // FTS migration failed, non-fatal — search still works via LIKE fallback
348
+ }
349
+
350
+ // Create new triggers (observations + distillations)
351
+ try {
352
+ db.exec(FTS_TRIGGERS_SQL);
353
+ } catch {
354
+ // Triggers may already exist
355
+ }
356
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Memory Database Types & Configuration
3
+ *
4
+ * All types, interfaces, and configuration constants for the 4-tier memory system.
5
+ * This module has zero internal dependencies — safe to import anywhere.
6
+ */
7
+
8
+ // ============================================================================
9
+ // Configuration
10
+ // ============================================================================
11
+
12
+ export type ConfidenceLevel = "high" | "medium" | "low";
13
+
14
+ export const MEMORY_CONFIG = {
15
+ capture: {
16
+ enabled: true,
17
+ maxContentLength: 4000,
18
+ maxAgeDays: 180,
19
+ },
20
+ distillation: {
21
+ enabled: true,
22
+ minMessages: 10,
23
+ maxMessages: 50,
24
+ compressionTarget: 0.2,
25
+ topTerms: 30,
26
+ },
27
+ curator: {
28
+ enabled: true,
29
+ minDistillations: 3,
30
+ defaultConfidence: "medium" as ConfidenceLevel,
31
+ },
32
+ injection: {
33
+ enabled: true,
34
+ tokenBudget: 2000,
35
+ recencyDecay: 0.95,
36
+ minScore: 0.1,
37
+ topTerms: 30,
38
+ },
39
+ context: {
40
+ enabled: true,
41
+ maxContextTokens: 100_000,
42
+ protectedMessages: 5,
43
+ },
44
+ fts: {
45
+ tokenizer: "porter unicode61",
46
+ },
47
+ } as const;
48
+
49
+ // ============================================================================
50
+ // Observation Types
51
+ // ============================================================================
52
+
53
+ export type ObservationType =
54
+ | "decision"
55
+ | "bugfix"
56
+ | "feature"
57
+ | "pattern"
58
+ | "discovery"
59
+ | "learning"
60
+ | "warning";
61
+
62
+ export type ObservationSource = "manual" | "curator" | "imported";
63
+
64
+ export interface ObservationRow {
65
+ id: number;
66
+ type: ObservationType;
67
+ title: string;
68
+ subtitle: string | null;
69
+ facts: string | null; // JSON array
70
+ narrative: string | null;
71
+ concepts: string | null; // JSON array
72
+ files_read: string | null; // JSON array
73
+ files_modified: string | null; // JSON array
74
+ confidence: ConfidenceLevel;
75
+ bead_id: string | null;
76
+ supersedes: number | null;
77
+ superseded_by: number | null;
78
+ valid_until: string | null;
79
+ markdown_file: string | null;
80
+ source: ObservationSource;
81
+ created_at: string;
82
+ created_at_epoch: number;
83
+ updated_at: string | null;
84
+ }
85
+
86
+ export interface ObservationInput {
87
+ type: ObservationType;
88
+ title: string;
89
+ subtitle?: string;
90
+ facts?: string[];
91
+ narrative?: string;
92
+ concepts?: string[];
93
+ files_read?: string[];
94
+ files_modified?: string[];
95
+ confidence?: ConfidenceLevel;
96
+ bead_id?: string;
97
+ supersedes?: number;
98
+ markdown_file?: string;
99
+ source?: ObservationSource;
100
+ }
101
+
102
+ export interface SearchIndexResult {
103
+ id: number;
104
+ type: ObservationType;
105
+ title: string;
106
+ snippet: string;
107
+ created_at: string;
108
+ relevance_score: number;
109
+ }
110
+
111
+ // ============================================================================
112
+ // Memory File Types
113
+ // ============================================================================
114
+
115
+ export interface MemoryFileRow {
116
+ id: number;
117
+ file_path: string;
118
+ content: string;
119
+ mode: "replace" | "append";
120
+ created_at: string;
121
+ created_at_epoch: number;
122
+ updated_at: string | null;
123
+ updated_at_epoch: number | null;
124
+ }
125
+
126
+ // ============================================================================
127
+ // Temporal Message Types (v2)
128
+ // ============================================================================
129
+
130
+ export interface TemporalMessageRow {
131
+ id: number;
132
+ session_id: string;
133
+ message_id: string;
134
+ role: string;
135
+ content: string;
136
+ token_estimate: number;
137
+ time_created: number;
138
+ distillation_id: number | null;
139
+ created_at: string;
140
+ }
141
+
142
+ export interface TemporalMessageInput {
143
+ session_id: string;
144
+ message_id: string;
145
+ role: string;
146
+ content: string;
147
+ token_estimate: number;
148
+ time_created: number;
149
+ }
150
+
151
+ // ============================================================================
152
+ // Distillation Types (v2)
153
+ // ============================================================================
154
+
155
+ export interface DistillationRow {
156
+ id: number;
157
+ session_id: string;
158
+ content: string;
159
+ terms: string; // JSON array of top-N TF-IDF terms
160
+ message_count: number;
161
+ compression_ratio: number;
162
+ time_start: number;
163
+ time_end: number;
164
+ time_created: number;
165
+ meta_distillation_id: number | null;
166
+ created_at: string;
167
+ }
168
+
169
+ export interface DistillationInput {
170
+ session_id: string;
171
+ content: string;
172
+ terms: string[];
173
+ message_count: number;
174
+ compression_ratio: number;
175
+ time_start: number;
176
+ time_end: number;
177
+ meta_distillation_id?: number;
178
+ }
179
+
180
+ export interface DistillationSearchResult {
181
+ id: number;
182
+ session_id: string;
183
+ snippet: string;
184
+ message_count: number;
185
+ created_at: string;
186
+ relevance_score: number;
187
+ }
188
+
189
+ // ============================================================================
190
+ // Maintenance Types
191
+ // ============================================================================
192
+
193
+ export interface MaintenanceStats {
194
+ archived: number;
195
+ vacuumed: boolean;
196
+ checkpointed: boolean;
197
+ prunedMarkdown: number;
198
+ purgedMessages: number;
199
+ freedBytes: number;
200
+ dbSizeBefore: number;
201
+ dbSizeAfter: number;
202
+ }
203
+
204
+ export interface ArchiveOptions {
205
+ /** Archive observations older than this many days (default: 90) */
206
+ olderThanDays?: number;
207
+ /** Archive superseded observations regardless of age */
208
+ includeSuperseded?: boolean;
209
+ /** Dry run — don't actually archive, just count */
210
+ dryRun?: boolean;
211
+ }