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.
Files changed (80) hide show
  1. package/README.md +24 -5
  2. package/dist/{bootstrap-SPDT3XBQ.js → bootstrap-PFBH6ALD.js} +10 -7
  3. package/dist/bridge-guards-HZTNH7IB.js +9 -0
  4. package/dist/{chunk-FSL2MOYK.js → chunk-2UUGRY5B.js} +144 -163
  5. package/dist/{chunk-LM6AL6LN.js → chunk-4MFN75ZK.js} +4449 -2776
  6. package/dist/{chunk-GUX6ZFVF.js → chunk-4MG2AROG.js} +4 -6
  7. package/dist/{chunk-6U6VA2OT.js → chunk-6IFNQWIM.js} +7276 -7300
  8. package/dist/chunk-7KI25UJU.js +215 -0
  9. package/dist/chunk-AX5NBEHX.js +12 -0
  10. package/dist/{chunk-7ZXUUDQQ.js → chunk-BLUES3FJ.js} +79 -100
  11. package/dist/{chunk-KYSAHDYE.js → chunk-BT2I3ETV.js} +1 -1
  12. package/dist/chunk-CXTZPOTA.js +107 -0
  13. package/dist/{chunk-4KURCUWD.js → chunk-D3GT6YIY.js} +59 -7
  14. package/dist/chunk-EKCXKL5M.js +53 -0
  15. package/dist/{chunk-5K4YDCVU.js → chunk-F6S3L3OV.js} +3 -3
  16. package/dist/{chunk-L3LPVF4Z.js → chunk-J4WDJ7XS.js} +2 -2
  17. package/dist/{chunk-Z63KUQX4.js → chunk-JYF2MM5I.js} +120 -110
  18. package/dist/{chunk-NVKBBTI6.js → chunk-K3QSIIMZ.js} +9 -6
  19. package/dist/chunk-OMQIAWEU.js +273 -0
  20. package/dist/chunk-PCT7GYBP.js +274 -0
  21. package/dist/{chunk-35X3V6OW.js → chunk-QYZBWU2D.js} +5 -5
  22. package/dist/{chunk-WTDAICGT.js → chunk-R6W4DJRK.js} +7 -7
  23. package/dist/{chunk-5SEMA47R.js → chunk-RILOEIK6.js} +1 -1
  24. package/dist/{chunk-6OOHHJ4N.js → chunk-TFTNZZDH.js} +20 -20
  25. package/dist/chunk-TTOZCZWE.js +96 -0
  26. package/dist/chunk-UJ54YT2T.js +12 -0
  27. package/dist/{chunk-2MZP75SH.js → chunk-ULVL2W3D.js} +152 -256
  28. package/dist/{chunk-NQ6FZKCE.js → chunk-V3S3NXBQ.js} +3 -1
  29. package/dist/{chunk-H7MFXJZK.js → chunk-WSL4KIOI.js} +31 -26
  30. package/dist/{chunk-PK3TVFBT.js → chunk-Z5WY7BSB.js} +5 -5
  31. package/dist/{chunk-LD24DWWE.js → chunk-ZGKE3OTA.js} +110 -47
  32. package/dist/{chunk-M6M4DCDU.js → chunk-ZHRDETCX.js} +3 -3
  33. package/dist/cli/index.d.ts +2 -0
  34. package/dist/cli/index.js +263 -163
  35. package/dist/{client-G62EZT6U.js → client-S5UIK6OG.js} +9 -7
  36. package/dist/daily-logs-3WXGYAQF.js +25 -0
  37. package/dist/{get-my-gifts-Y7EN7RK4.js → get-my-gifts-3YSYM3LI.js} +3 -2
  38. package/dist/{harden-permissions-6BLHRCQJ.js → harden-permissions-PV5SGV5D.js} +1 -1
  39. package/dist/index.d.ts +923 -0
  40. package/dist/index.js +28 -20
  41. package/dist/knowledge-RRWUIO3G.js +19 -0
  42. package/dist/{local-HQ3UJ7KR.js → local-MSZAXWUL.js} +2 -2
  43. package/dist/mcp-loader-OELDFR63.js +15 -0
  44. package/dist/{memory-BJH724PQ.js → memory-6U6HGRK2.js} +22 -12
  45. package/dist/{memory-hook-LUAKTXU5.js → memory-hook-T7Y235KY.js} +7 -7
  46. package/dist/messages-KV5ADNJB.js +17 -0
  47. package/dist/{migrate-C4LBLOZH.js → migrate-AX3HOKOO.js} +9 -7
  48. package/dist/{server-4J56HS62.js → server-MFRYOGHR.js} +20 -16
  49. package/dist/{server-I6TYJ36S.js → server-SFLCAZFR.js} +220 -19
  50. package/dist/{setup-server-VJ3MGUSM.js → setup-server-YWAPKZVE.js} +14 -13
  51. package/dist/{store-2IGAMTES.js → store-PGHQASBC.js} +10 -8
  52. package/dist/{task-dependency-resolver-CQ432Z7J.js → task-dependency-resolver-YQKADDEU.js} +24 -10
  53. package/dist/{task-executor-JELRREUV.js → task-executor-LWAWD225.js} +4 -4
  54. package/dist/{tool-adapter-IVX2XQJE.js → tool-adapter-VKLUZSQS.js} +1 -1
  55. package/dist/{tool-index-XPCMWBYY.js → tool-index-YEWDF5CK.js} +4 -4
  56. package/dist/{transcript-OEO3HA4Z.js → transcript-4Y3Z2BJ3.js} +2 -2
  57. package/dist/web/assets/Config-MNxA69ib.js +1 -0
  58. package/dist/web/assets/Conversations-Dk958paA.js +1 -0
  59. package/dist/web/assets/Dashboard-dM18fGOm.js +1 -0
  60. package/dist/web/assets/Hooks-D2griQnI.js +1 -0
  61. package/dist/web/assets/Mcp-CtWNzwsz.js +1 -0
  62. package/dist/web/assets/Memory-CfLwH45G.js +1 -0
  63. package/dist/web/assets/Plugins-3hoJprFo.js +1 -0
  64. package/dist/web/assets/SearchInput-CpcETdpE.js +1 -0
  65. package/dist/web/assets/Soul-BSxE73aK.js +1 -0
  66. package/dist/web/assets/Tasks-DkCkfu3A.js +1 -0
  67. package/dist/web/assets/TelegramSettingsPanel-BRzc5G6e.js +1 -0
  68. package/dist/web/assets/Tools-Du8B8Mb4.js +1 -0
  69. package/dist/web/assets/Wallet-BLILP2Gn.js +1 -0
  70. package/dist/web/assets/Workspace-qklcXpXV.js +1 -0
  71. package/dist/web/assets/index-BwEPTTKp.js +90 -0
  72. package/dist/web/assets/index-noejUsK7.css +1 -0
  73. package/dist/web/assets/{index.es-eSR4Qv6s.js → index.es-DdpKlnGb.js} +1 -1
  74. package/dist/web/assets/useToolManager-tdxkKn3H.js +1 -0
  75. package/dist/web/assets/utils-CnsbSMo4.js +1 -0
  76. package/dist/web/index.html +2 -2
  77. package/package.json +7 -8
  78. package/dist/web/assets/index-DmlyQVhR.css +0 -1
  79. package/dist/web/assets/index-Dn5ZH1Y6.js +0 -80
  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
