teleton 0.8.5 → 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 +24 -5
- package/dist/{bootstrap-SPDT3XBQ.js → bootstrap-PFBH6ALD.js} +10 -7
- package/dist/bridge-guards-HZTNH7IB.js +9 -0
- package/dist/{chunk-FSL2MOYK.js → chunk-2UUGRY5B.js} +144 -163
- package/dist/{chunk-LM6AL6LN.js → chunk-4MFN75ZK.js} +4449 -2776
- package/dist/{chunk-GUX6ZFVF.js → chunk-4MG2AROG.js} +4 -6
- package/dist/{chunk-6U6VA2OT.js → chunk-6IFNQWIM.js} +7276 -7300
- package/dist/chunk-7KI25UJU.js +215 -0
- package/dist/chunk-AX5NBEHX.js +12 -0
- package/dist/{chunk-7ZXUUDQQ.js → chunk-BLUES3FJ.js} +79 -100
- package/dist/{chunk-KYSAHDYE.js → chunk-BT2I3ETV.js} +1 -1
- package/dist/chunk-CXTZPOTA.js +107 -0
- package/dist/{chunk-4KURCUWD.js → chunk-D3GT6YIY.js} +59 -7
- package/dist/chunk-EKCXKL5M.js +53 -0
- package/dist/{chunk-5K4YDCVU.js → chunk-F6S3L3OV.js} +3 -3
- package/dist/{chunk-L3LPVF4Z.js → chunk-J4WDJ7XS.js} +2 -2
- package/dist/{chunk-Z63KUQX4.js → chunk-JYF2MM5I.js} +120 -110
- package/dist/{chunk-NVKBBTI6.js → chunk-K3QSIIMZ.js} +9 -6
- package/dist/chunk-OMQIAWEU.js +273 -0
- package/dist/chunk-PCT7GYBP.js +274 -0
- package/dist/{chunk-35X3V6OW.js → chunk-QYZBWU2D.js} +5 -5
- 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-2MZP75SH.js → chunk-ULVL2W3D.js} +152 -256
- package/dist/{chunk-NQ6FZKCE.js → chunk-V3S3NXBQ.js} +3 -1
- package/dist/{chunk-H7MFXJZK.js → chunk-WSL4KIOI.js} +31 -26
- package/dist/{chunk-PK3TVFBT.js → chunk-Z5WY7BSB.js} +5 -5
- package/dist/{chunk-LD24DWWE.js → chunk-ZGKE3OTA.js} +110 -47
- package/dist/{chunk-M6M4DCDU.js → chunk-ZHRDETCX.js} +3 -3
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +263 -163
- package/dist/{client-G62EZT6U.js → client-S5UIK6OG.js} +9 -7
- 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-6BLHRCQJ.js → harden-permissions-PV5SGV5D.js} +1 -1
- package/dist/index.d.ts +923 -0
- package/dist/index.js +28 -20
- package/dist/knowledge-RRWUIO3G.js +19 -0
- package/dist/{local-HQ3UJ7KR.js → local-MSZAXWUL.js} +2 -2
- package/dist/mcp-loader-OELDFR63.js +15 -0
- package/dist/{memory-BJH724PQ.js → memory-6U6HGRK2.js} +22 -12
- package/dist/{memory-hook-LUAKTXU5.js → memory-hook-T7Y235KY.js} +7 -7
- package/dist/messages-KV5ADNJB.js +17 -0
- package/dist/{migrate-C4LBLOZH.js → migrate-AX3HOKOO.js} +9 -7
- package/dist/{server-4J56HS62.js → server-MFRYOGHR.js} +20 -16
- package/dist/{server-I6TYJ36S.js → server-SFLCAZFR.js} +220 -19
- package/dist/{setup-server-VJ3MGUSM.js → setup-server-YWAPKZVE.js} +14 -13
- package/dist/{store-2IGAMTES.js → store-PGHQASBC.js} +10 -8
- package/dist/{task-dependency-resolver-CQ432Z7J.js → task-dependency-resolver-YQKADDEU.js} +24 -10
- package/dist/{task-executor-JELRREUV.js → task-executor-LWAWD225.js} +4 -4
- package/dist/{tool-adapter-IVX2XQJE.js → tool-adapter-VKLUZSQS.js} +1 -1
- package/dist/{tool-index-XPCMWBYY.js → tool-index-YEWDF5CK.js} +4 -4
- package/dist/{transcript-OEO3HA4Z.js → transcript-4Y3Z2BJ3.js} +2 -2
- 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-eSR4Qv6s.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 -8
- package/dist/web/assets/index-DmlyQVhR.css +0 -1
- package/dist/web/assets/index-Dn5ZH1Y6.js +0 -80
- package/dist/{chunk-WFTC3JJW.js → chunk-3NO7QU7W.js} +1 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
serializeEmbedding
|
|
3
|
+
} from "./chunk-Z5WY7BSB.js";
|
|
4
|
+
|
|
5
|
+
// src/memory/feed/messages.ts
|
|
6
|
+
function pruneOldMessages(db, maxAgeDays = 90) {
|
|
7
|
+
const cutoffSec = Math.floor(Date.now() / 1e3) - maxAgeDays * 86400;
|
|
8
|
+
const result = db.prepare("DELETE FROM tg_messages WHERE timestamp < ?").run(cutoffSec);
|
|
9
|
+
return result.changes;
|
|
10
|
+
}
|
|
11
|
+
var MessageStore = class {
|
|
12
|
+
constructor(db, embedder, vectorEnabled) {
|
|
13
|
+
this.db = db;
|
|
14
|
+
this.embedder = embedder;
|
|
15
|
+
this.vectorEnabled = vectorEnabled;
|
|
16
|
+
}
|
|
17
|
+
ensureChat(chatId, isGroup = false) {
|
|
18
|
+
const existing = this.db.prepare(`SELECT id FROM tg_chats WHERE id = ?`).get(chatId);
|
|
19
|
+
if (!existing) {
|
|
20
|
+
this.db.prepare(`INSERT INTO tg_chats (id, type, is_monitored) VALUES (?, ?, 1)`).run(chatId, isGroup ? "group" : "dm");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
ensureUser(userId) {
|
|
24
|
+
if (!userId) return;
|
|
25
|
+
const existing = this.db.prepare(`SELECT id FROM tg_users WHERE id = ?`).get(userId);
|
|
26
|
+
if (!existing) {
|
|
27
|
+
this.db.prepare(`INSERT INTO tg_users (id) VALUES (?)`).run(userId);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async storeMessage(message) {
|
|
31
|
+
this.ensureChat(message.chatId);
|
|
32
|
+
if (message.senderId) {
|
|
33
|
+
this.ensureUser(message.senderId);
|
|
34
|
+
}
|
|
35
|
+
const embedding = this.vectorEnabled && message.text ? await this.embedder.embedQuery(message.text) : [];
|
|
36
|
+
const embeddingBuffer = serializeEmbedding(embedding);
|
|
37
|
+
this.db.transaction(() => {
|
|
38
|
+
this.db.prepare(
|
|
39
|
+
`
|
|
40
|
+
INSERT OR REPLACE INTO tg_messages (
|
|
41
|
+
id, chat_id, sender_id, text, embedding, reply_to_id,
|
|
42
|
+
is_from_agent, has_media, media_type, timestamp
|
|
43
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
44
|
+
`
|
|
45
|
+
).run(
|
|
46
|
+
message.id,
|
|
47
|
+
message.chatId,
|
|
48
|
+
message.senderId,
|
|
49
|
+
message.text,
|
|
50
|
+
embeddingBuffer,
|
|
51
|
+
message.replyToId,
|
|
52
|
+
message.isFromAgent ? 1 : 0,
|
|
53
|
+
message.hasMedia ? 1 : 0,
|
|
54
|
+
message.mediaType,
|
|
55
|
+
message.timestamp
|
|
56
|
+
);
|
|
57
|
+
if (this.vectorEnabled && embedding.length > 0 && message.text) {
|
|
58
|
+
this.db.prepare(`DELETE FROM tg_messages_vec WHERE id = ?`).run(message.id);
|
|
59
|
+
this.db.prepare(`INSERT INTO tg_messages_vec (id, embedding) VALUES (?, ?)`).run(message.id, embeddingBuffer);
|
|
60
|
+
}
|
|
61
|
+
this.db.prepare(`UPDATE tg_chats SET last_message_at = ?, last_message_id = ? WHERE id = ?`).run(message.timestamp, message.id, message.chatId);
|
|
62
|
+
})();
|
|
63
|
+
}
|
|
64
|
+
pruneOldMessages(maxAgeDays = 90) {
|
|
65
|
+
const cutoffSec = Math.floor(Date.now() / 1e3) - maxAgeDays * 86400;
|
|
66
|
+
const result = this.db.prepare("DELETE FROM tg_messages WHERE timestamp < ?").run(cutoffSec);
|
|
67
|
+
return result.changes;
|
|
68
|
+
}
|
|
69
|
+
getRecentMessages(chatId, limit = 20) {
|
|
70
|
+
const rows = this.db.prepare(
|
|
71
|
+
`
|
|
72
|
+
SELECT id, chat_id, sender_id, text, reply_to_id, is_from_agent, has_media, media_type, timestamp
|
|
73
|
+
FROM tg_messages
|
|
74
|
+
WHERE chat_id = ?
|
|
75
|
+
ORDER BY timestamp DESC
|
|
76
|
+
LIMIT ?
|
|
77
|
+
`
|
|
78
|
+
).all(chatId, limit);
|
|
79
|
+
return rows.reverse().map((row) => ({
|
|
80
|
+
id: row.id,
|
|
81
|
+
chatId: row.chat_id,
|
|
82
|
+
senderId: row.sender_id,
|
|
83
|
+
text: row.text,
|
|
84
|
+
replyToId: row.reply_to_id ?? void 0,
|
|
85
|
+
isFromAgent: Boolean(row.is_from_agent),
|
|
86
|
+
hasMedia: Boolean(row.has_media),
|
|
87
|
+
mediaType: row.media_type ?? void 0,
|
|
88
|
+
timestamp: row.timestamp
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
pruneOldMessages,
|
|
95
|
+
MessageStore
|
|
96
|
+
};
|
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JOURNAL_SCHEMA
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QYZBWU2D.js";
|
|
4
|
+
import {
|
|
5
|
+
KnowledgeIndexer
|
|
6
|
+
} from "./chunk-OMQIAWEU.js";
|
|
7
|
+
import {
|
|
8
|
+
MessageStore
|
|
9
|
+
} from "./chunk-TTOZCZWE.js";
|
|
4
10
|
import {
|
|
5
11
|
CachedEmbeddingProvider,
|
|
6
12
|
createEmbeddingProvider,
|
|
7
13
|
hashText,
|
|
8
14
|
serializeEmbedding
|
|
9
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-Z5WY7BSB.js";
|
|
10
16
|
import {
|
|
11
17
|
FEED_MESSAGE_MAX_CHARS,
|
|
12
18
|
HYBRID_SEARCH_MIN_SCORE,
|
|
13
|
-
KNOWLEDGE_CHUNK_SIZE,
|
|
14
19
|
RECENCY_DECAY_FACTOR,
|
|
15
20
|
RECENCY_WEIGHT,
|
|
16
21
|
SECONDS_PER_DAY,
|
|
17
22
|
SECONDS_PER_HOUR,
|
|
18
23
|
SQLITE_CACHE_SIZE_KB,
|
|
19
24
|
SQLITE_MMAP_SIZE
|
|
20
|
-
} from "./chunk-
|
|
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
31
|
import Database from "better-sqlite3";
|
|
@@ -246,16 +251,16 @@ function ensureSchema(db) {
|
|
|
246
251
|
);
|
|
247
252
|
|
|
248
253
|
-- FTS triggers for messages
|
|
249
|
-
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
|
|
250
255
|
INSERT INTO tg_messages_fts(rowid, text, id, chat_id, sender_id, timestamp)
|
|
251
256
|
VALUES (new.rowid, new.text, new.id, new.chat_id, new.sender_id, new.timestamp);
|
|
252
257
|
END;
|
|
253
258
|
|
|
254
|
-
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
|
|
255
260
|
DELETE FROM tg_messages_fts WHERE rowid = old.rowid;
|
|
256
261
|
END;
|
|
257
262
|
|
|
258
|
-
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
|
|
259
264
|
DELETE FROM tg_messages_fts WHERE rowid = old.rowid;
|
|
260
265
|
INSERT INTO tg_messages_fts(rowid, text, id, chat_id, sender_id, timestamp)
|
|
261
266
|
VALUES (new.rowid, new.text, new.id, new.chat_id, new.sender_id, new.timestamp);
|
|
@@ -367,7 +372,7 @@ function setSchemaVersion(db, version) {
|
|
|
367
372
|
`
|
|
368
373
|
).run(version);
|
|
369
374
|
}
|
|
370
|
-
var CURRENT_SCHEMA_VERSION = "1.
|
|
375
|
+
var CURRENT_SCHEMA_VERSION = "1.18.0";
|
|
371
376
|
function runMigrations(db) {
|
|
372
377
|
const currentVersion = getSchemaVersion(db);
|
|
373
378
|
if (!currentVersion || versionLessThan(currentVersion, "1.1.0")) {
|
|
@@ -420,9 +425,9 @@ function runMigrations(db) {
|
|
|
420
425
|
const addColumnIfNotExists = (table, column, type) => {
|
|
421
426
|
try {
|
|
422
427
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
423
|
-
} catch (
|
|
424
|
-
if (!(
|
|
425
|
-
throw
|
|
428
|
+
} catch (error) {
|
|
429
|
+
if (!(error instanceof Error) || !error.message.includes("duplicate column name")) {
|
|
430
|
+
throw error;
|
|
426
431
|
}
|
|
427
432
|
}
|
|
428
433
|
};
|
|
@@ -589,9 +594,9 @@ function runMigrations(db) {
|
|
|
589
594
|
const addColumnIfNotExists = (table, column, type) => {
|
|
590
595
|
try {
|
|
591
596
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
592
|
-
} catch (
|
|
593
|
-
if (!(
|
|
594
|
-
throw
|
|
597
|
+
} catch (error) {
|
|
598
|
+
if (!(error instanceof Error) || !error.message.includes("duplicate column name")) {
|
|
599
|
+
throw error;
|
|
595
600
|
}
|
|
596
601
|
}
|
|
597
602
|
};
|
|
@@ -635,6 +640,98 @@ function runMigrations(db) {
|
|
|
635
640
|
throw error;
|
|
636
641
|
}
|
|
637
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");
|
|
666
|
+
} catch (error) {
|
|
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");
|
|
732
|
+
throw error;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
638
735
|
setSchemaVersion(db, CURRENT_SCHEMA_VERSION);
|
|
639
736
|
}
|
|
640
737
|
|
|
@@ -656,8 +753,8 @@ var MemoryDatabase = class {
|
|
|
656
753
|
});
|
|
657
754
|
try {
|
|
658
755
|
chmodSync(config.path, 384);
|
|
659
|
-
} catch (
|
|
660
|
-
log2.warn({ err, path: config.path }, "Failed to set DB file permissions to 0o600");
|
|
756
|
+
} catch (error) {
|
|
757
|
+
log2.warn({ err: error, path: config.path }, "Failed to set DB file permissions to 0o600");
|
|
661
758
|
}
|
|
662
759
|
this.db.pragma("journal_mode = WAL");
|
|
663
760
|
this.db.pragma("synchronous = NORMAL");
|
|
@@ -671,8 +768,8 @@ var MemoryDatabase = class {
|
|
|
671
768
|
let currentVersion = null;
|
|
672
769
|
try {
|
|
673
770
|
currentVersion = getSchemaVersion(this.db);
|
|
674
|
-
} catch (
|
|
675
|
-
log2.warn({ err }, "Could not read schema version, assuming fresh database");
|
|
771
|
+
} catch (error) {
|
|
772
|
+
log2.warn({ err: error }, "Could not read schema version, assuming fresh database");
|
|
676
773
|
currentVersion = null;
|
|
677
774
|
}
|
|
678
775
|
if (!currentVersion) {
|
|
@@ -810,157 +907,6 @@ function closeDatabase() {
|
|
|
810
907
|
}
|
|
811
908
|
}
|
|
812
909
|
|
|
813
|
-
// src/memory/agent/knowledge.ts
|
|
814
|
-
import { readFileSync, existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
815
|
-
import { join } from "path";
|
|
816
|
-
var KnowledgeIndexer = class {
|
|
817
|
-
constructor(db, workspaceDir, embedder, vectorEnabled) {
|
|
818
|
-
this.db = db;
|
|
819
|
-
this.workspaceDir = workspaceDir;
|
|
820
|
-
this.embedder = embedder;
|
|
821
|
-
this.vectorEnabled = vectorEnabled;
|
|
822
|
-
}
|
|
823
|
-
async indexAll(options) {
|
|
824
|
-
const files = this.listMemoryFiles();
|
|
825
|
-
let indexed = 0;
|
|
826
|
-
let skipped = 0;
|
|
827
|
-
for (const file of files) {
|
|
828
|
-
const wasIndexed = await this.indexFile(file, options?.force);
|
|
829
|
-
if (wasIndexed) {
|
|
830
|
-
indexed++;
|
|
831
|
-
} else {
|
|
832
|
-
skipped++;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
return { indexed, skipped };
|
|
836
|
-
}
|
|
837
|
-
async indexFile(absPath, force) {
|
|
838
|
-
if (!existsSync2(absPath) || !absPath.endsWith(".md")) {
|
|
839
|
-
return false;
|
|
840
|
-
}
|
|
841
|
-
const content = readFileSync(absPath, "utf-8");
|
|
842
|
-
const relPath = absPath.replace(this.workspaceDir + "/", "");
|
|
843
|
-
const fileHash = hashText(content);
|
|
844
|
-
if (!force) {
|
|
845
|
-
const existing = this.db.prepare(`SELECT hash FROM knowledge WHERE path = ? AND source = 'memory' LIMIT 1`).get(relPath);
|
|
846
|
-
if (existing?.hash === fileHash) {
|
|
847
|
-
return false;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
const chunks = this.chunkMarkdown(content, relPath);
|
|
851
|
-
const texts = chunks.map((c) => c.text);
|
|
852
|
-
const embeddings = await this.embedder.embedBatch(texts);
|
|
853
|
-
this.db.transaction(() => {
|
|
854
|
-
if (this.vectorEnabled) {
|
|
855
|
-
this.db.prepare(
|
|
856
|
-
`DELETE FROM knowledge_vec WHERE id IN (
|
|
857
|
-
SELECT id FROM knowledge WHERE path = ? AND source = 'memory'
|
|
858
|
-
)`
|
|
859
|
-
).run(relPath);
|
|
860
|
-
}
|
|
861
|
-
this.db.prepare(`DELETE FROM knowledge WHERE path = ? AND source = 'memory'`).run(relPath);
|
|
862
|
-
const insert = this.db.prepare(`
|
|
863
|
-
INSERT INTO knowledge (id, source, path, text, embedding, start_line, end_line, hash)
|
|
864
|
-
VALUES (?, 'memory', ?, ?, ?, ?, ?, ?)
|
|
865
|
-
`);
|
|
866
|
-
const insertVec = this.vectorEnabled ? this.db.prepare(`INSERT INTO knowledge_vec (id, embedding) VALUES (?, ?)`) : null;
|
|
867
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
868
|
-
const chunk = chunks[i];
|
|
869
|
-
const embedding = embeddings[i] ?? [];
|
|
870
|
-
insert.run(
|
|
871
|
-
chunk.id,
|
|
872
|
-
chunk.path,
|
|
873
|
-
chunk.text,
|
|
874
|
-
serializeEmbedding(embedding),
|
|
875
|
-
chunk.startLine,
|
|
876
|
-
chunk.endLine,
|
|
877
|
-
fileHash
|
|
878
|
-
);
|
|
879
|
-
if (insertVec && embedding.length > 0) {
|
|
880
|
-
insertVec.run(chunk.id, serializeEmbedding(embedding));
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
})();
|
|
884
|
-
return true;
|
|
885
|
-
}
|
|
886
|
-
listMemoryFiles() {
|
|
887
|
-
const files = [];
|
|
888
|
-
const memoryMd = join(this.workspaceDir, "MEMORY.md");
|
|
889
|
-
if (existsSync2(memoryMd)) {
|
|
890
|
-
files.push(memoryMd);
|
|
891
|
-
}
|
|
892
|
-
const memoryDir = join(this.workspaceDir, "memory");
|
|
893
|
-
if (existsSync2(memoryDir)) {
|
|
894
|
-
const entries = readdirSync(memoryDir);
|
|
895
|
-
for (const entry of entries) {
|
|
896
|
-
const absPath = join(memoryDir, entry);
|
|
897
|
-
if (statSync(absPath).isFile() && entry.endsWith(".md")) {
|
|
898
|
-
files.push(absPath);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
return files;
|
|
903
|
-
}
|
|
904
|
-
/**
|
|
905
|
-
* Chunk markdown content with structure awareness.
|
|
906
|
-
* Respects heading boundaries, code blocks, and list groups.
|
|
907
|
-
* Target: KNOWLEDGE_CHUNK_SIZE chars, hard max: 2x target.
|
|
908
|
-
*/
|
|
909
|
-
chunkMarkdown(content, path) {
|
|
910
|
-
const lines = content.split("\n");
|
|
911
|
-
const chunks = [];
|
|
912
|
-
const targetSize = KNOWLEDGE_CHUNK_SIZE;
|
|
913
|
-
const hardMax = targetSize * 2;
|
|
914
|
-
let currentChunk = "";
|
|
915
|
-
let startLine = 1;
|
|
916
|
-
let currentLine = 1;
|
|
917
|
-
let inCodeBlock = false;
|
|
918
|
-
let overlapPrefix = "";
|
|
919
|
-
const flushChunk = () => {
|
|
920
|
-
const text = currentChunk.trim();
|
|
921
|
-
if (text.length > 0) {
|
|
922
|
-
chunks.push({
|
|
923
|
-
id: hashText(`${path}:${startLine}:${currentLine - 1}`),
|
|
924
|
-
source: "memory",
|
|
925
|
-
path,
|
|
926
|
-
text,
|
|
927
|
-
startLine,
|
|
928
|
-
endLine: currentLine - 1,
|
|
929
|
-
hash: hashText(text)
|
|
930
|
-
});
|
|
931
|
-
const nonEmpty = text.split("\n").filter((l) => l.trim());
|
|
932
|
-
overlapPrefix = nonEmpty.length > 0 ? nonEmpty.slice(-2).join("\n") + "\n" : "";
|
|
933
|
-
}
|
|
934
|
-
currentChunk = overlapPrefix;
|
|
935
|
-
startLine = currentLine;
|
|
936
|
-
};
|
|
937
|
-
for (const line of lines) {
|
|
938
|
-
if (line.trimStart().startsWith("```")) {
|
|
939
|
-
inCodeBlock = !inCodeBlock;
|
|
940
|
-
}
|
|
941
|
-
if (!inCodeBlock && currentChunk.length >= targetSize) {
|
|
942
|
-
const isHeading = /^#{1,6}\s/.test(line);
|
|
943
|
-
const isBlankLine = line.trim() === "";
|
|
944
|
-
const isHorizontalRule = /^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trim());
|
|
945
|
-
if (isHeading) {
|
|
946
|
-
flushChunk();
|
|
947
|
-
} else if ((isBlankLine || isHorizontalRule) && currentChunk.length >= targetSize) {
|
|
948
|
-
currentChunk += line + "\n";
|
|
949
|
-
currentLine++;
|
|
950
|
-
flushChunk();
|
|
951
|
-
continue;
|
|
952
|
-
} else if (currentChunk.length >= hardMax) {
|
|
953
|
-
flushChunk();
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
currentChunk += line + "\n";
|
|
957
|
-
currentLine++;
|
|
958
|
-
}
|
|
959
|
-
flushChunk();
|
|
960
|
-
return chunks;
|
|
961
|
-
}
|
|
962
|
-
};
|
|
963
|
-
|
|
964
910
|
// src/memory/agent/sessions.ts
|
|
965
911
|
import { randomUUID } from "crypto";
|
|
966
912
|
var log3 = createLogger("Memory");
|
|
@@ -1102,84 +1048,6 @@ ${session.summary}`;
|
|
|
1102
1048
|
}
|
|
1103
1049
|
};
|
|
1104
1050
|
|
|
1105
|
-
// src/memory/feed/messages.ts
|
|
1106
|
-
var MessageStore = class {
|
|
1107
|
-
constructor(db, embedder, vectorEnabled) {
|
|
1108
|
-
this.db = db;
|
|
1109
|
-
this.embedder = embedder;
|
|
1110
|
-
this.vectorEnabled = vectorEnabled;
|
|
1111
|
-
}
|
|
1112
|
-
ensureChat(chatId, isGroup = false) {
|
|
1113
|
-
const existing = this.db.prepare(`SELECT id FROM tg_chats WHERE id = ?`).get(chatId);
|
|
1114
|
-
if (!existing) {
|
|
1115
|
-
this.db.prepare(`INSERT INTO tg_chats (id, type, is_monitored) VALUES (?, ?, 1)`).run(chatId, isGroup ? "group" : "dm");
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
ensureUser(userId) {
|
|
1119
|
-
if (!userId) return;
|
|
1120
|
-
const existing = this.db.prepare(`SELECT id FROM tg_users WHERE id = ?`).get(userId);
|
|
1121
|
-
if (!existing) {
|
|
1122
|
-
this.db.prepare(`INSERT INTO tg_users (id) VALUES (?)`).run(userId);
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
async storeMessage(message) {
|
|
1126
|
-
this.ensureChat(message.chatId);
|
|
1127
|
-
if (message.senderId) {
|
|
1128
|
-
this.ensureUser(message.senderId);
|
|
1129
|
-
}
|
|
1130
|
-
const embedding = this.vectorEnabled && message.text ? await this.embedder.embedQuery(message.text) : [];
|
|
1131
|
-
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1132
|
-
this.db.transaction(() => {
|
|
1133
|
-
this.db.prepare(
|
|
1134
|
-
`
|
|
1135
|
-
INSERT OR REPLACE INTO tg_messages (
|
|
1136
|
-
id, chat_id, sender_id, text, embedding, reply_to_id,
|
|
1137
|
-
is_from_agent, has_media, media_type, timestamp
|
|
1138
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1139
|
-
`
|
|
1140
|
-
).run(
|
|
1141
|
-
message.id,
|
|
1142
|
-
message.chatId,
|
|
1143
|
-
message.senderId,
|
|
1144
|
-
message.text,
|
|
1145
|
-
embeddingBuffer,
|
|
1146
|
-
message.replyToId,
|
|
1147
|
-
message.isFromAgent ? 1 : 0,
|
|
1148
|
-
message.hasMedia ? 1 : 0,
|
|
1149
|
-
message.mediaType,
|
|
1150
|
-
message.timestamp
|
|
1151
|
-
);
|
|
1152
|
-
if (this.vectorEnabled && embedding.length > 0 && message.text) {
|
|
1153
|
-
this.db.prepare(`DELETE FROM tg_messages_vec WHERE id = ?`).run(message.id);
|
|
1154
|
-
this.db.prepare(`INSERT INTO tg_messages_vec (id, embedding) VALUES (?, ?)`).run(message.id, embeddingBuffer);
|
|
1155
|
-
}
|
|
1156
|
-
this.db.prepare(`UPDATE tg_chats SET last_message_at = ?, last_message_id = ? WHERE id = ?`).run(message.timestamp, message.id, message.chatId);
|
|
1157
|
-
})();
|
|
1158
|
-
}
|
|
1159
|
-
getRecentMessages(chatId, limit = 20) {
|
|
1160
|
-
const rows = this.db.prepare(
|
|
1161
|
-
`
|
|
1162
|
-
SELECT id, chat_id, sender_id, text, reply_to_id, is_from_agent, has_media, media_type, timestamp
|
|
1163
|
-
FROM tg_messages
|
|
1164
|
-
WHERE chat_id = ?
|
|
1165
|
-
ORDER BY timestamp DESC
|
|
1166
|
-
LIMIT ?
|
|
1167
|
-
`
|
|
1168
|
-
).all(chatId, limit);
|
|
1169
|
-
return rows.reverse().map((row) => ({
|
|
1170
|
-
id: row.id,
|
|
1171
|
-
chatId: row.chat_id,
|
|
1172
|
-
senderId: row.sender_id,
|
|
1173
|
-
text: row.text,
|
|
1174
|
-
replyToId: row.reply_to_id ?? void 0,
|
|
1175
|
-
isFromAgent: Boolean(row.is_from_agent),
|
|
1176
|
-
hasMedia: Boolean(row.has_media),
|
|
1177
|
-
mediaType: row.media_type ?? void 0,
|
|
1178
|
-
timestamp: row.timestamp
|
|
1179
|
-
}));
|
|
1180
|
-
}
|
|
1181
|
-
};
|
|
1182
|
-
|
|
1183
1051
|
// src/memory/feed/chats.ts
|
|
1184
1052
|
var ChatStore = class {
|
|
1185
1053
|
constructor(db) {
|
|
@@ -1496,7 +1364,26 @@ var HybridSearch = class {
|
|
|
1496
1364
|
const keywordWeight = options.keywordWeight ?? 0.5;
|
|
1497
1365
|
const vectorResults = this.vectorEnabled ? this.vectorSearchKnowledge(queryEmbedding, Math.ceil(limit * 3)) : [];
|
|
1498
1366
|
const keywordResults = this.keywordSearchKnowledge(query, Math.ceil(limit * 3));
|
|
1499
|
-
|
|
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;
|
|
1500
1387
|
}
|
|
1501
1388
|
async searchMessages(query, queryEmbedding, options = {}) {
|
|
1502
1389
|
const limit = options.limit ?? 10;
|
|
@@ -1522,13 +1409,14 @@ var HybridSearch = class {
|
|
|
1522
1409
|
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1523
1410
|
const rows = this.db.prepare(
|
|
1524
1411
|
`
|
|
1525
|
-
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
|
|
1526
1413
|
FROM (
|
|
1527
1414
|
SELECT id, distance
|
|
1528
1415
|
FROM knowledge_vec
|
|
1529
1416
|
WHERE embedding MATCH ? AND k = ?
|
|
1530
1417
|
) kv
|
|
1531
1418
|
JOIN knowledge k ON k.id = kv.id
|
|
1419
|
+
WHERE (k.status = 'active' OR k.status IS NULL)
|
|
1532
1420
|
`
|
|
1533
1421
|
).all(embeddingBuffer, limit);
|
|
1534
1422
|
return rows.map((row) => ({
|
|
@@ -1537,7 +1425,9 @@ var HybridSearch = class {
|
|
|
1537
1425
|
source: row.source,
|
|
1538
1426
|
score: 1 - row.distance,
|
|
1539
1427
|
vectorScore: 1 - row.distance,
|
|
1540
|
-
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
|
|
1541
1431
|
}));
|
|
1542
1432
|
} catch (error) {
|
|
1543
1433
|
log4.error({ err: error }, "Vector search error (knowledge)");
|
|
@@ -1550,10 +1440,11 @@ var HybridSearch = class {
|
|
|
1550
1440
|
try {
|
|
1551
1441
|
const rows = this.db.prepare(
|
|
1552
1442
|
`
|
|
1553
|
-
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
|
|
1554
1444
|
FROM knowledge_fts kf
|
|
1555
1445
|
JOIN knowledge k ON k.rowid = kf.rowid
|
|
1556
1446
|
WHERE knowledge_fts MATCH ?
|
|
1447
|
+
AND (k.status = 'active' OR k.status IS NULL)
|
|
1557
1448
|
ORDER BY rank
|
|
1558
1449
|
LIMIT ?
|
|
1559
1450
|
`
|
|
@@ -1561,7 +1452,9 @@ var HybridSearch = class {
|
|
|
1561
1452
|
return rows.map((row) => ({
|
|
1562
1453
|
...row,
|
|
1563
1454
|
keywordScore: this.bm25ToScore(row.score),
|
|
1564
|
-
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
|
|
1565
1458
|
}));
|
|
1566
1459
|
} catch (error) {
|
|
1567
1460
|
log4.error({ err: error }, "FTS5 search error (knowledge)");
|
|
@@ -1659,7 +1552,12 @@ var HybridSearch = class {
|
|
|
1659
1552
|
const now = Math.floor(Date.now() / 1e3);
|
|
1660
1553
|
const results = Array.from(byId.values());
|
|
1661
1554
|
for (const r of results) {
|
|
1662
|
-
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) {
|
|
1663
1561
|
const ageDays = Math.max(0, (now - r.createdAt) / SECONDS_PER_DAY);
|
|
1664
1562
|
const boost = 1 / (1 + ageDays * RECENCY_DECAY_FACTOR);
|
|
1665
1563
|
r.score *= 1 - RECENCY_WEIGHT + RECENCY_WEIGHT * boost;
|
|
@@ -1811,9 +1709,7 @@ export {
|
|
|
1811
1709
|
MemoryDatabase,
|
|
1812
1710
|
getDatabase,
|
|
1813
1711
|
closeDatabase,
|
|
1814
|
-
KnowledgeIndexer,
|
|
1815
1712
|
SessionStore,
|
|
1816
|
-
MessageStore,
|
|
1817
1713
|
ChatStore,
|
|
1818
1714
|
UserStore,
|
|
1819
1715
|
parseTemporalIntent,
|