teleton 0.8.4 → 0.8.6
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/README.md +40 -17
- package/dist/{bootstrap-NNEI3Z5H.js → bootstrap-PFBH6ALD.js} +11 -8
- package/dist/bridge-guards-HZTNH7IB.js +9 -0
- package/dist/{chunk-NH2CNRKJ.js → chunk-2UUGRY5B.js} +151 -159
- package/dist/{chunk-UMUONAD6.js → chunk-4MFN75ZK.js} +5941 -2716
- package/dist/{chunk-LC4TV3KL.js → chunk-4MG2AROG.js} +5 -7
- package/dist/{chunk-LZQOX6YY.js → chunk-6IFNQWIM.js} +7714 -8748
- package/dist/chunk-7KI25UJU.js +215 -0
- package/dist/chunk-AX5NBEHX.js +12 -0
- package/dist/{chunk-5LOHRZYY.js → chunk-BLUES3FJ.js} +80 -101
- package/dist/{chunk-CUE4UZXR.js → chunk-BT2I3ETV.js} +3 -3
- package/dist/chunk-CXTZPOTA.js +107 -0
- package/dist/{chunk-LVTKJQ7O.js → chunk-D3GT6YIY.js} +59 -7
- package/dist/chunk-EKCXKL5M.js +53 -0
- package/dist/{chunk-XDZDOKIF.js → chunk-F6S3L3OV.js} +3 -3
- package/dist/{chunk-C4NKJT2Z.js → chunk-J4WDJ7XS.js} +1 -1
- package/dist/{chunk-G7PCW63M.js → chunk-JYF2MM5I.js} +147 -113
- package/dist/{chunk-NVKBBTI6.js → chunk-K3QSIIMZ.js} +9 -6
- package/dist/{chunk-EYWNOHMJ.js → chunk-L653KKCR.js} +1 -0
- package/dist/chunk-OMQIAWEU.js +273 -0
- package/dist/chunk-PCT7GYBP.js +274 -0
- package/dist/chunk-QYZBWU2D.js +139 -0
- package/dist/{chunk-WTDAICGT.js → chunk-R6W4DJRK.js} +7 -7
- package/dist/{chunk-5SEMA47R.js → chunk-RILOEIK6.js} +1 -1
- package/dist/{chunk-6OOHHJ4N.js → chunk-TFTNZZDH.js} +20 -20
- package/dist/chunk-TTOZCZWE.js +96 -0
- package/dist/chunk-UJ54YT2T.js +12 -0
- package/dist/{chunk-GHMXWAXI.js → chunk-ULVL2W3D.js} +211 -445
- package/dist/{chunk-NQ6FZKCE.js → chunk-V3S3NXBQ.js} +3 -1
- package/dist/{chunk-H7MFXJZK.js → chunk-WSL4KIOI.js} +31 -26
- package/dist/{chunk-35MX4ZUI.js → chunk-Z5WY7BSB.js} +5 -5
- package/dist/{chunk-ALKAAG4O.js → chunk-ZGKE3OTA.js} +112 -49
- package/dist/{chunk-JROBTXWY.js → chunk-ZHRDETCX.js} +38 -4
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +272 -159
- package/dist/{client-5KD25NOP.js → client-S5UIK6OG.js} +10 -8
- package/dist/daily-logs-3WXGYAQF.js +25 -0
- package/dist/{get-my-gifts-Y7EN7RK4.js → get-my-gifts-3YSYM3LI.js} +3 -2
- package/dist/harden-permissions-PV5SGV5D.js +100 -0
- package/dist/index.d.ts +923 -0
- package/dist/index.js +29 -20
- package/dist/knowledge-RRWUIO3G.js +19 -0
- package/dist/{local-IHKJFQJS.js → local-MSZAXWUL.js} +3 -3
- package/dist/mcp-loader-OELDFR63.js +15 -0
- package/dist/{memory-QMJRM3XJ.js → memory-6U6HGRK2.js} +23 -12
- package/dist/memory-hook-T7Y235KY.js +19 -0
- package/dist/messages-KV5ADNJB.js +17 -0
- package/dist/{migrate-5VBAP52B.js → migrate-AX3HOKOO.js} +10 -7
- package/dist/{paths-XA2RJH4S.js → paths-WMVV7ZAJ.js} +1 -1
- package/dist/{server-WWGVDFPW.js → server-MFRYOGHR.js} +21 -23
- package/dist/{server-AJCOURH7.js → server-SFLCAZFR.js} +221 -27
- package/dist/{setup-server-VDY64CWW.js → setup-server-YWAPKZVE.js} +26 -26
- package/dist/{store-BY7S6IFN.js → store-PGHQASBC.js} +11 -8
- package/dist/{task-dependency-resolver-L6UUMTHK.js → task-dependency-resolver-YQKADDEU.js} +24 -10
- package/dist/{task-executor-XBNJLUCS.js → task-executor-LWAWD225.js} +4 -4
- package/dist/{tool-adapter-IVX2XQJE.js → tool-adapter-VKLUZSQS.js} +1 -1
- package/dist/{tool-index-FTERJSZK.js → tool-index-YEWDF5CK.js} +5 -5
- package/dist/{transcript-IM7G25OS.js → transcript-4Y3Z2BJ3.js} +3 -3
- package/dist/web/assets/Config-MNxA69ib.js +1 -0
- package/dist/web/assets/Conversations-Dk958paA.js +1 -0
- package/dist/web/assets/Dashboard-dM18fGOm.js +1 -0
- package/dist/web/assets/Hooks-D2griQnI.js +1 -0
- package/dist/web/assets/Mcp-CtWNzwsz.js +1 -0
- package/dist/web/assets/Memory-CfLwH45G.js +1 -0
- package/dist/web/assets/Plugins-3hoJprFo.js +1 -0
- package/dist/web/assets/SearchInput-CpcETdpE.js +1 -0
- package/dist/web/assets/Soul-BSxE73aK.js +1 -0
- package/dist/web/assets/Tasks-DkCkfu3A.js +1 -0
- package/dist/web/assets/TelegramSettingsPanel-BRzc5G6e.js +1 -0
- package/dist/web/assets/Tools-Du8B8Mb4.js +1 -0
- package/dist/web/assets/Wallet-BLILP2Gn.js +1 -0
- package/dist/web/assets/Workspace-qklcXpXV.js +1 -0
- package/dist/web/assets/index-BwEPTTKp.js +90 -0
- package/dist/web/assets/index-noejUsK7.css +1 -0
- package/dist/web/assets/{index.es-DitvF-9H.js → index.es-DdpKlnGb.js} +1 -1
- package/dist/web/assets/useToolManager-tdxkKn3H.js +1 -0
- package/dist/web/assets/utils-CnsbSMo4.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +7 -12
- package/src/templates/HEARTBEAT.md +5 -0
- package/dist/memory-hook-VUNWZ3NY.js +0 -19
- package/dist/web/assets/index-BfYCdwLI.js +0 -80
- package/dist/web/assets/index-DmlyQVhR.css +0 -1
- package/dist/{chunk-WFTC3JJW.js → chunk-3NO7QU7W.js} +1 -1
|
@@ -1,160 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JOURNAL_SCHEMA
|
|
3
|
+
} from "./chunk-QYZBWU2D.js";
|
|
4
|
+
import {
|
|
5
|
+
KnowledgeIndexer
|
|
6
|
+
} from "./chunk-OMQIAWEU.js";
|
|
7
|
+
import {
|
|
8
|
+
MessageStore
|
|
9
|
+
} from "./chunk-TTOZCZWE.js";
|
|
1
10
|
import {
|
|
2
11
|
CachedEmbeddingProvider,
|
|
3
12
|
createEmbeddingProvider,
|
|
4
13
|
hashText,
|
|
5
14
|
serializeEmbedding
|
|
6
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Z5WY7BSB.js";
|
|
7
16
|
import {
|
|
8
17
|
FEED_MESSAGE_MAX_CHARS,
|
|
9
18
|
HYBRID_SEARCH_MIN_SCORE,
|
|
10
|
-
KNOWLEDGE_CHUNK_SIZE,
|
|
11
19
|
RECENCY_DECAY_FACTOR,
|
|
12
20
|
RECENCY_WEIGHT,
|
|
13
21
|
SECONDS_PER_DAY,
|
|
14
22
|
SECONDS_PER_HOUR,
|
|
15
23
|
SQLITE_CACHE_SIZE_KB,
|
|
16
24
|
SQLITE_MMAP_SIZE
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import {
|
|
19
|
-
TELETON_ROOT
|
|
20
|
-
} from "./chunk-EYWNOHMJ.js";
|
|
25
|
+
} from "./chunk-J4WDJ7XS.js";
|
|
21
26
|
import {
|
|
22
27
|
createLogger
|
|
23
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-V3S3NXBQ.js";
|
|
24
29
|
|
|
25
30
|
// src/memory/database.ts
|
|
26
|
-
import Database2 from "better-sqlite3";
|
|
27
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync as chmodSync2 } from "fs";
|
|
28
|
-
import { dirname as dirname2 } from "path";
|
|
29
|
-
import * as sqliteVec from "sqlite-vec";
|
|
30
|
-
|
|
31
|
-
// src/utils/module-db.ts
|
|
32
31
|
import Database from "better-sqlite3";
|
|
33
32
|
import { existsSync, mkdirSync, chmodSync } from "fs";
|
|
34
|
-
import { dirname
|
|
35
|
-
|
|
36
|
-
var JOURNAL_SCHEMA = `
|
|
37
|
-
CREATE TABLE IF NOT EXISTS journal (
|
|
38
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
39
|
-
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
40
|
-
type TEXT NOT NULL CHECK(type IN ('trade', 'gift', 'middleman', 'kol')),
|
|
41
|
-
action TEXT NOT NULL,
|
|
42
|
-
asset_from TEXT,
|
|
43
|
-
asset_to TEXT,
|
|
44
|
-
amount_from REAL,
|
|
45
|
-
amount_to REAL,
|
|
46
|
-
price_ton REAL,
|
|
47
|
-
counterparty TEXT,
|
|
48
|
-
platform TEXT,
|
|
49
|
-
reasoning TEXT,
|
|
50
|
-
outcome TEXT CHECK(outcome IN ('pending', 'profit', 'loss', 'neutral', 'cancelled')),
|
|
51
|
-
pnl_ton REAL,
|
|
52
|
-
pnl_pct REAL,
|
|
53
|
-
tx_hash TEXT,
|
|
54
|
-
tool_used TEXT,
|
|
55
|
-
chat_id TEXT,
|
|
56
|
-
user_id INTEGER,
|
|
57
|
-
closed_at INTEGER,
|
|
58
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
CREATE INDEX IF NOT EXISTS idx_journal_type ON journal(type);
|
|
62
|
-
CREATE INDEX IF NOT EXISTS idx_journal_timestamp ON journal(timestamp DESC);
|
|
63
|
-
CREATE INDEX IF NOT EXISTS idx_journal_asset_from ON journal(asset_from);
|
|
64
|
-
CREATE INDEX IF NOT EXISTS idx_journal_outcome ON journal(outcome);
|
|
65
|
-
CREATE INDEX IF NOT EXISTS idx_journal_type_timestamp ON journal(type, timestamp DESC);
|
|
66
|
-
`;
|
|
67
|
-
var USED_TRANSACTIONS_SCHEMA = `
|
|
68
|
-
CREATE TABLE IF NOT EXISTS used_transactions (
|
|
69
|
-
tx_hash TEXT PRIMARY KEY,
|
|
70
|
-
user_id TEXT NOT NULL,
|
|
71
|
-
amount REAL NOT NULL,
|
|
72
|
-
game_type TEXT NOT NULL,
|
|
73
|
-
used_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
CREATE INDEX IF NOT EXISTS idx_used_tx_user ON used_transactions(user_id);
|
|
77
|
-
CREATE INDEX IF NOT EXISTS idx_used_tx_used_at ON used_transactions(used_at);
|
|
78
|
-
`;
|
|
79
|
-
function openModuleDb(path) {
|
|
80
|
-
const dir = dirname(path);
|
|
81
|
-
if (!existsSync(dir)) {
|
|
82
|
-
mkdirSync(dir, { recursive: true });
|
|
83
|
-
}
|
|
84
|
-
const db = new Database(path);
|
|
85
|
-
try {
|
|
86
|
-
chmodSync(path, 384);
|
|
87
|
-
} catch {
|
|
88
|
-
}
|
|
89
|
-
db.pragma("journal_mode = WAL");
|
|
90
|
-
return db;
|
|
91
|
-
}
|
|
92
|
-
function createDbWrapper(getDb, moduleName) {
|
|
93
|
-
return function withDb(executor) {
|
|
94
|
-
return (params, context) => {
|
|
95
|
-
const moduleDb = getDb();
|
|
96
|
-
if (!moduleDb) {
|
|
97
|
-
return Promise.resolve({
|
|
98
|
-
success: false,
|
|
99
|
-
error: `${moduleName} module not started`
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
return executor(params, { ...context, db: moduleDb });
|
|
103
|
-
};
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
var MAIN_DB_PATH = join(TELETON_ROOT, "memory.db");
|
|
107
|
-
function migrateFromMainDb(moduleDb, tables) {
|
|
108
|
-
let totalMigrated = 0;
|
|
109
|
-
for (const table of tables) {
|
|
110
|
-
if (!/^[a-z_]+$/.test(table)) {
|
|
111
|
-
throw new Error(`Invalid table name for migration: "${table}"`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
for (const table of tables) {
|
|
115
|
-
try {
|
|
116
|
-
const row = moduleDb.prepare(`SELECT COUNT(*) as c FROM ${table}`).get();
|
|
117
|
-
if (row.c > 0) return 0;
|
|
118
|
-
} catch {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (!existsSync(MAIN_DB_PATH)) return 0;
|
|
123
|
-
try {
|
|
124
|
-
moduleDb.exec(`ATTACH DATABASE '${MAIN_DB_PATH}' AS main_db`);
|
|
125
|
-
for (const table of tables) {
|
|
126
|
-
try {
|
|
127
|
-
const exists = moduleDb.prepare(`SELECT name FROM main_db.sqlite_master WHERE type='table' AND name=?`).get(table);
|
|
128
|
-
if (!exists) continue;
|
|
129
|
-
const src = moduleDb.prepare(`SELECT COUNT(*) as c FROM main_db.${table}`).get();
|
|
130
|
-
if (src.c === 0) continue;
|
|
131
|
-
const dstCols = moduleDb.prepare(`PRAGMA table_info(${table})`).all().map((r) => r.name);
|
|
132
|
-
const srcCols = moduleDb.prepare(`PRAGMA main_db.table_info(${table})`).all().map((r) => r.name);
|
|
133
|
-
const shared = dstCols.filter((c) => srcCols.includes(c));
|
|
134
|
-
if (shared.length === 0) continue;
|
|
135
|
-
const cols = shared.join(", ");
|
|
136
|
-
moduleDb.exec(
|
|
137
|
-
`INSERT OR IGNORE INTO ${table} (${cols}) SELECT ${cols} FROM main_db.${table}`
|
|
138
|
-
);
|
|
139
|
-
totalMigrated += src.c;
|
|
140
|
-
log.info(`Migrated ${src.c} rows from memory.db \u2192 ${table}`);
|
|
141
|
-
} catch (e) {
|
|
142
|
-
log.warn({ err: e }, `Could not migrate table ${table}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
moduleDb.exec(`DETACH DATABASE main_db`);
|
|
146
|
-
} catch (e) {
|
|
147
|
-
log.warn({ err: e }, `Migration from memory.db failed`);
|
|
148
|
-
try {
|
|
149
|
-
moduleDb.exec(`DETACH DATABASE main_db`);
|
|
150
|
-
} catch {
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return totalMigrated;
|
|
154
|
-
}
|
|
33
|
+
import { dirname } from "path";
|
|
34
|
+
import * as sqliteVec from "sqlite-vec";
|
|
155
35
|
|
|
156
36
|
// src/memory/schema.ts
|
|
157
|
-
var
|
|
37
|
+
var log = createLogger("Memory");
|
|
158
38
|
function compareSemver(a, b) {
|
|
159
39
|
const parseVersion = (v) => {
|
|
160
40
|
const parts = v.split("-")[0].split(".").map(Number);
|
|
@@ -371,16 +251,16 @@ function ensureSchema(db) {
|
|
|
371
251
|
);
|
|
372
252
|
|
|
373
253
|
-- FTS triggers for messages
|
|
374
|
-
CREATE TRIGGER IF NOT EXISTS tg_messages_fts_insert AFTER INSERT ON tg_messages BEGIN
|
|
254
|
+
CREATE TRIGGER IF NOT EXISTS tg_messages_fts_insert AFTER INSERT ON tg_messages WHEN new.text IS NOT NULL BEGIN
|
|
375
255
|
INSERT INTO tg_messages_fts(rowid, text, id, chat_id, sender_id, timestamp)
|
|
376
256
|
VALUES (new.rowid, new.text, new.id, new.chat_id, new.sender_id, new.timestamp);
|
|
377
257
|
END;
|
|
378
258
|
|
|
379
|
-
CREATE TRIGGER IF NOT EXISTS tg_messages_fts_delete AFTER DELETE ON tg_messages BEGIN
|
|
259
|
+
CREATE TRIGGER IF NOT EXISTS tg_messages_fts_delete AFTER DELETE ON tg_messages WHEN old.text IS NOT NULL BEGIN
|
|
380
260
|
DELETE FROM tg_messages_fts WHERE rowid = old.rowid;
|
|
381
261
|
END;
|
|
382
262
|
|
|
383
|
-
CREATE TRIGGER IF NOT EXISTS tg_messages_fts_update AFTER UPDATE ON tg_messages BEGIN
|
|
263
|
+
CREATE TRIGGER IF NOT EXISTS tg_messages_fts_update AFTER UPDATE ON tg_messages WHEN old.text IS NOT NULL OR new.text IS NOT NULL BEGIN
|
|
384
264
|
DELETE FROM tg_messages_fts WHERE rowid = old.rowid;
|
|
385
265
|
INSERT INTO tg_messages_fts(rowid, text, id, chat_id, sender_id, timestamp)
|
|
386
266
|
VALUES (new.rowid, new.text, new.id, new.chat_id, new.sender_id, new.timestamp);
|
|
@@ -492,15 +372,15 @@ function setSchemaVersion(db, version) {
|
|
|
492
372
|
`
|
|
493
373
|
).run(version);
|
|
494
374
|
}
|
|
495
|
-
var CURRENT_SCHEMA_VERSION = "1.
|
|
375
|
+
var CURRENT_SCHEMA_VERSION = "1.18.0";
|
|
496
376
|
function runMigrations(db) {
|
|
497
377
|
const currentVersion = getSchemaVersion(db);
|
|
498
378
|
if (!currentVersion || versionLessThan(currentVersion, "1.1.0")) {
|
|
499
|
-
|
|
379
|
+
log.info("Running migration: Adding scheduled task columns...");
|
|
500
380
|
try {
|
|
501
381
|
const tableExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='tasks'").get();
|
|
502
382
|
if (!tableExists) {
|
|
503
|
-
|
|
383
|
+
log.info("Tasks table doesn't exist yet, skipping column migration");
|
|
504
384
|
setSchemaVersion(db, CURRENT_SCHEMA_VERSION);
|
|
505
385
|
return;
|
|
506
386
|
}
|
|
@@ -533,21 +413,21 @@ function runMigrations(db) {
|
|
|
533
413
|
CREATE INDEX IF NOT EXISTS idx_task_deps_task ON task_dependencies(task_id);
|
|
534
414
|
CREATE INDEX IF NOT EXISTS idx_task_deps_parent ON task_dependencies(depends_on_task_id);
|
|
535
415
|
`);
|
|
536
|
-
|
|
416
|
+
log.info("Migration 1.1.0 complete: Scheduled tasks support added");
|
|
537
417
|
} catch (error) {
|
|
538
|
-
|
|
418
|
+
log.error({ err: error }, "Migration 1.1.0 failed");
|
|
539
419
|
throw error;
|
|
540
420
|
}
|
|
541
421
|
}
|
|
542
422
|
if (!currentVersion || versionLessThan(currentVersion, "1.2.0")) {
|
|
543
423
|
try {
|
|
544
|
-
|
|
424
|
+
log.info("Running migration 1.2.0: Extend sessions table for SQLite backend");
|
|
545
425
|
const addColumnIfNotExists = (table, column, type) => {
|
|
546
426
|
try {
|
|
547
427
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
548
|
-
} catch (
|
|
549
|
-
if (!(
|
|
550
|
-
throw
|
|
428
|
+
} catch (error) {
|
|
429
|
+
if (!(error instanceof Error) || !error.message.includes("duplicate column name")) {
|
|
430
|
+
throw error;
|
|
551
431
|
}
|
|
552
432
|
}
|
|
553
433
|
};
|
|
@@ -570,14 +450,14 @@ function runMigrations(db) {
|
|
|
570
450
|
);
|
|
571
451
|
}
|
|
572
452
|
db.exec("CREATE INDEX IF NOT EXISTS idx_sessions_updated ON sessions(updated_at DESC)");
|
|
573
|
-
|
|
453
|
+
log.info("Migration 1.2.0 complete: Sessions table extended");
|
|
574
454
|
} catch (error) {
|
|
575
|
-
|
|
455
|
+
log.error({ err: error }, "Migration 1.2.0 failed");
|
|
576
456
|
throw error;
|
|
577
457
|
}
|
|
578
458
|
}
|
|
579
459
|
if (!currentVersion || versionLessThan(currentVersion, "1.9.0")) {
|
|
580
|
-
|
|
460
|
+
log.info("Running migration 1.9.0: Upgrade embedding_cache to BLOB storage");
|
|
581
461
|
try {
|
|
582
462
|
db.exec(`DROP TABLE IF EXISTS embedding_cache`);
|
|
583
463
|
db.exec(`
|
|
@@ -593,14 +473,14 @@ function runMigrations(db) {
|
|
|
593
473
|
);
|
|
594
474
|
CREATE INDEX IF NOT EXISTS idx_embedding_cache_accessed ON embedding_cache(accessed_at);
|
|
595
475
|
`);
|
|
596
|
-
|
|
476
|
+
log.info("Migration 1.9.0 complete: embedding_cache upgraded to BLOB storage");
|
|
597
477
|
} catch (error) {
|
|
598
|
-
|
|
478
|
+
log.error({ err: error }, "Migration 1.9.0 failed");
|
|
599
479
|
throw error;
|
|
600
480
|
}
|
|
601
481
|
}
|
|
602
482
|
if (!currentVersion || versionLessThan(currentVersion, "1.10.0")) {
|
|
603
|
-
|
|
483
|
+
log.info("Running migration 1.10.0: Add tool_config table for runtime tool management");
|
|
604
484
|
try {
|
|
605
485
|
db.exec(`
|
|
606
486
|
CREATE TABLE IF NOT EXISTS tool_config (
|
|
@@ -611,14 +491,14 @@ function runMigrations(db) {
|
|
|
611
491
|
updated_by INTEGER
|
|
612
492
|
);
|
|
613
493
|
`);
|
|
614
|
-
|
|
494
|
+
log.info("Migration 1.10.0 complete: tool_config table created");
|
|
615
495
|
} catch (error) {
|
|
616
|
-
|
|
496
|
+
log.error({ err: error }, "Migration 1.10.0 failed");
|
|
617
497
|
throw error;
|
|
618
498
|
}
|
|
619
499
|
}
|
|
620
500
|
if (!currentVersion || versionLessThan(currentVersion, "1.10.1")) {
|
|
621
|
-
|
|
501
|
+
log.info("Running migration 1.10.1: Fix tool_config scope CHECK constraint (add admin-only)");
|
|
622
502
|
try {
|
|
623
503
|
db.transaction(() => {
|
|
624
504
|
db.exec(`
|
|
@@ -634,14 +514,14 @@ function runMigrations(db) {
|
|
|
634
514
|
ALTER TABLE tool_config_new RENAME TO tool_config;
|
|
635
515
|
`);
|
|
636
516
|
})();
|
|
637
|
-
|
|
517
|
+
log.info("Migration 1.10.1 complete: tool_config CHECK constraint updated");
|
|
638
518
|
} catch (error) {
|
|
639
|
-
|
|
519
|
+
log.error({ err: error }, "Migration 1.10.1 failed");
|
|
640
520
|
throw error;
|
|
641
521
|
}
|
|
642
522
|
}
|
|
643
523
|
if (!currentVersion || versionLessThan(currentVersion, "1.11.0")) {
|
|
644
|
-
|
|
524
|
+
log.info("Running migration 1.11.0: Add tool_index tables for Tool RAG");
|
|
645
525
|
try {
|
|
646
526
|
db.exec(`
|
|
647
527
|
CREATE TABLE IF NOT EXISTS tool_index (
|
|
@@ -673,14 +553,14 @@ function runMigrations(db) {
|
|
|
673
553
|
VALUES (new.rowid, new.search_text, new.name);
|
|
674
554
|
END;
|
|
675
555
|
`);
|
|
676
|
-
|
|
556
|
+
log.info("Migration 1.11.0 complete: tool_index tables created");
|
|
677
557
|
} catch (error) {
|
|
678
|
-
|
|
558
|
+
log.error({ err: error }, "Migration 1.11.0 failed");
|
|
679
559
|
throw error;
|
|
680
560
|
}
|
|
681
561
|
}
|
|
682
562
|
if (!currentVersion || versionLessThan(currentVersion, "1.12.0")) {
|
|
683
|
-
|
|
563
|
+
log.info("Running migration 1.12.0: Add exec_audit table");
|
|
684
564
|
try {
|
|
685
565
|
db.exec(`
|
|
686
566
|
CREATE TABLE IF NOT EXISTS exec_audit (
|
|
@@ -702,34 +582,34 @@ function runMigrations(db) {
|
|
|
702
582
|
CREATE INDEX IF NOT EXISTS idx_exec_audit_timestamp ON exec_audit(timestamp DESC);
|
|
703
583
|
CREATE INDEX IF NOT EXISTS idx_exec_audit_user ON exec_audit(user_id);
|
|
704
584
|
`);
|
|
705
|
-
|
|
585
|
+
log.info("Migration 1.12.0 complete: exec_audit table created");
|
|
706
586
|
} catch (error) {
|
|
707
|
-
|
|
587
|
+
log.error({ err: error }, "Migration 1.12.0 failed");
|
|
708
588
|
throw error;
|
|
709
589
|
}
|
|
710
590
|
}
|
|
711
591
|
if (!currentVersion || versionLessThan(currentVersion, "1.13.0")) {
|
|
712
|
-
|
|
592
|
+
log.info("Running migration 1.13.0: Add token usage columns to sessions");
|
|
713
593
|
try {
|
|
714
594
|
const addColumnIfNotExists = (table, column, type) => {
|
|
715
595
|
try {
|
|
716
596
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
717
|
-
} catch (
|
|
718
|
-
if (!(
|
|
719
|
-
throw
|
|
597
|
+
} catch (error) {
|
|
598
|
+
if (!(error instanceof Error) || !error.message.includes("duplicate column name")) {
|
|
599
|
+
throw error;
|
|
720
600
|
}
|
|
721
601
|
}
|
|
722
602
|
};
|
|
723
603
|
addColumnIfNotExists("sessions", "input_tokens", "INTEGER DEFAULT 0");
|
|
724
604
|
addColumnIfNotExists("sessions", "output_tokens", "INTEGER DEFAULT 0");
|
|
725
|
-
|
|
605
|
+
log.info("Migration 1.13.0 complete: Token usage columns added to sessions");
|
|
726
606
|
} catch (error) {
|
|
727
|
-
|
|
607
|
+
log.error({ err: error }, "Migration 1.13.0 failed");
|
|
728
608
|
throw error;
|
|
729
609
|
}
|
|
730
610
|
}
|
|
731
611
|
if (!currentVersion || versionLessThan(currentVersion, "1.14.0")) {
|
|
732
|
-
|
|
612
|
+
log.info("Running migration 1.14.0: Add plugin_config table for plugin priority");
|
|
733
613
|
try {
|
|
734
614
|
db.exec(`
|
|
735
615
|
CREATE TABLE IF NOT EXISTS plugin_config (
|
|
@@ -738,14 +618,14 @@ function runMigrations(db) {
|
|
|
738
618
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
739
619
|
);
|
|
740
620
|
`);
|
|
741
|
-
|
|
621
|
+
log.info("Migration 1.14.0 complete: plugin_config table created");
|
|
742
622
|
} catch (error) {
|
|
743
|
-
|
|
623
|
+
log.error({ err: error }, "Migration 1.14.0 failed");
|
|
744
624
|
throw error;
|
|
745
625
|
}
|
|
746
626
|
}
|
|
747
627
|
if (!currentVersion || versionLessThan(currentVersion, "1.15.0")) {
|
|
748
|
-
|
|
628
|
+
log.info("Running migration 1.15.0: Add user_hook_config table");
|
|
749
629
|
try {
|
|
750
630
|
db.exec(`
|
|
751
631
|
CREATE TABLE IF NOT EXISTS user_hook_config (
|
|
@@ -754,9 +634,101 @@ function runMigrations(db) {
|
|
|
754
634
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
755
635
|
);
|
|
756
636
|
`);
|
|
757
|
-
|
|
637
|
+
log.info("Migration 1.15.0 complete: user_hook_config table created");
|
|
638
|
+
} catch (error) {
|
|
639
|
+
log.error({ err: error }, "Migration 1.15.0 failed");
|
|
640
|
+
throw error;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (!currentVersion || versionLessThan(currentVersion, "1.16.0")) {
|
|
644
|
+
log.info("Running migration 1.16.0: Fix tg_messages FTS triggers to skip NULL text rows");
|
|
645
|
+
try {
|
|
646
|
+
db.exec(`
|
|
647
|
+
DROP TRIGGER IF EXISTS tg_messages_fts_insert;
|
|
648
|
+
CREATE TRIGGER tg_messages_fts_insert AFTER INSERT ON tg_messages WHEN new.text IS NOT NULL BEGIN
|
|
649
|
+
INSERT INTO tg_messages_fts(rowid, text, id, chat_id, sender_id, timestamp)
|
|
650
|
+
VALUES (new.rowid, new.text, new.id, new.chat_id, new.sender_id, new.timestamp);
|
|
651
|
+
END;
|
|
652
|
+
|
|
653
|
+
DROP TRIGGER IF EXISTS tg_messages_fts_delete;
|
|
654
|
+
CREATE TRIGGER tg_messages_fts_delete AFTER DELETE ON tg_messages WHEN old.text IS NOT NULL BEGIN
|
|
655
|
+
DELETE FROM tg_messages_fts WHERE rowid = old.rowid;
|
|
656
|
+
END;
|
|
657
|
+
|
|
658
|
+
DROP TRIGGER IF EXISTS tg_messages_fts_update;
|
|
659
|
+
CREATE TRIGGER tg_messages_fts_update AFTER UPDATE ON tg_messages WHEN old.text IS NOT NULL OR new.text IS NOT NULL BEGIN
|
|
660
|
+
DELETE FROM tg_messages_fts WHERE rowid = old.rowid;
|
|
661
|
+
INSERT INTO tg_messages_fts(rowid, text, id, chat_id, sender_id, timestamp)
|
|
662
|
+
VALUES (new.rowid, new.text, new.id, new.chat_id, new.sender_id, new.timestamp);
|
|
663
|
+
END;
|
|
664
|
+
`);
|
|
665
|
+
log.info("Migration 1.16.0 complete: tg_messages FTS triggers updated");
|
|
758
666
|
} catch (error) {
|
|
759
|
-
|
|
667
|
+
log.error({ err: error }, "Migration 1.16.0 failed");
|
|
668
|
+
throw error;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
if (!currentVersion || versionLessThan(currentVersion, "1.17.0")) {
|
|
672
|
+
log.info(
|
|
673
|
+
"Running migration 1.17.0: Add importance, access tracking, and lifecycle columns to knowledge"
|
|
674
|
+
);
|
|
675
|
+
try {
|
|
676
|
+
const addColumnIfNotExists = (table, column, type) => {
|
|
677
|
+
try {
|
|
678
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
679
|
+
} catch (error) {
|
|
680
|
+
if (!(error instanceof Error) || !error.message.includes("duplicate column name")) {
|
|
681
|
+
throw error;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
addColumnIfNotExists("knowledge", "importance", "REAL DEFAULT 0.5");
|
|
686
|
+
addColumnIfNotExists("knowledge", "access_count", "INTEGER DEFAULT 0");
|
|
687
|
+
addColumnIfNotExists("knowledge", "last_accessed_at", "INTEGER");
|
|
688
|
+
addColumnIfNotExists("knowledge", "status", "TEXT DEFAULT 'active'");
|
|
689
|
+
addColumnIfNotExists("knowledge", "memory_type", "TEXT DEFAULT 'semantic'");
|
|
690
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_knowledge_status ON knowledge(status)`);
|
|
691
|
+
log.info("Migration 1.17.0 complete: importance/access/lifecycle columns added to knowledge");
|
|
692
|
+
} catch (error) {
|
|
693
|
+
log.error({ err: error }, "Migration 1.17.0 failed");
|
|
694
|
+
throw error;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (!currentVersion || versionLessThan(currentVersion, "1.18.0")) {
|
|
698
|
+
log.info(
|
|
699
|
+
"Running migration 1.18.0: Extend tool_config scope CHECK constraint for new scope values"
|
|
700
|
+
);
|
|
701
|
+
try {
|
|
702
|
+
const tableExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='tool_config'").get();
|
|
703
|
+
if (tableExists) {
|
|
704
|
+
db.transaction(() => {
|
|
705
|
+
db.exec(`
|
|
706
|
+
CREATE TABLE IF NOT EXISTS tool_config_new (
|
|
707
|
+
tool_name TEXT PRIMARY KEY,
|
|
708
|
+
enabled INTEGER NOT NULL DEFAULT 1 CHECK(enabled IN (0, 1)),
|
|
709
|
+
scope TEXT CHECK(scope IN ('always', 'open', 'dm-only', 'group-only', 'admin-only', 'allowlist', 'disabled')),
|
|
710
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
711
|
+
updated_by INTEGER
|
|
712
|
+
);
|
|
713
|
+
INSERT OR IGNORE INTO tool_config_new SELECT * FROM tool_config;
|
|
714
|
+
DROP TABLE tool_config;
|
|
715
|
+
ALTER TABLE tool_config_new RENAME TO tool_config;
|
|
716
|
+
`);
|
|
717
|
+
})();
|
|
718
|
+
} else {
|
|
719
|
+
db.exec(`
|
|
720
|
+
CREATE TABLE IF NOT EXISTS tool_config (
|
|
721
|
+
tool_name TEXT PRIMARY KEY,
|
|
722
|
+
enabled INTEGER NOT NULL DEFAULT 1 CHECK(enabled IN (0, 1)),
|
|
723
|
+
scope TEXT CHECK(scope IN ('always', 'open', 'dm-only', 'group-only', 'admin-only', 'allowlist', 'disabled')),
|
|
724
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
725
|
+
updated_by INTEGER
|
|
726
|
+
);
|
|
727
|
+
`);
|
|
728
|
+
}
|
|
729
|
+
log.info("Migration 1.18.0 complete: tool_config scope CHECK constraint extended");
|
|
730
|
+
} catch (error) {
|
|
731
|
+
log.error({ err: error }, "Migration 1.18.0 failed");
|
|
760
732
|
throw error;
|
|
761
733
|
}
|
|
762
734
|
}
|
|
@@ -764,7 +736,7 @@ function runMigrations(db) {
|
|
|
764
736
|
}
|
|
765
737
|
|
|
766
738
|
// src/memory/database.ts
|
|
767
|
-
var
|
|
739
|
+
var log2 = createLogger("Memory");
|
|
768
740
|
var MemoryDatabase = class {
|
|
769
741
|
db;
|
|
770
742
|
config;
|
|
@@ -772,17 +744,17 @@ var MemoryDatabase = class {
|
|
|
772
744
|
_dimensionsChanged = false;
|
|
773
745
|
constructor(config) {
|
|
774
746
|
this.config = config;
|
|
775
|
-
const dir =
|
|
776
|
-
if (!
|
|
777
|
-
|
|
747
|
+
const dir = dirname(config.path);
|
|
748
|
+
if (!existsSync(dir)) {
|
|
749
|
+
mkdirSync(dir, { recursive: true });
|
|
778
750
|
}
|
|
779
|
-
this.db = new
|
|
780
|
-
verbose: process.env.DEBUG_SQL ? (msg) =>
|
|
751
|
+
this.db = new Database(config.path, {
|
|
752
|
+
verbose: process.env.DEBUG_SQL ? (msg) => log2.debug(String(msg)) : void 0
|
|
781
753
|
});
|
|
782
754
|
try {
|
|
783
|
-
|
|
784
|
-
} catch (
|
|
785
|
-
|
|
755
|
+
chmodSync(config.path, 384);
|
|
756
|
+
} catch (error) {
|
|
757
|
+
log2.warn({ err: error, path: config.path }, "Failed to set DB file permissions to 0o600");
|
|
786
758
|
}
|
|
787
759
|
this.db.pragma("journal_mode = WAL");
|
|
788
760
|
this.db.pragma("synchronous = NORMAL");
|
|
@@ -796,8 +768,8 @@ var MemoryDatabase = class {
|
|
|
796
768
|
let currentVersion = null;
|
|
797
769
|
try {
|
|
798
770
|
currentVersion = getSchemaVersion(this.db);
|
|
799
|
-
} catch (
|
|
800
|
-
|
|
771
|
+
} catch (error) {
|
|
772
|
+
log2.warn({ err: error }, "Could not read schema version, assuming fresh database");
|
|
801
773
|
currentVersion = null;
|
|
802
774
|
}
|
|
803
775
|
if (!currentVersion) {
|
|
@@ -819,16 +791,16 @@ var MemoryDatabase = class {
|
|
|
819
791
|
this._dimensionsChanged = ensureVectorTables(this.db, dims);
|
|
820
792
|
this.vectorReady = true;
|
|
821
793
|
} catch (error) {
|
|
822
|
-
|
|
823
|
-
|
|
794
|
+
log2.warn(`sqlite-vec not available, vector search disabled: ${error.message}`);
|
|
795
|
+
log2.warn("Falling back to keyword-only search");
|
|
824
796
|
this.config.enableVectorSearch = false;
|
|
825
797
|
}
|
|
826
798
|
}
|
|
827
799
|
migrate(from, to) {
|
|
828
|
-
|
|
800
|
+
log2.info(`Migrating database from ${from} to ${to}...`);
|
|
829
801
|
runMigrations(this.db);
|
|
830
802
|
ensureSchema(this.db);
|
|
831
|
-
|
|
803
|
+
log2.info("Migration complete");
|
|
832
804
|
}
|
|
833
805
|
getDb() {
|
|
834
806
|
return this.db;
|
|
@@ -935,160 +907,9 @@ function closeDatabase() {
|
|
|
935
907
|
}
|
|
936
908
|
}
|
|
937
909
|
|
|
938
|
-
// src/memory/agent/knowledge.ts
|
|
939
|
-
import { readFileSync, existsSync as existsSync3, readdirSync, statSync } from "fs";
|
|
940
|
-
import { join as join2 } from "path";
|
|
941
|
-
var KnowledgeIndexer = class {
|
|
942
|
-
constructor(db, workspaceDir, embedder, vectorEnabled) {
|
|
943
|
-
this.db = db;
|
|
944
|
-
this.workspaceDir = workspaceDir;
|
|
945
|
-
this.embedder = embedder;
|
|
946
|
-
this.vectorEnabled = vectorEnabled;
|
|
947
|
-
}
|
|
948
|
-
async indexAll(options) {
|
|
949
|
-
const files = this.listMemoryFiles();
|
|
950
|
-
let indexed = 0;
|
|
951
|
-
let skipped = 0;
|
|
952
|
-
for (const file of files) {
|
|
953
|
-
const wasIndexed = await this.indexFile(file, options?.force);
|
|
954
|
-
if (wasIndexed) {
|
|
955
|
-
indexed++;
|
|
956
|
-
} else {
|
|
957
|
-
skipped++;
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
return { indexed, skipped };
|
|
961
|
-
}
|
|
962
|
-
async indexFile(absPath, force) {
|
|
963
|
-
if (!existsSync3(absPath) || !absPath.endsWith(".md")) {
|
|
964
|
-
return false;
|
|
965
|
-
}
|
|
966
|
-
const content = readFileSync(absPath, "utf-8");
|
|
967
|
-
const relPath = absPath.replace(this.workspaceDir + "/", "");
|
|
968
|
-
const fileHash = hashText(content);
|
|
969
|
-
if (!force) {
|
|
970
|
-
const existing = this.db.prepare(`SELECT hash FROM knowledge WHERE path = ? AND source = 'memory' LIMIT 1`).get(relPath);
|
|
971
|
-
if (existing?.hash === fileHash) {
|
|
972
|
-
return false;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
const chunks = this.chunkMarkdown(content, relPath);
|
|
976
|
-
const texts = chunks.map((c) => c.text);
|
|
977
|
-
const embeddings = await this.embedder.embedBatch(texts);
|
|
978
|
-
this.db.transaction(() => {
|
|
979
|
-
if (this.vectorEnabled) {
|
|
980
|
-
this.db.prepare(
|
|
981
|
-
`DELETE FROM knowledge_vec WHERE id IN (
|
|
982
|
-
SELECT id FROM knowledge WHERE path = ? AND source = 'memory'
|
|
983
|
-
)`
|
|
984
|
-
).run(relPath);
|
|
985
|
-
}
|
|
986
|
-
this.db.prepare(`DELETE FROM knowledge WHERE path = ? AND source = 'memory'`).run(relPath);
|
|
987
|
-
const insert = this.db.prepare(`
|
|
988
|
-
INSERT INTO knowledge (id, source, path, text, embedding, start_line, end_line, hash)
|
|
989
|
-
VALUES (?, 'memory', ?, ?, ?, ?, ?, ?)
|
|
990
|
-
`);
|
|
991
|
-
const insertVec = this.vectorEnabled ? this.db.prepare(`INSERT INTO knowledge_vec (id, embedding) VALUES (?, ?)`) : null;
|
|
992
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
993
|
-
const chunk = chunks[i];
|
|
994
|
-
const embedding = embeddings[i] ?? [];
|
|
995
|
-
insert.run(
|
|
996
|
-
chunk.id,
|
|
997
|
-
chunk.path,
|
|
998
|
-
chunk.text,
|
|
999
|
-
serializeEmbedding(embedding),
|
|
1000
|
-
chunk.startLine,
|
|
1001
|
-
chunk.endLine,
|
|
1002
|
-
fileHash
|
|
1003
|
-
);
|
|
1004
|
-
if (insertVec && embedding.length > 0) {
|
|
1005
|
-
insertVec.run(chunk.id, serializeEmbedding(embedding));
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
})();
|
|
1009
|
-
return true;
|
|
1010
|
-
}
|
|
1011
|
-
listMemoryFiles() {
|
|
1012
|
-
const files = [];
|
|
1013
|
-
const memoryMd = join2(this.workspaceDir, "MEMORY.md");
|
|
1014
|
-
if (existsSync3(memoryMd)) {
|
|
1015
|
-
files.push(memoryMd);
|
|
1016
|
-
}
|
|
1017
|
-
const memoryDir = join2(this.workspaceDir, "memory");
|
|
1018
|
-
if (existsSync3(memoryDir)) {
|
|
1019
|
-
const entries = readdirSync(memoryDir);
|
|
1020
|
-
for (const entry of entries) {
|
|
1021
|
-
const absPath = join2(memoryDir, entry);
|
|
1022
|
-
if (statSync(absPath).isFile() && entry.endsWith(".md")) {
|
|
1023
|
-
files.push(absPath);
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
return files;
|
|
1028
|
-
}
|
|
1029
|
-
/**
|
|
1030
|
-
* Chunk markdown content with structure awareness.
|
|
1031
|
-
* Respects heading boundaries, code blocks, and list groups.
|
|
1032
|
-
* Target: KNOWLEDGE_CHUNK_SIZE chars, hard max: 2x target.
|
|
1033
|
-
*/
|
|
1034
|
-
chunkMarkdown(content, path) {
|
|
1035
|
-
const lines = content.split("\n");
|
|
1036
|
-
const chunks = [];
|
|
1037
|
-
const targetSize = KNOWLEDGE_CHUNK_SIZE;
|
|
1038
|
-
const hardMax = targetSize * 2;
|
|
1039
|
-
let currentChunk = "";
|
|
1040
|
-
let startLine = 1;
|
|
1041
|
-
let currentLine = 1;
|
|
1042
|
-
let inCodeBlock = false;
|
|
1043
|
-
let overlapPrefix = "";
|
|
1044
|
-
const flushChunk = () => {
|
|
1045
|
-
const text = currentChunk.trim();
|
|
1046
|
-
if (text.length > 0) {
|
|
1047
|
-
chunks.push({
|
|
1048
|
-
id: hashText(`${path}:${startLine}:${currentLine - 1}`),
|
|
1049
|
-
source: "memory",
|
|
1050
|
-
path,
|
|
1051
|
-
text,
|
|
1052
|
-
startLine,
|
|
1053
|
-
endLine: currentLine - 1,
|
|
1054
|
-
hash: hashText(text)
|
|
1055
|
-
});
|
|
1056
|
-
const nonEmpty = text.split("\n").filter((l) => l.trim());
|
|
1057
|
-
overlapPrefix = nonEmpty.length > 0 ? nonEmpty.slice(-2).join("\n") + "\n" : "";
|
|
1058
|
-
}
|
|
1059
|
-
currentChunk = overlapPrefix;
|
|
1060
|
-
startLine = currentLine;
|
|
1061
|
-
};
|
|
1062
|
-
for (const line of lines) {
|
|
1063
|
-
if (line.trimStart().startsWith("```")) {
|
|
1064
|
-
inCodeBlock = !inCodeBlock;
|
|
1065
|
-
}
|
|
1066
|
-
if (!inCodeBlock && currentChunk.length >= targetSize) {
|
|
1067
|
-
const isHeading = /^#{1,6}\s/.test(line);
|
|
1068
|
-
const isBlankLine = line.trim() === "";
|
|
1069
|
-
const isHorizontalRule = /^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trim());
|
|
1070
|
-
if (isHeading) {
|
|
1071
|
-
flushChunk();
|
|
1072
|
-
} else if ((isBlankLine || isHorizontalRule) && currentChunk.length >= targetSize) {
|
|
1073
|
-
currentChunk += line + "\n";
|
|
1074
|
-
currentLine++;
|
|
1075
|
-
flushChunk();
|
|
1076
|
-
continue;
|
|
1077
|
-
} else if (currentChunk.length >= hardMax) {
|
|
1078
|
-
flushChunk();
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
currentChunk += line + "\n";
|
|
1082
|
-
currentLine++;
|
|
1083
|
-
}
|
|
1084
|
-
flushChunk();
|
|
1085
|
-
return chunks;
|
|
1086
|
-
}
|
|
1087
|
-
};
|
|
1088
|
-
|
|
1089
910
|
// src/memory/agent/sessions.ts
|
|
1090
911
|
import { randomUUID } from "crypto";
|
|
1091
|
-
var
|
|
912
|
+
var log3 = createLogger("Memory");
|
|
1092
913
|
var SessionStore = class {
|
|
1093
914
|
constructor(db, embedder, vectorEnabled) {
|
|
1094
915
|
this.db = db;
|
|
@@ -1212,9 +1033,9 @@ ${session.summary}`;
|
|
|
1212
1033
|
this.db.prepare(`DELETE FROM knowledge_vec WHERE id = ?`).run(knowledgeId);
|
|
1213
1034
|
this.db.prepare(`INSERT INTO knowledge_vec (id, embedding) VALUES (?, ?)`).run(knowledgeId, embeddingBuffer);
|
|
1214
1035
|
}
|
|
1215
|
-
|
|
1036
|
+
log3.info(`Indexed session ${sessionId} to knowledge base`);
|
|
1216
1037
|
} catch (error) {
|
|
1217
|
-
|
|
1038
|
+
log3.error({ err: error }, "Error indexing session");
|
|
1218
1039
|
}
|
|
1219
1040
|
}
|
|
1220
1041
|
deleteSession(sessionId) {
|
|
@@ -1227,84 +1048,6 @@ ${session.summary}`;
|
|
|
1227
1048
|
}
|
|
1228
1049
|
};
|
|
1229
1050
|
|
|
1230
|
-
// src/memory/feed/messages.ts
|
|
1231
|
-
var MessageStore = class {
|
|
1232
|
-
constructor(db, embedder, vectorEnabled) {
|
|
1233
|
-
this.db = db;
|
|
1234
|
-
this.embedder = embedder;
|
|
1235
|
-
this.vectorEnabled = vectorEnabled;
|
|
1236
|
-
}
|
|
1237
|
-
ensureChat(chatId, isGroup = false) {
|
|
1238
|
-
const existing = this.db.prepare(`SELECT id FROM tg_chats WHERE id = ?`).get(chatId);
|
|
1239
|
-
if (!existing) {
|
|
1240
|
-
this.db.prepare(`INSERT INTO tg_chats (id, type, is_monitored) VALUES (?, ?, 1)`).run(chatId, isGroup ? "group" : "dm");
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
ensureUser(userId) {
|
|
1244
|
-
if (!userId) return;
|
|
1245
|
-
const existing = this.db.prepare(`SELECT id FROM tg_users WHERE id = ?`).get(userId);
|
|
1246
|
-
if (!existing) {
|
|
1247
|
-
this.db.prepare(`INSERT INTO tg_users (id) VALUES (?)`).run(userId);
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
async storeMessage(message) {
|
|
1251
|
-
this.ensureChat(message.chatId);
|
|
1252
|
-
if (message.senderId) {
|
|
1253
|
-
this.ensureUser(message.senderId);
|
|
1254
|
-
}
|
|
1255
|
-
const embedding = this.vectorEnabled && message.text ? await this.embedder.embedQuery(message.text) : [];
|
|
1256
|
-
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1257
|
-
this.db.transaction(() => {
|
|
1258
|
-
this.db.prepare(
|
|
1259
|
-
`
|
|
1260
|
-
INSERT OR REPLACE INTO tg_messages (
|
|
1261
|
-
id, chat_id, sender_id, text, embedding, reply_to_id,
|
|
1262
|
-
is_from_agent, has_media, media_type, timestamp
|
|
1263
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1264
|
-
`
|
|
1265
|
-
).run(
|
|
1266
|
-
message.id,
|
|
1267
|
-
message.chatId,
|
|
1268
|
-
message.senderId,
|
|
1269
|
-
message.text,
|
|
1270
|
-
embeddingBuffer,
|
|
1271
|
-
message.replyToId,
|
|
1272
|
-
message.isFromAgent ? 1 : 0,
|
|
1273
|
-
message.hasMedia ? 1 : 0,
|
|
1274
|
-
message.mediaType,
|
|
1275
|
-
message.timestamp
|
|
1276
|
-
);
|
|
1277
|
-
if (this.vectorEnabled && embedding.length > 0 && message.text) {
|
|
1278
|
-
this.db.prepare(`DELETE FROM tg_messages_vec WHERE id = ?`).run(message.id);
|
|
1279
|
-
this.db.prepare(`INSERT INTO tg_messages_vec (id, embedding) VALUES (?, ?)`).run(message.id, embeddingBuffer);
|
|
1280
|
-
}
|
|
1281
|
-
this.db.prepare(`UPDATE tg_chats SET last_message_at = ?, last_message_id = ? WHERE id = ?`).run(message.timestamp, message.id, message.chatId);
|
|
1282
|
-
})();
|
|
1283
|
-
}
|
|
1284
|
-
getRecentMessages(chatId, limit = 20) {
|
|
1285
|
-
const rows = this.db.prepare(
|
|
1286
|
-
`
|
|
1287
|
-
SELECT id, chat_id, sender_id, text, reply_to_id, is_from_agent, has_media, media_type, timestamp
|
|
1288
|
-
FROM tg_messages
|
|
1289
|
-
WHERE chat_id = ?
|
|
1290
|
-
ORDER BY timestamp DESC
|
|
1291
|
-
LIMIT ?
|
|
1292
|
-
`
|
|
1293
|
-
).all(chatId, limit);
|
|
1294
|
-
return rows.reverse().map((row) => ({
|
|
1295
|
-
id: row.id,
|
|
1296
|
-
chatId: row.chat_id,
|
|
1297
|
-
senderId: row.sender_id,
|
|
1298
|
-
text: row.text,
|
|
1299
|
-
replyToId: row.reply_to_id ?? void 0,
|
|
1300
|
-
isFromAgent: Boolean(row.is_from_agent),
|
|
1301
|
-
hasMedia: Boolean(row.has_media),
|
|
1302
|
-
mediaType: row.media_type ?? void 0,
|
|
1303
|
-
timestamp: row.timestamp
|
|
1304
|
-
}));
|
|
1305
|
-
}
|
|
1306
|
-
};
|
|
1307
|
-
|
|
1308
1051
|
// src/memory/feed/chats.ts
|
|
1309
1052
|
var ChatStore = class {
|
|
1310
1053
|
constructor(db) {
|
|
@@ -1579,7 +1322,7 @@ var UserStore = class {
|
|
|
1579
1322
|
};
|
|
1580
1323
|
|
|
1581
1324
|
// src/memory/search/hybrid.ts
|
|
1582
|
-
var
|
|
1325
|
+
var log4 = createLogger("Memory");
|
|
1583
1326
|
var UNIT_SECONDS = {
|
|
1584
1327
|
hour: SECONDS_PER_HOUR,
|
|
1585
1328
|
day: SECONDS_PER_DAY,
|
|
@@ -1621,7 +1364,26 @@ var HybridSearch = class {
|
|
|
1621
1364
|
const keywordWeight = options.keywordWeight ?? 0.5;
|
|
1622
1365
|
const vectorResults = this.vectorEnabled ? this.vectorSearchKnowledge(queryEmbedding, Math.ceil(limit * 3)) : [];
|
|
1623
1366
|
const keywordResults = this.keywordSearchKnowledge(query, Math.ceil(limit * 3));
|
|
1624
|
-
|
|
1367
|
+
const results = this.mergeResults(
|
|
1368
|
+
vectorResults,
|
|
1369
|
+
keywordResults,
|
|
1370
|
+
vectorWeight,
|
|
1371
|
+
keywordWeight,
|
|
1372
|
+
limit
|
|
1373
|
+
);
|
|
1374
|
+
if (results.length > 0) {
|
|
1375
|
+
const ids = results.map((r) => r.id);
|
|
1376
|
+
setImmediate(() => {
|
|
1377
|
+
try {
|
|
1378
|
+
const ph = ids.map(() => "?").join(", ");
|
|
1379
|
+
this.db.prepare(
|
|
1380
|
+
`UPDATE knowledge SET access_count = access_count + 1, last_accessed_at = unixepoch() WHERE id IN (${ph})`
|
|
1381
|
+
).run(...ids);
|
|
1382
|
+
} catch {
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
return results;
|
|
1625
1387
|
}
|
|
1626
1388
|
async searchMessages(query, queryEmbedding, options = {}) {
|
|
1627
1389
|
const limit = options.limit ?? 10;
|
|
@@ -1647,13 +1409,14 @@ var HybridSearch = class {
|
|
|
1647
1409
|
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1648
1410
|
const rows = this.db.prepare(
|
|
1649
1411
|
`
|
|
1650
|
-
SELECT kv.id, k.text, k.source, kv.distance, k.created_at
|
|
1412
|
+
SELECT kv.id, k.text, k.source, kv.distance, k.created_at, k.importance, k.last_accessed_at
|
|
1651
1413
|
FROM (
|
|
1652
1414
|
SELECT id, distance
|
|
1653
1415
|
FROM knowledge_vec
|
|
1654
1416
|
WHERE embedding MATCH ? AND k = ?
|
|
1655
1417
|
) kv
|
|
1656
1418
|
JOIN knowledge k ON k.id = kv.id
|
|
1419
|
+
WHERE (k.status = 'active' OR k.status IS NULL)
|
|
1657
1420
|
`
|
|
1658
1421
|
).all(embeddingBuffer, limit);
|
|
1659
1422
|
return rows.map((row) => ({
|
|
@@ -1662,10 +1425,12 @@ var HybridSearch = class {
|
|
|
1662
1425
|
source: row.source,
|
|
1663
1426
|
score: 1 - row.distance,
|
|
1664
1427
|
vectorScore: 1 - row.distance,
|
|
1665
|
-
createdAt: row.created_at ?? void 0
|
|
1428
|
+
createdAt: row.created_at ?? void 0,
|
|
1429
|
+
importance: row.importance ?? 0.5,
|
|
1430
|
+
lastAccessedAt: row.last_accessed_at ?? void 0
|
|
1666
1431
|
}));
|
|
1667
1432
|
} catch (error) {
|
|
1668
|
-
|
|
1433
|
+
log4.error({ err: error }, "Vector search error (knowledge)");
|
|
1669
1434
|
return [];
|
|
1670
1435
|
}
|
|
1671
1436
|
}
|
|
@@ -1675,10 +1440,11 @@ var HybridSearch = class {
|
|
|
1675
1440
|
try {
|
|
1676
1441
|
const rows = this.db.prepare(
|
|
1677
1442
|
`
|
|
1678
|
-
SELECT k.id, k.text, k.source, rank as score, k.created_at
|
|
1443
|
+
SELECT k.id, k.text, k.source, rank as score, k.created_at, k.importance, k.last_accessed_at
|
|
1679
1444
|
FROM knowledge_fts kf
|
|
1680
1445
|
JOIN knowledge k ON k.rowid = kf.rowid
|
|
1681
1446
|
WHERE knowledge_fts MATCH ?
|
|
1447
|
+
AND (k.status = 'active' OR k.status IS NULL)
|
|
1682
1448
|
ORDER BY rank
|
|
1683
1449
|
LIMIT ?
|
|
1684
1450
|
`
|
|
@@ -1686,10 +1452,12 @@ var HybridSearch = class {
|
|
|
1686
1452
|
return rows.map((row) => ({
|
|
1687
1453
|
...row,
|
|
1688
1454
|
keywordScore: this.bm25ToScore(row.score),
|
|
1689
|
-
createdAt: row.created_at ?? void 0
|
|
1455
|
+
createdAt: row.created_at ?? void 0,
|
|
1456
|
+
importance: row.importance ?? 0.5,
|
|
1457
|
+
lastAccessedAt: row.last_accessed_at ?? void 0
|
|
1690
1458
|
}));
|
|
1691
1459
|
} catch (error) {
|
|
1692
|
-
|
|
1460
|
+
log4.error({ err: error }, "FTS5 search error (knowledge)");
|
|
1693
1461
|
return [];
|
|
1694
1462
|
}
|
|
1695
1463
|
}
|
|
@@ -1728,7 +1496,7 @@ var HybridSearch = class {
|
|
|
1728
1496
|
createdAt: row.timestamp ?? void 0
|
|
1729
1497
|
}));
|
|
1730
1498
|
} catch (error) {
|
|
1731
|
-
|
|
1499
|
+
log4.error({ err: error }, "Vector search error (messages)");
|
|
1732
1500
|
return [];
|
|
1733
1501
|
}
|
|
1734
1502
|
}
|
|
@@ -1763,7 +1531,7 @@ var HybridSearch = class {
|
|
|
1763
1531
|
createdAt: row.timestamp ?? void 0
|
|
1764
1532
|
}));
|
|
1765
1533
|
} catch (error) {
|
|
1766
|
-
|
|
1534
|
+
log4.error({ err: error }, "FTS5 search error (messages)");
|
|
1767
1535
|
return [];
|
|
1768
1536
|
}
|
|
1769
1537
|
}
|
|
@@ -1784,7 +1552,12 @@ var HybridSearch = class {
|
|
|
1784
1552
|
const now = Math.floor(Date.now() / 1e3);
|
|
1785
1553
|
const results = Array.from(byId.values());
|
|
1786
1554
|
for (const r of results) {
|
|
1787
|
-
if (r.
|
|
1555
|
+
if (r.source !== "message" && r.importance !== void 0) {
|
|
1556
|
+
const refTime = r.lastAccessedAt ?? r.createdAt ?? now;
|
|
1557
|
+
const hoursSince = Math.max(0, (now - refTime) / SECONDS_PER_HOUR);
|
|
1558
|
+
const recency = Math.pow(0.995, hoursSince);
|
|
1559
|
+
r.score = 0.4 * r.score + 0.3 * r.importance + 0.3 * recency;
|
|
1560
|
+
} else if (r.createdAt) {
|
|
1788
1561
|
const ageDays = Math.max(0, (now - r.createdAt) / SECONDS_PER_DAY);
|
|
1789
1562
|
const boost = 1 / (1 + ageDays * RECENCY_DECAY_FACTOR);
|
|
1790
1563
|
r.score *= 1 - RECENCY_WEIGHT + RECENCY_WEIGHT * boost;
|
|
@@ -1802,7 +1575,7 @@ var HybridSearch = class {
|
|
|
1802
1575
|
};
|
|
1803
1576
|
|
|
1804
1577
|
// src/memory/search/context.ts
|
|
1805
|
-
var
|
|
1578
|
+
var log5 = createLogger("Memory");
|
|
1806
1579
|
function reorderForEdges(items) {
|
|
1807
1580
|
if (items.length <= 2) return items;
|
|
1808
1581
|
const result = new Array(items.length);
|
|
@@ -1851,7 +1624,7 @@ var ContextBuilder = class {
|
|
|
1851
1624
|
recentTgMessages.filter((m) => m.text && m.text.length > 0).map((m) => m.text)
|
|
1852
1625
|
);
|
|
1853
1626
|
const knowledgePromise = includeAgentMemory ? this.hybridSearch.searchKnowledge(query, queryEmbedding, { limit: maxRelevantChunks }).catch((error) => {
|
|
1854
|
-
|
|
1627
|
+
log5.warn({ err: error }, "Knowledge search failed");
|
|
1855
1628
|
return [];
|
|
1856
1629
|
}) : Promise.resolve([]);
|
|
1857
1630
|
const { afterTimestamp } = parseTemporalIntent(query);
|
|
@@ -1860,7 +1633,7 @@ var ContextBuilder = class {
|
|
|
1860
1633
|
limit: maxRelevantChunks,
|
|
1861
1634
|
afterTimestamp
|
|
1862
1635
|
}).catch((error) => {
|
|
1863
|
-
|
|
1636
|
+
log5.warn({ err: error }, "Feed search failed");
|
|
1864
1637
|
return [];
|
|
1865
1638
|
}) : Promise.resolve([]);
|
|
1866
1639
|
const [knowledgeResults, feedResults] = await Promise.all([knowledgePromise, feedPromise]);
|
|
@@ -1888,7 +1661,7 @@ var ContextBuilder = class {
|
|
|
1888
1661
|
}
|
|
1889
1662
|
}
|
|
1890
1663
|
} catch (error) {
|
|
1891
|
-
|
|
1664
|
+
log5.warn({ err: error }, "Global feed search failed");
|
|
1892
1665
|
}
|
|
1893
1666
|
}
|
|
1894
1667
|
if (relevantFeed.length === 0 && recentTgMessages.length > 0) {
|
|
@@ -1927,11 +1700,6 @@ function initializeMemory(config) {
|
|
|
1927
1700
|
}
|
|
1928
1701
|
|
|
1929
1702
|
export {
|
|
1930
|
-
JOURNAL_SCHEMA,
|
|
1931
|
-
USED_TRANSACTIONS_SCHEMA,
|
|
1932
|
-
openModuleDb,
|
|
1933
|
-
createDbWrapper,
|
|
1934
|
-
migrateFromMainDb,
|
|
1935
1703
|
ensureSchema,
|
|
1936
1704
|
ensureVectorTables,
|
|
1937
1705
|
getSchemaVersion,
|
|
@@ -1941,9 +1709,7 @@ export {
|
|
|
1941
1709
|
MemoryDatabase,
|
|
1942
1710
|
getDatabase,
|
|
1943
1711
|
closeDatabase,
|
|
1944
|
-
KnowledgeIndexer,
|
|
1945
1712
|
SessionStore,
|
|
1946
|
-
MessageStore,
|
|
1947
1713
|
ChatStore,
|
|
1948
1714
|
UserStore,
|
|
1949
1715
|
parseTemporalIntent,
|