instar 0.9.0 → 0.9.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 (59) hide show
  1. package/dist/cli.js +0 -0
  2. package/dist/commands/server.d.ts.map +1 -1
  3. package/dist/commands/server.js +202 -71
  4. package/dist/commands/server.js.map +1 -1
  5. package/dist/commands/setup.d.ts.map +1 -1
  6. package/dist/commands/setup.js +38 -4
  7. package/dist/commands/setup.js.map +1 -1
  8. package/dist/core/AgentConnector.d.ts +76 -0
  9. package/dist/core/AgentConnector.d.ts.map +1 -0
  10. package/dist/core/AgentConnector.js +323 -0
  11. package/dist/core/AgentConnector.js.map +1 -0
  12. package/dist/core/AutoUpdater.d.ts +7 -0
  13. package/dist/core/AutoUpdater.d.ts.map +1 -1
  14. package/dist/core/AutoUpdater.js +31 -3
  15. package/dist/core/AutoUpdater.js.map +1 -1
  16. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  17. package/dist/core/PostUpdateMigrator.js +86 -5
  18. package/dist/core/PostUpdateMigrator.js.map +1 -1
  19. package/dist/core/StateWriteAuthority.d.ts +101 -0
  20. package/dist/core/StateWriteAuthority.d.ts.map +1 -0
  21. package/dist/core/StateWriteAuthority.js +167 -0
  22. package/dist/core/StateWriteAuthority.js.map +1 -0
  23. package/dist/core/types.d.ts +104 -0
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/index.d.ts +2 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/memory/TopicMemory.d.ts +167 -0
  30. package/dist/memory/TopicMemory.d.ts.map +1 -0
  31. package/dist/memory/TopicMemory.js +494 -0
  32. package/dist/memory/TopicMemory.js.map +1 -0
  33. package/dist/memory/TopicSummarizer.d.ts +58 -0
  34. package/dist/memory/TopicSummarizer.d.ts.map +1 -0
  35. package/dist/memory/TopicSummarizer.js +140 -0
  36. package/dist/memory/TopicSummarizer.js.map +1 -0
  37. package/dist/messaging/TelegramAdapter.d.ts +35 -0
  38. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  39. package/dist/messaging/TelegramAdapter.js +136 -2
  40. package/dist/messaging/TelegramAdapter.js.map +1 -1
  41. package/dist/server/AgentServer.d.ts +2 -0
  42. package/dist/server/AgentServer.d.ts.map +1 -1
  43. package/dist/server/AgentServer.js +1 -0
  44. package/dist/server/AgentServer.js.map +1 -1
  45. package/dist/server/routes.d.ts +2 -0
  46. package/dist/server/routes.d.ts.map +1 -1
  47. package/dist/server/routes.js +340 -1
  48. package/dist/server/routes.js.map +1 -1
  49. package/dist/users/UserManager.d.ts +21 -0
  50. package/dist/users/UserManager.d.ts.map +1 -1
  51. package/dist/users/UserManager.js +32 -0
  52. package/dist/users/UserManager.js.map +1 -1
  53. package/dist/users/UserOnboarding.d.ts +116 -0
  54. package/dist/users/UserOnboarding.d.ts.map +1 -0
  55. package/dist/users/UserOnboarding.js +365 -0
  56. package/dist/users/UserOnboarding.js.map +1 -0
  57. package/package.json +2 -1
  58. package/upgrades/0.8.23.md +106 -0
  59. package/upgrades/0.9.1.md +91 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * TopicMemory — SQLite-backed conversational memory per Telegram topic.