+ };
@@ -0,0 +1,12 @@
1
+ // src/telegram/bridge-guards.ts
2
+ function isBotBridge(bridge) {
3
+ return bridge.getMode() === "bot";
4
+ }
5
+ function isUserBridge(bridge) {
6
+ return bridge.getMode() === "user";
7
+ }
8
+
9
+ export {
10
+ isBotBridge,
11
+ isUserBridge
12
+ };
@@ -1,26 +1,31 @@
1
1
  import {
2
2
  JOURNAL_SCHEMA
3
- } from "./chunk-35X3V6OW.js";
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-PK3TVFBT.js";
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-L3LPVF4Z.js";
25
+ } from "./chunk-J4WDJ7XS.js";
21
26
  import {
22
27
  createLogger
23
- } from "./chunk-NQ6FZKCE.js";
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.15.0";
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 (e) {
424
- if (!(e instanceof Error) || !e.message.includes("duplicate column name")) {
425
- throw e;
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 (e) {
593
- if (!(e instanceof Error) || !e.message.includes("duplicate column name")) {
594
- throw e;
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 (err) {
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 (err) {
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
- return this.mergeResults(vectorResults, keywordResults, vectorWeight, keywordWeight, limit);
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.createdAt) {
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,
@@ -105,7 +105,9 @@ var rootLogger = pino(
105
105
  "*.password",
106
106
  "*.secret",
107
107
  "*.token",
108
- "*.mnemonic"
108
+ "*.mnemonic",
109
+ "bot_token",
110
+ "*.bot_token"
109
111
  ],
110
112
  censor: "[REDACTED]"
111
113
  }