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.
- package/dist/cli.js +0 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +202 -71
- package/dist/commands/server.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +38 -4
- package/dist/commands/setup.js.map +1 -1
- package/dist/core/AgentConnector.d.ts +76 -0
- package/dist/core/AgentConnector.d.ts.map +1 -0
- package/dist/core/AgentConnector.js +323 -0
- package/dist/core/AgentConnector.js.map +1 -0
- package/dist/core/AutoUpdater.d.ts +7 -0
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +31 -3
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +86 -5
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/StateWriteAuthority.d.ts +101 -0
- package/dist/core/StateWriteAuthority.d.ts.map +1 -0
- package/dist/core/StateWriteAuthority.js +167 -0
- package/dist/core/StateWriteAuthority.js.map +1 -0
- package/dist/core/types.d.ts +104 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/TopicMemory.d.ts +167 -0
- package/dist/memory/TopicMemory.d.ts.map +1 -0
- package/dist/memory/TopicMemory.js +494 -0
- package/dist/memory/TopicMemory.js.map +1 -0
- package/dist/memory/TopicSummarizer.d.ts +58 -0
- package/dist/memory/TopicSummarizer.d.ts.map +1 -0
- package/dist/memory/TopicSummarizer.js +140 -0
- package/dist/memory/TopicSummarizer.js.map +1 -0
- package/dist/messaging/TelegramAdapter.d.ts +35 -0
- package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
- package/dist/messaging/TelegramAdapter.js +136 -2
- package/dist/messaging/TelegramAdapter.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +2 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +340 -1
- package/dist/server/routes.js.map +1 -1
- package/dist/users/UserManager.d.ts +21 -0
- package/dist/users/UserManager.d.ts.map +1 -1
- package/dist/users/UserManager.js +32 -0
- package/dist/users/UserManager.js.map +1 -1
- package/dist/users/UserOnboarding.d.ts +116 -0
- package/dist/users/UserOnboarding.d.ts.map +1 -0
- package/dist/users/UserOnboarding.js +365 -0
- package/dist/users/UserOnboarding.js.map +1 -0
- package/package.json +2 -1
- package/upgrades/0.8.23.md +106 -0
- 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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"}
|