opencodekit 0.17.13 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -6
- package/dist/template/.opencode/dcp.jsonc +81 -81
- package/dist/template/.opencode/memory/memory.db +0 -0
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +199 -23
- package/dist/template/.opencode/opencode.json.tui-migration.bak +1380 -0
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugin/lib/capture.ts +177 -0
- package/dist/template/.opencode/plugin/lib/context.ts +194 -0
- package/dist/template/.opencode/plugin/lib/curator.ts +234 -0
- package/dist/template/.opencode/plugin/lib/db/maintenance.ts +312 -0
- package/dist/template/.opencode/plugin/lib/db/observations.ts +299 -0
- package/dist/template/.opencode/plugin/lib/db/pipeline.ts +520 -0
- package/dist/template/.opencode/plugin/lib/db/schema.ts +356 -0
- package/dist/template/.opencode/plugin/lib/db/types.ts +211 -0
- package/dist/template/.opencode/plugin/lib/distill.ts +376 -0
- package/dist/template/.opencode/plugin/lib/inject.ts +126 -0
- package/dist/template/.opencode/plugin/lib/memory-admin-tools.ts +188 -0
- package/dist/template/.opencode/plugin/lib/memory-db.ts +54 -936
- package/dist/template/.opencode/plugin/lib/memory-helpers.ts +202 -0
- package/dist/template/.opencode/plugin/lib/memory-hooks.ts +240 -0
- package/dist/template/.opencode/plugin/lib/memory-tools.ts +341 -0
- package/dist/template/.opencode/plugin/memory.ts +56 -60
- package/dist/template/.opencode/plugin/sessions.ts +372 -93
- package/dist/template/.opencode/tui.json +15 -0
- package/package.json +1 -1
- package/dist/template/.opencode/tool/action-queue.ts +0 -313
- package/dist/template/.opencode/tool/memory-admin.ts +0 -445
- package/dist/template/.opencode/tool/memory-get.ts +0 -143
- package/dist/template/.opencode/tool/memory-read.ts +0 -45
- package/dist/template/.opencode/tool/memory-search.ts +0 -264
- package/dist/template/.opencode/tool/memory-timeline.ts +0 -105
- package/dist/template/.opencode/tool/memory-update.ts +0 -63
- 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
|
+
}
|