3
+ *
4
+ * Topic history is the HIGHEST priority context for any agent. It represents
5
+ * what the user and agent have been working on — the living relationship.
6
+ * All other context (identity, memory, relationships) supports this primary layer.
7
+ *
8
+ * Architecture:
9
+ * - Messages: Dual-written to JSONL (append log) AND SQLite (query layer)
10
+ * - Search: FTS5 full-text search over all messages, filterable by topic
11
+ * - Summaries: Rolling LLM-generated summaries per topic, updated on session end
12
+ * - Context: Session-start loads topic summary + recent messages as primary context
13
+ *
14
+ * The JSONL log remains the source of truth for disaster recovery.
15
+ * SQLite is a derived query layer that can be rebuilt from JSONL at any time.
16
+ *
17
+ * Born from the insight: "Topic history represents the highest level of information
18
+ * of what the user and the agent have been working on." — Justin, 2026-02-24
19
+ */
20
+ export interface TopicMessage {
21
+ messageId: number;
22
+ topicId: number;
23
+ text: string;
24
+ fromUser: boolean;
25
+ timestamp: string;
26
+ sessionName: string | null;
27
+ }
28
+ export interface TopicSummary {
29
+ topicId: number;
30
+ summary: string;
31
+ messageCountAtSummary: number;
32
+ lastMessageId: number;
33
+ updatedAt: string;
34
+ }
35
+ export interface TopicMeta {
36
+ topicId: number;
37
+ topicName: string | null;
38
+ messageCount: number;
39
+ lastActivity: string;
40
+ hasSummary: boolean;
41
+ }
42
+ export interface TopicSearchResult {
43
+ text: string;
44
+ topicId: number;
45
+ fromUser: boolean;
46
+ timestamp: string;
47
+ messageId: number;
48
+ rank: number;
49
+ highlight?: string;
50
+ }
51
+ export interface TopicContext {
52
+ /** Rolling summary of the full conversation (null if none generated yet) */
53
+ summary: string | null;
54
+ /** Recent messages (most recent N) */
55
+ recentMessages: TopicMessage[];
56
+ /** Total message count for this topic */
57
+ totalMessages: number;
58
+ /** Topic name if known */
59
+ topicName: string | null;
60
+ }
61
+ export declare class TopicMemory {
62
+ private db;
63
+ private dbPath;
64
+ private stateDir;
65
+ constructor(stateDir: string);
66
+ /**
67
+ * Open the database and create schema if needed.
68
+ */
69
+ open(): Promise<void>;
70
+ /**
71
+ * Close the database cleanly.
72
+ */
73
+ close(): void;
74
+ /**
75
+ * Create the schema if it doesn't exist.
76
+ */
77
+ private createSchema;
78
+ /**
79
+ * Insert a message into the database.
80
+ * Idempotent — duplicate messageId+topicId pairs are ignored.
81
+ */
82
+ insertMessage(msg: TopicMessage): void;
83
+ /**
84
+ * Batch-insert messages (for JSONL import).
85
+ */
86
+ insertMessages(messages: TopicMessage[]): number;
87
+ /**
88
+ * Get recent messages for a topic.
89
+ */
90
+ getRecentMessages(topicId: number, limit?: number): TopicMessage[];
91
+ /**
92
+ * Get the full context for a topic: summary + recent messages.
93
+ * This is the primary context loader for session spawning.
94
+ */
95
+ getTopicContext(topicId: number, recentLimit?: number): TopicContext;
96
+ /**
97
+ * Get message count for a topic.
98
+ */
99
+ getMessageCount(topicId: number): number;
100
+ /**
101
+ * Full-text search across topic messages.
102
+ * Optionally scoped to a single topic.
103
+ */
104
+ search(query: string, opts?: {
105
+ topicId?: number;
106
+ limit?: number;
107
+ }): TopicSearchResult[];
108
+ /**
109
+ * Get the rolling summary for a topic.
110
+ */
111
+ getTopicSummary(topicId: number): TopicSummary | null;
112
+ /**
113
+ * Save or update a rolling summary for a topic.
114
+ */
115
+ saveTopicSummary(topicId: number, summary: string, messageCount: number, lastMessageId: number): void;
116
+ /**
117
+ * Get messages since the last summary for a topic.
118
+ * Used to generate incremental summary updates.
119
+ */
120
+ getMessagesSinceSummary(topicId: number): TopicMessage[];
121
+ /**
122
+ * Check if a topic needs its summary updated.
123
+ * Returns true if there are more than `threshold` new messages since the last summary.
124
+ */
125
+ needsSummaryUpdate(topicId: number, threshold?: number): boolean;
126
+ /**
127
+ * Get metadata for a topic.
128
+ */
129
+ getTopicMeta(topicId: number): TopicMeta | null;
130
+ /**
131
+ * Update topic name in metadata.
132
+ */
133
+ setTopicName(topicId: number, name: string): void;
134
+ /**
135
+ * List all topics with metadata.
136
+ */
137
+ listTopics(): TopicMeta[];
138
+ /**
139
+ * Import messages from the JSONL log file.
140
+ * Idempotent — only inserts messages not already in the database.
141
+ * Returns the number of new messages imported.
142
+ */
143
+ importFromJsonl(jsonlPath: string): number;
144
+ /**
145
+ * Full rebuild — drop all data and reimport from JSONL.
146
+ */
147
+ rebuild(jsonlPath: string): number;
148
+ /**
149
+ * Rebuild topic_meta counts from messages table.
150
+ */
151
+ private rebuildTopicMeta;
152
+ /**
153
+ * Get database statistics.
154
+ */
155
+ stats(): {
156
+ totalMessages: number;
157
+ totalTopics: number;
158
+ topicsWithSummaries: number;
159
+ dbSizeBytes: number;
160
+ };
161
+ /**
162
+ * Format topic context as readable text for session injection.
163
+ * This is the primary interface for loading topic context into a session.
164
+ */
165
+ formatContextForSession(topicId: number, recentLimit?: number): string;
166
+ }
167
+ //# sourceMappingURL=TopicMemory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicMemory.d.ts","sourceRoot":"","sources":["../../src/memory/TopicMemory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,494 @@
1
+ /**
2
+ * TopicMemory — SQLite-backed conversational memory per Telegram topic.
3
+ *
4
+ * Topic history is the HIGHEST priority context for any agent. It represents
5
+ * what the user and agent have been working on — the living relationship.
6
+ * All other context (identity, memory, relationships) supports this primary layer.
7
+ *
8
+ * Architecture:
9
+ * - Messages: Dual-written to JSONL (append log) AND SQLite (query layer)
10
+ * - Search: FTS5 full-text search over all messages, filterable by topic
11
+ * - Summaries: Rolling LLM-generated summaries per topic, updated on session end
12
+ * - Context: Session-start loads topic summary + recent messages as primary context
13
+ *
14
+ * The JSONL log remains the source of truth for disaster recovery.
15
+ * SQLite is a derived query layer that can be rebuilt from JSONL at any time.
16
+ *
17
+ * Born from the insight: "Topic history represents the highest level of information
18
+ * of what the user and the agent have been working on." — Justin, 2026-02-24
19
+ */
20
+ import fs from 'node:fs';
21
+ import path from 'node:path';
22
+ const SCHEMA_VERSION = '1';
23
+ /**
24
+ * Strip FTS5 special syntax characters from a query.
25
+ */
26
+ function sanitizeFtsQuery(query) {
27
+ return query
28
+ .replace(/\b(AND|OR|NOT|NEAR)\b/gi, '')
29
+ .replace(/[*:"^{}().]/g, '')
30
+ .replace(/\s+/g, ' ')
31
+ .trim();
32
+ }
33
+ export class TopicMemory {
34
+ db = null;
35
+ dbPath;
36
+ stateDir;
37
+ constructor(stateDir) {
38
+ this.stateDir = stateDir;
39
+ this.dbPath = path.join(stateDir, 'topic-memory.db');
40
+ }
41
+ /**
42
+ * Open the database and create schema if needed.
43
+ */
44
+ async open() {
45
+ if (this.db)
46
+ return;
47
+ const BetterSqlite3 = (await import('better-sqlite3')).default;
48
+ const dir = path.dirname(this.dbPath);
49
+ fs.mkdirSync(dir, { recursive: true });
50
+ this.db = new BetterSqlite3(this.dbPath);
51
+ // WAL mode for concurrent reads during writes
52
+ this.db.pragma('journal_mode = WAL');
53
+ this.createSchema();
54
+ }
55
+ /**
56
+ * Close the database cleanly.
57
+ */
58
+ close() {
59
+ if (this.db) {
60
+ this.db.close();
61
+ this.db = null;
62
+ }
63
+ }
64
+ /**
65
+ * Create the schema if it doesn't exist.
66
+ */
67
+ createSchema() {
68
+ if (!this.db)
69
+ throw new Error('Database not open');
70
+ this.db.exec(`
71
+ -- Schema version tracking
72
+ CREATE TABLE IF NOT EXISTS meta (
73
+ key TEXT PRIMARY KEY,
74
+ value TEXT
75
+ );
76
+
77
+ -- All messages, indexed by topic
78
+ CREATE TABLE IF NOT EXISTS messages (
79
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
80
+ message_id INTEGER NOT NULL,
81
+ topic_id INTEGER NOT NULL,
82
+ text TEXT NOT NULL,
83
+ from_user INTEGER NOT NULL DEFAULT 0,
84
+ timestamp TEXT NOT NULL,
85
+ session_name TEXT,
86
+ UNIQUE(message_id, topic_id)
87
+ );
88
+
89
+ -- Indexes for efficient topic queries
90
+ CREATE INDEX IF NOT EXISTS idx_messages_topic ON messages(topic_id, timestamp);
91
+ CREATE INDEX IF NOT EXISTS idx_messages_topic_id ON messages(topic_id, message_id);
92
+
93
+ -- FTS5 full-text search over messages
94
+ CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
95
+ text,
96
+ content='messages',
97
+ content_rowid='id',
98
+ tokenize='porter unicode61'
99
+ );
100
+
101
+ -- Triggers to keep FTS5 in sync with messages table
102
+ CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN
103
+ INSERT INTO messages_fts(rowid, text) VALUES (new.id, new.text);
104
+ END;
105
+ CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN
106
+ INSERT INTO messages_fts(messages_fts, rowid, text) VALUES ('delete', old.id, old.text);
107
+ END;
108
+ CREATE TRIGGER IF NOT EXISTS messages_au AFTER UPDATE ON messages BEGIN
109
+ INSERT INTO messages_fts(messages_fts, rowid, text) VALUES ('delete', old.id, old.text);
110
+ INSERT INTO messages_fts(rowid, text) VALUES (new.id, new.text);
111
+ END;
112
+
113
+ -- Rolling summaries per topic
114
+ CREATE TABLE IF NOT EXISTS topic_summaries (
115
+ topic_id INTEGER PRIMARY KEY,
116
+ summary TEXT NOT NULL,
117
+ message_count_at_summary INTEGER NOT NULL DEFAULT 0,
118
+ last_message_id INTEGER NOT NULL DEFAULT 0,
119
+ updated_at TEXT NOT NULL
120
+ );
121
+
122
+ -- Topic metadata
123
+ CREATE TABLE IF NOT EXISTS topic_meta (
124
+ topic_id INTEGER PRIMARY KEY,
125
+ topic_name TEXT,
126
+ message_count INTEGER NOT NULL DEFAULT 0,
127
+ last_activity TEXT NOT NULL
128
+ );
129
+ `);
130
+ // Set schema version
131
+ this.db.prepare('INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)').run('schema_version', SCHEMA_VERSION);
132
+ }
133
+ // ── Message Operations ──────────────────────────────────────
134
+ /**
135
+ * Insert a message into the database.
136
+ * Idempotent — duplicate messageId+topicId pairs are ignored.
137
+ */
138
+ insertMessage(msg) {
139
+ if (!this.db)
140
+ return;
141
+ const stmt = this.db.prepare(`
142
+ INSERT OR IGNORE INTO messages (message_id, topic_id, text, from_user, timestamp, session_name)
143
+ VALUES (?, ?, ?, ?, ?, ?)
144
+ `);
145
+ stmt.run(msg.messageId, msg.topicId, msg.text, msg.fromUser ? 1 : 0, msg.timestamp, msg.sessionName);
146
+ // Update topic metadata
147
+ this.db.prepare(`
148
+ INSERT INTO topic_meta (topic_id, message_count, last_activity)
149
+ VALUES (?, 1, ?)
150
+ ON CONFLICT(topic_id) DO UPDATE SET
151
+ message_count = message_count + 1,
152
+ last_activity = excluded.last_activity
153
+ `).run(msg.topicId, msg.timestamp);
154
+ }
155
+ /**
156
+ * Batch-insert messages (for JSONL import).
157
+ */
158
+ insertMessages(messages) {
159
+ if (!this.db)
160
+ return 0;
161
+ const insert = this.db.prepare(`
162
+ INSERT OR IGNORE INTO messages (message_id, topic_id, text, from_user, timestamp, session_name)
163
+ VALUES (?, ?, ?, ?, ?, ?)
164
+ `);
165
+ let count = 0;
166
+ const tx = this.db.transaction(() => {
167
+ for (const msg of messages) {
168
+ const result = insert.run(msg.messageId, msg.topicId, msg.text, msg.fromUser ? 1 : 0, msg.timestamp, msg.sessionName);
169
+ if (result.changes > 0)
170
+ count++;
171
+ }
172
+ });
173
+ tx();
174
+ // Rebuild topic_meta from messages after bulk import
175
+ this.rebuildTopicMeta();
176
+ return count;
177
+ }
178
+ /**
179
+ * Get recent messages for a topic.
180
+ */
181
+ getRecentMessages(topicId, limit = 20) {
182
+ if (!this.db)
183
+ return [];
184
+ return this.db.prepare(`
185
+ SELECT message_id AS messageId, topic_id AS topicId, text, from_user AS fromUser, timestamp, session_name AS sessionName
186
+ FROM messages
187
+ WHERE topic_id = ?
188
+ ORDER BY timestamp DESC, id DESC
189
+ LIMIT ?
190
+ `).all(topicId, limit).reverse().map((row) => ({
191
+ ...row,
192
+ fromUser: !!row.fromUser,
193
+ }));
194
+ }
195
+ /**
196
+ * Get the full context for a topic: summary + recent messages.
197
+ * This is the primary context loader for session spawning.
198
+ */
199
+ getTopicContext(topicId, recentLimit = 20) {
200
+ if (!this.db)
201
+ return { summary: null, recentMessages: [], totalMessages: 0, topicName: null };
202
+ const summary = this.getTopicSummary(topicId);
203
+ const recentMessages = this.getRecentMessages(topicId, recentLimit);
204
+ const meta = this.getTopicMeta(topicId);
205
+ return {
206
+ summary: summary?.summary ?? null,
207
+ recentMessages,
208
+ totalMessages: meta?.messageCount ?? 0,
209
+ topicName: meta?.topicName ?? null,
210
+ };
211
+ }
212
+ /**
213
+ * Get message count for a topic.
214
+ */
215
+ getMessageCount(topicId) {
216
+ if (!this.db)
217
+ return 0;
218
+ const row = this.db.prepare('SELECT COUNT(*) AS count FROM messages WHERE topic_id = ?').get(topicId);
219
+ return row?.count ?? 0;
220
+ }
221
+ // ── Search ──────────────────────────────────────────────────
222
+ /**
223
+ * Full-text search across topic messages.
224
+ * Optionally scoped to a single topic.
225
+ */
226
+ search(query, opts) {
227
+ if (!this.db)
228
+ return [];
229
+ const sanitized = sanitizeFtsQuery(query);
230
+ if (!sanitized)
231
+ return [];
232
+ const limit = Math.min(opts?.limit ?? 20, 100);
233
+ let sql;
234
+ let params;
235
+ if (opts?.topicId !== undefined) {
236
+ sql = `
237
+ SELECT m.message_id AS messageId, m.topic_id AS topicId, m.text, m.from_user AS fromUser,
238
+ m.timestamp, rank,
239
+ highlight(messages_fts, 0, '<b>', '</b>') AS highlight
240
+ FROM messages_fts
241
+ JOIN messages m ON m.id = messages_fts.rowid
242
+ WHERE messages_fts MATCH ?
243
+ AND m.topic_id = ?
244
+ ORDER BY rank
245
+ LIMIT ?
246
+ `;
247
+ params = [sanitized, opts.topicId, limit];
248
+ }
249
+ else {
250
+ sql = `
251
+ SELECT m.message_id AS messageId, m.topic_id AS topicId, m.text, m.from_user AS fromUser,
252
+ m.timestamp, rank,
253
+ highlight(messages_fts, 0, '<b>', '</b>') AS highlight
254
+ FROM messages_fts
255
+ JOIN messages m ON m.id = messages_fts.rowid
256
+ WHERE messages_fts MATCH ?
257
+ ORDER BY rank
258
+ LIMIT ?
259
+ `;
260
+ params = [sanitized, limit];
261
+ }
262
+ return this.db.prepare(sql).all(...params).map((row) => ({
263
+ ...row,
264
+ fromUser: !!row.fromUser,
265
+ }));
266
+ }
267
+ // ── Summaries ───────────────────────────────────────────────
268
+ /**
269
+ * Get the rolling summary for a topic.
270
+ */
271
+ getTopicSummary(topicId) {
272
+ if (!this.db)
273
+ return null;
274
+ const row = this.db.prepare(`
275
+ SELECT topic_id AS topicId, summary, message_count_at_summary AS messageCountAtSummary,
276
+ last_message_id AS lastMessageId, updated_at AS updatedAt
277
+ FROM topic_summaries
278
+ WHERE topic_id = ?
279
+ `).get(topicId);
280
+ return row ?? null;
281
+ }
282
+ /**
283
+ * Save or update a rolling summary for a topic.
284
+ */
285
+ saveTopicSummary(topicId, summary, messageCount, lastMessageId) {
286
+ if (!this.db)
287
+ return;
288
+ this.db.prepare(`
289
+ INSERT INTO topic_summaries (topic_id, summary, message_count_at_summary, last_message_id, updated_at)
290
+ VALUES (?, ?, ?, ?, ?)
291
+ ON CONFLICT(topic_id) DO UPDATE SET
292
+ summary = excluded.summary,
293
+ message_count_at_summary = excluded.message_count_at_summary,
294
+ last_message_id = excluded.last_message_id,
295
+ updated_at = excluded.updated_at
296
+ `).run(topicId, summary, messageCount, lastMessageId, new Date().toISOString());
297
+ }
298
+ /**
299
+ * Get messages since the last summary for a topic.
300
+ * Used to generate incremental summary updates.
301
+ */
302
+ getMessagesSinceSummary(topicId) {
303
+ if (!this.db)
304
+ return [];
305
+ const summary = this.getTopicSummary(topicId);
306
+ const lastMessageId = summary?.lastMessageId ?? -1;
307
+ return this.db.prepare(`
308
+ SELECT message_id AS messageId, topic_id AS topicId, text, from_user AS fromUser, timestamp, session_name AS sessionName
309
+ FROM messages
310
+ WHERE topic_id = ? AND message_id > ?
311
+ ORDER BY timestamp ASC
312
+ `).all(topicId, lastMessageId).map((row) => ({
313
+ ...row,
314
+ fromUser: !!row.fromUser,
315
+ }));
316
+ }
317
+ /**
318
+ * Check if a topic needs its summary updated.
319
+ * Returns true if there are more than `threshold` new messages since the last summary.
320
+ */
321
+ needsSummaryUpdate(topicId, threshold = 20) {
322
+ if (!this.db)
323
+ return false;
324
+ const summary = this.getTopicSummary(topicId);
325
+ const totalMessages = this.getMessageCount(topicId);
326
+ if (!summary) {
327
+ // No summary yet — need one if there are enough messages
328
+ return totalMessages >= threshold;
329
+ }
330
+ return (totalMessages - summary.messageCountAtSummary) >= threshold;
331
+ }
332
+ // ── Topic Metadata ──────────────────────────────────────────
333
+ /**
334
+ * Get metadata for a topic.
335
+ */
336
+ getTopicMeta(topicId) {
337
+ if (!this.db)
338
+ return null;
339
+ const row = this.db.prepare(`
340
+ SELECT topic_id AS topicId, topic_name AS topicName, message_count AS messageCount,
341
+ last_activity AS lastActivity
342
+ FROM topic_meta
343
+ WHERE topic_id = ?
344
+ `).get(topicId);
345
+ if (!row)
346
+ return null;
347
+ const hasSummary = !!this.db.prepare('SELECT 1 FROM topic_summaries WHERE topic_id = ?').get(topicId);
348
+ return { ...row, hasSummary };
349
+ }
350
+ /**
351
+ * Update topic name in metadata.
352
+ */
353
+ setTopicName(topicId, name) {
354
+ if (!this.db)
355
+ return;
356
+ this.db.prepare(`
357
+ INSERT INTO topic_meta (topic_id, topic_name, message_count, last_activity)
358
+ VALUES (?, ?, 0, ?)
359
+ ON CONFLICT(topic_id) DO UPDATE SET topic_name = excluded.topic_name
360
+ `).run(topicId, name, new Date().toISOString());
361
+ }
362
+ /**
363
+ * List all topics with metadata.
364
+ */
365
+ listTopics() {
366
+ if (!this.db)
367
+ return [];
368
+ const rows = this.db.prepare(`
369
+ SELECT tm.topic_id AS topicId, tm.topic_name AS topicName,
370
+ tm.message_count AS messageCount, tm.last_activity AS lastActivity,
371
+ CASE WHEN ts.topic_id IS NOT NULL THEN 1 ELSE 0 END AS hasSummary
372
+ FROM topic_meta tm
373
+ LEFT JOIN topic_summaries ts ON ts.topic_id = tm.topic_id
374
+ ORDER BY tm.last_activity DESC
375
+ `).all();
376
+ return rows.map(r => ({ ...r, hasSummary: !!r.hasSummary }));
377
+ }
378
+ // ── Import / Rebuild ────────────────────────────────────────
379
+ /**
380
+ * Import messages from the JSONL log file.
381
+ * Idempotent — only inserts messages not already in the database.
382
+ * Returns the number of new messages imported.
383
+ */
384
+ importFromJsonl(jsonlPath) {
385
+ if (!this.db)
386
+ return 0;
387
+ if (!fs.existsSync(jsonlPath))
388
+ return 0;
389
+ const content = fs.readFileSync(jsonlPath, 'utf-8');
390
+ const lines = content.split('\n').filter(Boolean);
391
+ const messages = [];
392
+ for (const line of lines) {
393
+ try {
394
+ const entry = JSON.parse(line);
395
+ if (entry.topicId != null && entry.text) {
396
+ messages.push({
397
+ messageId: entry.messageId,
398
+ topicId: entry.topicId,
399
+ text: entry.text,
400
+ fromUser: entry.fromUser ?? false,
401
+ timestamp: entry.timestamp,
402
+ sessionName: entry.sessionName ?? null,
403
+ });
404
+ }
405
+ }
406
+ catch { /* skip malformed */ }
407
+ }
408
+ return this.insertMessages(messages);
409
+ }
410
+ /**
411
+ * Full rebuild — drop all data and reimport from JSONL.
412
+ */
413
+ rebuild(jsonlPath) {
414
+ if (!this.db)
415
+ return 0;
416
+ this.db.exec('DELETE FROM messages');
417
+ this.db.exec('DELETE FROM topic_meta');
418
+ // Keep summaries — they're LLM-generated and expensive to recreate
419
+ this.db.exec(`INSERT INTO messages_fts(messages_fts) VALUES ('rebuild')`);
420
+ return this.importFromJsonl(jsonlPath);
421
+ }
422
+ /**
423
+ * Rebuild topic_meta counts from messages table.
424
+ */
425
+ rebuildTopicMeta() {
426
+ if (!this.db)
427
+ return;
428
+ this.db.exec(`
429
+ INSERT OR REPLACE INTO topic_meta (topic_id, topic_name, message_count, last_activity)
430
+ SELECT
431
+ topic_id,
432
+ (SELECT topic_name FROM topic_meta WHERE topic_id = m.topic_id),
433
+ COUNT(*),
434
+ MAX(timestamp)
435
+ FROM messages m
436
+ GROUP BY topic_id
437
+ `);
438
+ }
439
+ // ── Stats ───────────────────────────────────────────────────
440
+ /**
441
+ * Get database statistics.
442
+ */
443
+ stats() {
444
+ if (!this.db)
445
+ return { totalMessages: 0, totalTopics: 0, topicsWithSummaries: 0, dbSizeBytes: 0 };
446
+ const msgCount = this.db.prepare('SELECT COUNT(*) AS c FROM messages').get()?.c ?? 0;
447
+ const topicCount = this.db.prepare('SELECT COUNT(*) AS c FROM topic_meta').get()?.c ?? 0;
448
+ const summaryCount = this.db.prepare('SELECT COUNT(*) AS c FROM topic_summaries').get()?.c ?? 0;
449
+ let dbSize = 0;
450
+ try {
451
+ dbSize = fs.statSync(this.dbPath).size;
452
+ }
453
+ catch { /* file may not exist yet */ }
454
+ return {
455
+ totalMessages: msgCount,
456
+ totalTopics: topicCount,
457
+ topicsWithSummaries: summaryCount,
458
+ dbSizeBytes: dbSize,
459
+ };
460
+ }
461
+ /**
462
+ * Format topic context as readable text for session injection.
463
+ * This is the primary interface for loading topic context into a session.
464
+ */
465
+ formatContextForSession(topicId, recentLimit = 30) {
466
+ const ctx = this.getTopicContext(topicId, recentLimit);
467
+ const lines = [];
468
+ lines.push(`--- TOPIC CONTEXT (${ctx.totalMessages} total messages) ---`);
469
+ if (ctx.topicName) {
470
+ lines.push(`Topic: ${ctx.topicName}`);
471
+ }
472
+ if (ctx.summary) {
473
+ lines.push('');
474
+ lines.push('CONVERSATION SUMMARY:');
475
+ lines.push(ctx.summary);
476
+ }
477
+ if (ctx.recentMessages.length > 0) {
478
+ lines.push('');
479
+ lines.push(`RECENT MESSAGES (last ${ctx.recentMessages.length}${ctx.summary ? ', since last summary' : ''}):`);
480
+ lines.push('');
481
+ for (const m of ctx.recentMessages) {
482
+ const sender = m.fromUser ? 'User' : 'Agent';
483
+ const ts = m.timestamp ? new Date(m.timestamp).toISOString().slice(11, 19) : '??:??';
484
+ const text = (m.text || '').slice(0, 500);
485
+ lines.push(`[${ts}] ${sender}: ${text}`);
486
+ }
487
+ }
488
+ lines.push('');
489
+ lines.push('To search conversation history: curl http://localhost:PORT/topic/search?topic=TOPIC_ID&q=QUERY');
490
+ lines.push('--- END TOPIC CONTEXT ---');
491
+ return lines.join('\n');
492
+ }
493
+ }
494
+ //# sourceMappingURL=TopicMemory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicMemory.js","sourceRoot":"","sources":["../../src/memory/TopicMemory.ts"],"names":[],"mappings":"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"}