teleton 0.3.0 → 0.5.1

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 (39) hide show
  1. package/README.md +219 -95
  2. package/dist/BigInteger-DQ33LTTE.js +5 -0
  3. package/dist/chunk-4DU3C27M.js +30 -0
  4. package/dist/chunk-5WWR4CU3.js +124 -0
  5. package/dist/{chunk-QQW6KE7Q.js → chunk-BYTHCDZA.js} +282 -263
  6. package/dist/chunk-EHEV7FJ7.js +157 -0
  7. package/dist/{chunk-E2NXSWOS.js → chunk-NUGDTPE4.js} +24 -64
  8. package/dist/{chunk-UYF4TT44.js → chunk-O4R7V5Y2.js} +38 -6
  9. package/dist/chunk-QUAPFI2N.js +42 -0
  10. package/dist/{chunk-ILDG4OPK.js → chunk-RRB6BWU7.js} +9411 -10083
  11. package/dist/chunk-TSKJCWQQ.js +1263 -0
  12. package/dist/{chunk-B2PRMXOH.js → chunk-WL2Q3VRD.js} +0 -2
  13. package/dist/{chunk-OQGNS2FV.js → chunk-YBA6IBGT.js} +20 -5
  14. package/dist/cli/index.js +42 -176
  15. package/dist/endpoint-FLYNEZ2F.js +7 -0
  16. package/dist/format-transactions-FD74HI5N.js +9 -0
  17. package/dist/{get-my-gifts-AFKBG4YQ.js → get-my-gifts-KVULMBJ3.js} +1 -1
  18. package/dist/index.js +13 -11
  19. package/dist/{memory-ZXDAJBL6.js → memory-657W5AS6.js} +4 -5
  20. package/dist/{migrate-7OG67FXP.js → migrate-PMB2JVXH.js} +4 -5
  21. package/dist/server-BQY7CM2N.js +1120 -0
  22. package/dist/{task-dependency-resolver-S45DFI5C.js → task-dependency-resolver-TRPILAHM.js} +4 -4
  23. package/dist/{task-executor-AUTT3VAL.js → task-executor-N7XNVK5N.js} +1 -1
  24. package/dist/{tasks-M3QDPTGY.js → tasks-QSCWSMPS.js} +1 -1
  25. package/dist/{transcript-DF2Y6CFY.js → transcript-7V4UNID4.js} +1 -1
  26. package/dist/web/assets/index-CDMbujHf.css +1 -0
  27. package/dist/web/assets/index-DDX8oQ2z.js +67 -0
  28. package/dist/web/index.html +16 -0
  29. package/dist/web/logo_dark.png +0 -0
  30. package/package.json +23 -4
  31. package/src/templates/IDENTITY.md +1 -1
  32. package/src/templates/MEMORY.md +1 -0
  33. package/src/templates/SECURITY.md +5 -0
  34. package/src/templates/SOUL.md +3 -2
  35. package/dist/chunk-DAMFGHXV.js +0 -74
  36. package/dist/chunk-DUW5VBAZ.js +0 -133
  37. package/dist/chunk-LCMHAUNK.js +0 -62
  38. package/dist/scraper-KXRBQMVQ.js +0 -282
  39. package/dist/timeouts-ZAK6NELA.js +0 -63
@@ -1,30 +1,30 @@
1
1
  import {
2
- VOYAGE_API_URL,
3
- fetchWithTimeout
4
- } from "./chunk-DAMFGHXV.js";
2
+ DEFAULT_FETCH_TIMEOUT_MS
3
+ } from "./chunk-4DU3C27M.js";
5
4
  import {
6
5
  EMBEDDING_CACHE_EVICTION_INTERVAL,
6
+ EMBEDDING_CACHE_EVICTION_RATIO,
7
7
  EMBEDDING_CACHE_MAX_ENTRIES,
8
8
  EMBEDDING_CACHE_TTL_DAYS,
9
- KNOWLEDGE_CHUNK_OVERLAP,
9
+ HYBRID_SEARCH_MIN_SCORE,
10
10
  KNOWLEDGE_CHUNK_SIZE,
11
11
  SQLITE_CACHE_SIZE_KB,
12
12
  SQLITE_MMAP_SIZE,
13
13
  VOYAGE_BATCH_SIZE
14
- } from "./chunk-UYF4TT44.js";
14
+ } from "./chunk-O4R7V5Y2.js";
15
15
  import {
16
16
  TELETON_ROOT
17
17
  } from "./chunk-EYWNOHMJ.js";
18
18
 
19
19
  // src/memory/database.ts
20
20
  import Database2 from "better-sqlite3";
21
- import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
21
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, chmodSync as chmodSync2 } from "fs";
22
22
  import { dirname as dirname2 } from "path";
23
23
  import * as sqliteVec from "sqlite-vec";
24
24
 
25
25
  // src/utils/module-db.ts
26
26
  import Database from "better-sqlite3";
27
- import { existsSync, mkdirSync } from "fs";
27
+ import { existsSync, mkdirSync, chmodSync } from "fs";
28
28
  import { dirname, join } from "path";
29
29
  var JOURNAL_SCHEMA = `
30
30
  CREATE TABLE IF NOT EXISTS journal (
@@ -75,6 +75,10 @@ function openModuleDb(path) {
75
75
  mkdirSync(dir, { recursive: true });
76
76
  }
77
77
  const db = new Database(path);
78
+ try {
79
+ chmodSync(path, 384);
80
+ } catch {
81
+ }
78
82
  db.pragma("journal_mode = WAL");
79
83
  return db;
80
84
  }
@@ -95,6 +99,11 @@ function createDbWrapper(getDb, moduleName) {
95
99
  var MAIN_DB_PATH = join(TELETON_ROOT, "memory.db");
96
100
  function migrateFromMainDb(moduleDb, tables) {
97
101
  let totalMigrated = 0;
102
+ for (const table of tables) {
103
+ if (!/^[a-z_]+$/.test(table)) {
104
+ throw new Error(`Invalid table name for migration: "${table}"`);
105
+ }
106
+ }
98
107
  for (const table of tables) {
99
108
  try {
100
109
  const row = moduleDb.prepare(`SELECT COUNT(*) as c FROM ${table}`).get();
@@ -419,7 +428,7 @@ function setSchemaVersion(db, version) {
419
428
  `
420
429
  ).run(version);
421
430
  }
422
- var CURRENT_SCHEMA_VERSION = "1.9.0";
431
+ var CURRENT_SCHEMA_VERSION = "1.10.1";
423
432
  function runMigrations(db) {
424
433
  const currentVersion = getSchemaVersion(db);
425
434
  if (!currentVersion || versionLessThan(currentVersion, "1.1.0")) {
@@ -526,6 +535,47 @@ function runMigrations(db) {
526
535
  throw error;
527
536
  }
528
537
  }
538
+ if (!currentVersion || versionLessThan(currentVersion, "1.10.0")) {
539
+ console.log("\u{1F504} Running migration 1.10.0: Add tool_config table for runtime tool management");
540
+ try {
541
+ db.exec(`
542
+ CREATE TABLE IF NOT EXISTS tool_config (
543
+ tool_name TEXT PRIMARY KEY,
544
+ enabled INTEGER NOT NULL DEFAULT 1 CHECK(enabled IN (0, 1)),
545
+ scope TEXT CHECK(scope IN ('always', 'dm-only', 'group-only', 'admin-only')),
546
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
547
+ updated_by INTEGER
548
+ );
549
+ `);
550
+ console.log("\u2705 Migration 1.10.0 complete: tool_config table created");
551
+ } catch (error) {
552
+ console.error("\u274C Migration 1.10.0 failed:", error);
553
+ throw error;
554
+ }
555
+ }
556
+ if (!currentVersion || versionLessThan(currentVersion, "1.10.1")) {
557
+ console.log(
558
+ "\u{1F504} Running migration 1.10.1: Fix tool_config scope CHECK constraint (add admin-only)"
559
+ );
560
+ try {
561
+ db.exec(`
562
+ CREATE TABLE IF NOT EXISTS tool_config_new (
563
+ tool_name TEXT PRIMARY KEY,
564
+ enabled INTEGER NOT NULL DEFAULT 1 CHECK(enabled IN (0, 1)),
565
+ scope TEXT CHECK(scope IN ('always', 'dm-only', 'group-only', 'admin-only')),
566
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
567
+ updated_by INTEGER
568
+ );
569
+ INSERT OR IGNORE INTO tool_config_new SELECT * FROM tool_config;
570
+ DROP TABLE tool_config;
571
+ ALTER TABLE tool_config_new RENAME TO tool_config;
572
+ `);
573
+ console.log("\u2705 Migration 1.10.1 complete: tool_config CHECK constraint updated");
574
+ } catch (error) {
575
+ console.error("\u274C Migration 1.10.1 failed:", error);
576
+ throw error;
577
+ }
578
+ }
529
579
  setSchemaVersion(db, CURRENT_SCHEMA_VERSION);
530
580
  }
531
581
 
@@ -543,6 +593,10 @@ var MemoryDatabase = class {
543
593
  this.db = new Database2(config.path, {
544
594
  verbose: process.env.DEBUG_SQL ? console.log : void 0
545
595
  });
596
+ try {
597
+ chmodSync2(config.path, 384);
598
+ } catch {
599
+ }
546
600
  this.db.pragma("journal_mode = WAL");
547
601
  this.db.pragma("synchronous = NORMAL");
548
602
  this.db.pragma(`cache_size = -${SQLITE_CACHE_SIZE_KB}`);
@@ -567,13 +621,12 @@ var MemoryDatabase = class {
567
621
  if (this.config.enableVectorSearch) {
568
622
  this.loadVectorExtension();
569
623
  }
624
+ this.db.exec("ANALYZE");
570
625
  }
571
626
  loadVectorExtension() {
572
627
  try {
573
628
  sqliteVec.load(this.db);
574
- console.log("\u2705 sqlite-vec loaded successfully");
575
- const { vec_version } = this.db.prepare("SELECT vec_version() as vec_version").get();
576
- console.log(` Version: ${vec_version}`);
629
+ this.db.prepare("SELECT vec_version() as vec_version").get();
577
630
  const dims = this.config.vectorDimensions ?? 512;
578
631
  ensureVectorTables(this.db, dims);
579
632
  this.vectorReady = true;
@@ -591,33 +644,18 @@ var MemoryDatabase = class {
591
644
  ensureSchema(this.db);
592
645
  console.log("Migration complete");
593
646
  }
594
- /**
595
- * Get the underlying better-sqlite3 database
596
- */
597
647
  getDb() {
598
648
  return this.db;
599
649
  }
600
- /**
601
- * Check if vector search is available
602
- */
603
650
  isVectorSearchReady() {
604
651
  return this.vectorReady;
605
652
  }
606
- /**
607
- * Get vector dimensions
608
- */
609
653
  getVectorDimensions() {
610
654
  return this.config.vectorDimensions;
611
655
  }
612
- /**
613
- * Execute a function in a transaction
614
- */
615
656
  transaction(fn) {
616
657
  return this.db.transaction(fn)();
617
658
  }
618
- /**
619
- * Execute an async function in a transaction
620
- */
621
659
  async asyncTransaction(fn) {
622
660
  const beginTrans = this.db.prepare("BEGIN");
623
661
  const commitTrans = this.db.prepare("COMMIT");
@@ -632,9 +670,6 @@ var MemoryDatabase = class {
632
670
  throw error;
633
671
  }
634
672
  }
635
- /**
636
- * Get database stats
637
- */
638
673
  getStats() {
639
674
  const knowledge = this.db.prepare(`SELECT COUNT(*) as c FROM knowledge`).get();
640
675
  const sessions = this.db.prepare(`SELECT COUNT(*) as c FROM sessions`).get();
@@ -654,21 +689,15 @@ var MemoryDatabase = class {
654
689
  vectorSearchEnabled: this.vectorReady
655
690
  };
656
691
  }
657
- /**
658
- * Vacuum the database to reclaim space
659
- */
660
692
  vacuum() {
661
693
  this.db.exec("VACUUM");
662
694
  }
663
- /**
664
- * Optimize the database (ANALYZE)
665
- */
666
695
  optimize() {
667
696
  this.db.exec("ANALYZE");
668
697
  }
669
698
  /**
670
- * Rebuild FTS indexes from existing data
671
- * Call this if FTS triggers didn't fire correctly
699
+ * Rebuild FTS indexes from existing data.
700
+ * Call this if FTS triggers didn't fire correctly.
672
701
  */
673
702
  rebuildFtsIndexes() {
674
703
  this.db.exec(`DELETE FROM knowledge_fts`);
@@ -691,9 +720,6 @@ var MemoryDatabase = class {
691
720
  }
692
721
  return { knowledge: knowledgeRows.length, messages: messageRows.length };
693
722
  }
694
- /**
695
- * Close the database connection
696
- */
697
723
  close() {
698
724
  if (this.db.open) {
699
725
  this.db.close();
@@ -733,6 +759,63 @@ var NoopEmbeddingProvider = class {
733
759
  }
734
760
  };
735
761
 
762
+ // src/utils/fetch.ts
763
+ var DEFAULT_TIMEOUT_MS = DEFAULT_FETCH_TIMEOUT_MS;
764
+ function fetchWithTimeout(url, init) {
765
+ const { timeoutMs = DEFAULT_TIMEOUT_MS, ...fetchInit } = init ?? {};
766
+ if (fetchInit.signal) {
767
+ return fetch(url, fetchInit);
768
+ }
769
+ return fetch(url, {
770
+ ...fetchInit,
771
+ signal: AbortSignal.timeout(timeoutMs)
772
+ });
773
+ }
774
+
775
+ // src/constants/api-endpoints.ts
776
+ var TONAPI_BASE_URL = "https://tonapi.io/v2";
777
+ var _tonapiKey;
778
+ function setTonapiKey(key) {
779
+ _tonapiKey = key;
780
+ }
781
+ function tonapiHeaders() {
782
+ const headers = { Accept: "application/json" };
783
+ if (_tonapiKey) {
784
+ headers["Authorization"] = `Bearer ${_tonapiKey}`;
785
+ }
786
+ return headers;
787
+ }
788
+ var TONAPI_MAX_RPS = 5;
789
+ var _tonapiTimestamps = [];
790
+ async function waitForTonapiSlot() {
791
+ const clean = () => {
792
+ const cutoff = Date.now() - 1e3;
793
+ while (_tonapiTimestamps.length > 0 && _tonapiTimestamps[0] <= cutoff) {
794
+ _tonapiTimestamps.shift();
795
+ }
796
+ };
797
+ clean();
798
+ if (_tonapiTimestamps.length >= TONAPI_MAX_RPS) {
799
+ const waitMs = _tonapiTimestamps[0] + 1e3 - Date.now() + 50;
800
+ if (waitMs > 0) await new Promise((r) => setTimeout(r, waitMs));
801
+ clean();
802
+ }
803
+ _tonapiTimestamps.push(Date.now());
804
+ }
805
+ async function tonapiFetch(path, init) {
806
+ await waitForTonapiSlot();
807
+ return fetchWithTimeout(`${TONAPI_BASE_URL}${path}`, {
808
+ ...init,
809
+ headers: { ...tonapiHeaders(), ...init?.headers }
810
+ });
811
+ }
812
+ var STONFI_API_BASE_URL = "https://api.ston.fi/v1";
813
+ var GECKOTERMINAL_API_URL = "https://api.geckoterminal.com/api/v2";
814
+ var COINGECKO_API_URL = "https://api.coingecko.com/api/v3";
815
+ var OPENAI_TTS_URL = "https://api.openai.com/v1/audio/speech";
816
+ var ELEVENLABS_TTS_URL = "https://api.elevenlabs.io/v1/text-to-speech";
817
+ var VOYAGE_API_URL = "https://api.voyageai.com/v1";
818
+
736
819
  // src/memory/embeddings/anthropic.ts
737
820
  var AnthropicEmbeddingProvider = class {
738
821
  id = "anthropic";
@@ -801,6 +884,10 @@ function getExtractor(model) {
801
884
  }).then((ext) => {
802
885
  console.log(`\u2705 Local embedding model ready`);
803
886
  return ext;
887
+ }).catch((err) => {
888
+ console.error(`\u274C Failed to load embedding model: ${err.message}`);
889
+ extractorPromise = null;
890
+ throw err;
804
891
  });
805
892
  }
806
893
  return extractorPromise;
@@ -933,7 +1020,7 @@ var CachedEmbeddingProvider = class {
933
1020
  this.db.prepare(`DELETE FROM embedding_cache WHERE accessed_at < ?`).run(cutoff);
934
1021
  const count = this.db.prepare(`SELECT COUNT(*) as cnt FROM embedding_cache`).get().cnt;
935
1022
  if (count > EMBEDDING_CACHE_MAX_ENTRIES) {
936
- const toDelete = Math.ceil(count * 0.1);
1023
+ const toDelete = Math.ceil(count * EMBEDDING_CACHE_EVICTION_RATIO);
937
1024
  this.db.prepare(
938
1025
  `DELETE FROM embedding_cache WHERE (hash, model, provider) IN (
939
1026
  SELECT hash, model, provider FROM embedding_cache ORDER BY accessed_at ASC LIMIT ?
@@ -996,9 +1083,6 @@ var KnowledgeIndexer = class {
996
1083
  this.embedder = embedder;
997
1084
  this.vectorEnabled = vectorEnabled;
998
1085
  }
999
- /**
1000
- * Index all memory files
1001
- */
1002
1086
  async indexAll() {
1003
1087
  const files = this.listMemoryFiles();
1004
1088
  let indexed = 0;
@@ -1013,9 +1097,6 @@ var KnowledgeIndexer = class {
1013
1097
  }
1014
1098
  return { indexed, skipped };
1015
1099
  }
1016
- /**
1017
- * Index a single file
1018
- */
1019
1100
  async indexFile(absPath) {
1020
1101
  if (!existsSync3(absPath) || !absPath.endsWith(".md")) {
1021
1102
  return false;
@@ -1027,43 +1108,42 @@ var KnowledgeIndexer = class {
1027
1108
  if (existing?.hash === fileHash) {
1028
1109
  return false;
1029
1110
  }
1030
- if (this.vectorEnabled) {
1031
- this.db.prepare(
1032
- `DELETE FROM knowledge_vec WHERE id IN (
1033
- SELECT id FROM knowledge WHERE path = ? AND source = 'memory'
1034
- )`
1035
- ).run(relPath);
1036
- }
1037
- this.db.prepare(`DELETE FROM knowledge WHERE path = ? AND source = 'memory'`).run(relPath);
1038
1111
  const chunks = this.chunkMarkdown(content, relPath);
1039
1112
  const texts = chunks.map((c) => c.text);
1040
1113
  const embeddings = await this.embedder.embedBatch(texts);
1041
- const insert = this.db.prepare(`
1042
- INSERT INTO knowledge (id, source, path, text, embedding, start_line, end_line, hash)
1043
- VALUES (?, 'memory', ?, ?, ?, ?, ?, ?)
1044
- `);
1045
- const insertVec = this.vectorEnabled ? this.db.prepare(`INSERT INTO knowledge_vec (id, embedding) VALUES (?, ?)`) : null;
1046
- for (let i = 0; i < chunks.length; i++) {
1047
- const chunk = chunks[i];
1048
- const embedding = embeddings[i] ?? [];
1049
- insert.run(
1050
- chunk.id,
1051
- chunk.path,
1052
- chunk.text,
1053
- serializeEmbedding(embedding),
1054
- chunk.startLine,
1055
- chunk.endLine,
1056
- chunk.hash
1057
- );
1058
- if (insertVec && embedding.length > 0) {
1059
- insertVec.run(chunk.id, serializeEmbedding(embedding));
1114
+ this.db.transaction(() => {
1115
+ if (this.vectorEnabled) {
1116
+ this.db.prepare(
1117
+ `DELETE FROM knowledge_vec WHERE id IN (
1118
+ SELECT id FROM knowledge WHERE path = ? AND source = 'memory'
1119
+ )`
1120
+ ).run(relPath);
1060
1121
  }
1061
- }
1122
+ this.db.prepare(`DELETE FROM knowledge WHERE path = ? AND source = 'memory'`).run(relPath);
1123
+ const insert = this.db.prepare(`
1124
+ INSERT INTO knowledge (id, source, path, text, embedding, start_line, end_line, hash)
1125
+ VALUES (?, 'memory', ?, ?, ?, ?, ?, ?)
1126
+ `);
1127
+ const insertVec = this.vectorEnabled ? this.db.prepare(`INSERT INTO knowledge_vec (id, embedding) VALUES (?, ?)`) : null;
1128
+ for (let i = 0; i < chunks.length; i++) {
1129
+ const chunk = chunks[i];
1130
+ const embedding = embeddings[i] ?? [];
1131
+ insert.run(
1132
+ chunk.id,
1133
+ chunk.path,
1134
+ chunk.text,
1135
+ serializeEmbedding(embedding),
1136
+ chunk.startLine,
1137
+ chunk.endLine,
1138
+ chunk.hash
1139
+ );
1140
+ if (insertVec && embedding.length > 0) {
1141
+ insertVec.run(chunk.id, serializeEmbedding(embedding));
1142
+ }
1143
+ }
1144
+ })();
1062
1145
  return true;
1063
1146
  }
1064
- /**
1065
- * List all memory files
1066
- */
1067
1147
  listMemoryFiles() {
1068
1148
  const files = [];
1069
1149
  const memoryMd = join2(this.workspaceDir, "MEMORY.md");
@@ -1083,49 +1163,58 @@ var KnowledgeIndexer = class {
1083
1163
  return files;
1084
1164
  }
1085
1165
  /**
1086
- * Chunk markdown content
1166
+ * Chunk markdown content with structure awareness.
1167
+ * Respects heading boundaries, code blocks, and list groups.
1168
+ * Target: KNOWLEDGE_CHUNK_SIZE chars, hard max: 2x target.
1087
1169
  */
1088
1170
  chunkMarkdown(content, path) {
1089
1171
  const lines = content.split("\n");
1090
1172
  const chunks = [];
1091
- const chunkSize = KNOWLEDGE_CHUNK_SIZE;
1092
- const overlap = KNOWLEDGE_CHUNK_OVERLAP;
1173
+ const targetSize = KNOWLEDGE_CHUNK_SIZE;
1174
+ const hardMax = targetSize * 2;
1093
1175
  let currentChunk = "";
1094
1176
  let startLine = 1;
1095
1177
  let currentLine = 1;
1178
+ let inCodeBlock = false;
1179
+ const flushChunk = () => {
1180
+ const text = currentChunk.trim();
1181
+ if (text.length > 0) {
1182
+ chunks.push({
1183
+ id: hashText(`${path}:${startLine}:${currentLine - 1}`),
1184
+ source: "memory",
1185
+ path,
1186
+ text,
1187
+ startLine,
1188
+ endLine: currentLine - 1,
1189
+ hash: hashText(text)
1190
+ });
1191
+ }
1192
+ currentChunk = "";
1193
+ startLine = currentLine;
1194
+ };
1096
1195
  for (const line of lines) {
1097
- currentChunk += line + "\n";
1098
- if (currentChunk.length >= chunkSize) {
1099
- const text2 = currentChunk.trim();
1100
- if (text2.length > 0) {
1101
- chunks.push({
1102
- id: hashText(`${path}:${startLine}:${currentLine}`),
1103
- source: "memory",
1104
- path,
1105
- text: text2,
1106
- startLine,
1107
- endLine: currentLine,
1108
- hash: hashText(text2)
1109
- });
1196
+ if (line.trimStart().startsWith("```")) {
1197
+ inCodeBlock = !inCodeBlock;
1198
+ }
1199
+ if (!inCodeBlock && currentChunk.length >= targetSize) {
1200
+ const isHeading = /^#{1,6}\s/.test(line);
1201
+ const isBlankLine = line.trim() === "";
1202
+ const isHorizontalRule = /^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trim());
1203
+ if (isHeading) {
1204
+ flushChunk();
1205
+ } else if ((isBlankLine || isHorizontalRule) && currentChunk.length >= targetSize) {
1206
+ currentChunk += line + "\n";
1207
+ currentLine++;
1208
+ flushChunk();
1209
+ continue;
1210
+ } else if (currentChunk.length >= hardMax) {
1211
+ flushChunk();
1110
1212
  }
1111
- const overlapText = currentChunk.slice(-overlap);
1112
- currentChunk = overlapText;
1113
- startLine = currentLine + 1;
1114
1213
  }
1214
+ currentChunk += line + "\n";
1115
1215
  currentLine++;
1116
1216
  }
1117
- const text = currentChunk.trim();
1118
- if (text.length > 0) {
1119
- chunks.push({
1120
- id: hashText(`${path}:${startLine}:${currentLine}`),
1121
- source: "memory",
1122
- path,
1123
- text,
1124
- startLine,
1125
- endLine: currentLine,
1126
- hash: hashText(text)
1127
- });
1128
- }
1217
+ flushChunk();
1129
1218
  return chunks;
1130
1219
  }
1131
1220
  };
@@ -1138,9 +1227,6 @@ var SessionStore = class {
1138
1227
  this.embedder = embedder;
1139
1228
  this.vectorEnabled = vectorEnabled;
1140
1229
  }
1141
- /**
1142
- * Create a new session
1143
- */
1144
1230
  createSession(chatId) {
1145
1231
  const id = randomUUID();
1146
1232
  const now = Math.floor(Date.now() / 1e3);
@@ -1158,9 +1244,6 @@ var SessionStore = class {
1158
1244
  tokensUsed: 0
1159
1245
  };
1160
1246
  }
1161
- /**
1162
- * End a session with summary
1163
- */
1164
1247
  endSession(sessionId, summary, tokensUsed = 0) {
1165
1248
  const now = Math.floor(Date.now() / 1e3);
1166
1249
  this.db.prepare(
@@ -1171,9 +1254,6 @@ var SessionStore = class {
1171
1254
  `
1172
1255
  ).run(now, summary, tokensUsed, sessionId);
1173
1256
  }
1174
- /**
1175
- * Update message count for a session
1176
- */
1177
1257
  incrementMessageCount(sessionId, count = 1) {
1178
1258
  this.db.prepare(
1179
1259
  `
@@ -1183,9 +1263,6 @@ var SessionStore = class {
1183
1263
  `
1184
1264
  ).run(count, sessionId);
1185
1265
  }
1186
- /**
1187
- * Get a session by ID
1188
- */
1189
1266
  getSession(id) {
1190
1267
  const row = this.db.prepare(`SELECT * FROM sessions WHERE id = ?`).get(id);
1191
1268
  if (!row) return void 0;
@@ -1194,14 +1271,11 @@ var SessionStore = class {
1194
1271
  chatId: row.chat_id,
1195
1272
  startedAt: new Date(row.started_at * 1e3),
1196
1273
  endedAt: row.ended_at ? new Date(row.ended_at * 1e3) : void 0,
1197
- summary: row.summary,
1274
+ summary: row.summary ?? void 0,
1198
1275
  messageCount: row.message_count,
1199
1276
  tokensUsed: row.tokens_used
1200
1277
  };
1201
1278
  }
1202
- /**
1203
- * Get active (not ended) sessions
1204
- */
1205
1279
  getActiveSessions() {
1206
1280
  const rows = this.db.prepare(
1207
1281
  `
@@ -1215,14 +1289,11 @@ var SessionStore = class {
1215
1289
  chatId: row.chat_id,
1216
1290
  startedAt: new Date(row.started_at * 1e3),
1217
1291
  endedAt: void 0,
1218
- summary: row.summary,
1292
+ summary: row.summary ?? void 0,
1219
1293
  messageCount: row.message_count,
1220
1294
  tokensUsed: row.tokens_used
1221
1295
  }));
1222
1296
  }
1223
- /**
1224
- * Get sessions for a specific chat
1225
- */
1226
1297
  getSessionsByChat(chatId, limit = 50) {
1227
1298
  const rows = this.db.prepare(
1228
1299
  `
@@ -1237,14 +1308,14 @@ var SessionStore = class {
1237
1308
  chatId: row.chat_id,
1238
1309
  startedAt: new Date(row.started_at * 1e3),
1239
1310
  endedAt: row.ended_at ? new Date(row.ended_at * 1e3) : void 0,
1240
- summary: row.summary,
1311
+ summary: row.summary ?? void 0,
1241
1312
  messageCount: row.message_count,
1242
1313
  tokensUsed: row.tokens_used
1243
1314
  }));
1244
1315
  }
1245
1316
  /**
1246
- * Index a session for search (after ending)
1247
- * This creates a knowledge entry from the session summary
1317
+ * Index a session for search after ending.
1318
+ * Creates a knowledge entry from the session summary for future retrieval.
1248
1319
  */
1249
1320
  async indexSession(sessionId) {
1250
1321
  const session = this.getSession(sessionId);
@@ -1271,22 +1342,14 @@ ${session.summary}`;
1271
1342
  if (embedding && this.vectorEnabled) {
1272
1343
  const embeddingBuffer = serializeEmbedding(embedding);
1273
1344
  const rowid = this.db.prepare(`SELECT rowid FROM knowledge WHERE id = ?`).get(knowledgeId);
1274
- this.db.prepare(
1275
- `
1276
- INSERT INTO knowledge_vec (rowid, embedding)
1277
- VALUES (?, ?)
1278
- ON CONFLICT(rowid) DO UPDATE SET embedding = excluded.embedding
1279
- `
1280
- ).run(rowid.rowid, embeddingBuffer);
1345
+ this.db.prepare(`DELETE FROM knowledge_vec WHERE rowid = ?`).run(rowid.rowid);
1346
+ this.db.prepare(`INSERT INTO knowledge_vec (rowid, embedding) VALUES (?, ?)`).run(rowid.rowid, embeddingBuffer);
1281
1347
  }
1282
1348
  console.log(`Indexed session ${sessionId} to knowledge base`);
1283
1349
  } catch (error) {
1284
1350
  console.error("Error indexing session:", error);
1285
1351
  }
1286
1352
  }
1287
- /**
1288
- * Delete a session
1289
- */
1290
1353
  deleteSession(sessionId) {
1291
1354
  const knowledgeId = `session:${sessionId}`;
1292
1355
  if (this.vectorEnabled) {
@@ -1304,18 +1367,12 @@ var MessageStore = class {
1304
1367
  this.embedder = embedder;
1305
1368
  this.vectorEnabled = vectorEnabled;
1306
1369
  }
1307
- /**
1308
- * Ensure chat exists in database
1309
- */
1310
1370
  ensureChat(chatId, isGroup = false) {
1311
1371
  const existing = this.db.prepare(`SELECT id FROM tg_chats WHERE id = ?`).get(chatId);
1312
1372
  if (!existing) {
1313
1373
  this.db.prepare(`INSERT INTO tg_chats (id, type, is_monitored) VALUES (?, ?, 1)`).run(chatId, isGroup ? "group" : "dm");
1314
1374
  }
1315
1375
  }
1316
- /**
1317
- * Ensure user exists in database
1318
- */
1319
1376
  ensureUser(userId) {
1320
1377
  if (!userId) return;
1321
1378
  const existing = this.db.prepare(`SELECT id FROM tg_users WHERE id = ?`).get(userId);
@@ -1323,42 +1380,40 @@ var MessageStore = class {
1323
1380
  this.db.prepare(`INSERT INTO tg_users (id) VALUES (?)`).run(userId);
1324
1381
  }
1325
1382
  }
1326
- /**
1327
- * Store a message
1328
- */
1329
1383
  async storeMessage(message) {
1330
1384
  this.ensureChat(message.chatId);
1331
1385
  if (message.senderId) {
1332
1386
  this.ensureUser(message.senderId);
1333
1387
  }
1334
- const embedding = message.text ? await this.embedder.embedQuery(message.text) : [];
1335
- this.db.prepare(
1388
+ const embedding = this.vectorEnabled && message.text ? await this.embedder.embedQuery(message.text) : [];
1389
+ const embeddingBuffer = serializeEmbedding(embedding);
1390
+ this.db.transaction(() => {
1391
+ this.db.prepare(
1392
+ `
1393
+ INSERT OR REPLACE INTO tg_messages (
1394
+ id, chat_id, sender_id, text, embedding, reply_to_id,
1395
+ is_from_agent, has_media, media_type, timestamp
1396
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1336
1397
  `
1337
- INSERT OR REPLACE INTO tg_messages (
1338
- id, chat_id, sender_id, text, embedding, reply_to_id,
1339
- is_from_agent, has_media, media_type, timestamp
1340
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1341
- `
1342
- ).run(
1343
- message.id,
1344
- message.chatId,
1345
- message.senderId,
1346
- message.text,
1347
- serializeEmbedding(embedding),
1348
- message.replyToId,
1349
- message.isFromAgent ? 1 : 0,
1350
- message.hasMedia ? 1 : 0,
1351
- message.mediaType,
1352
- message.timestamp
1353
- );
1354
- if (this.vectorEnabled && embedding.length > 0 && message.text) {
1355
- this.db.prepare(`INSERT OR REPLACE INTO tg_messages_vec (id, embedding) VALUES (?, ?)`).run(message.id, serializeEmbedding(embedding));
1356
- }
1357
- this.db.prepare(`UPDATE tg_chats SET last_message_at = ?, last_message_id = ? WHERE id = ?`).run(message.timestamp, message.id, message.chatId);
1398
+ ).run(
1399
+ message.id,
1400
+ message.chatId,
1401
+ message.senderId,
1402
+ message.text,
1403
+ embeddingBuffer,
1404
+ message.replyToId,
1405
+ message.isFromAgent ? 1 : 0,
1406
+ message.hasMedia ? 1 : 0,
1407
+ message.mediaType,
1408
+ message.timestamp
1409
+ );
1410
+ if (this.vectorEnabled && embedding.length > 0 && message.text) {
1411
+ this.db.prepare(`DELETE FROM tg_messages_vec WHERE id = ?`).run(message.id);
1412
+ this.db.prepare(`INSERT INTO tg_messages_vec (id, embedding) VALUES (?, ?)`).run(message.id, embeddingBuffer);
1413
+ }
1414
+ this.db.prepare(`UPDATE tg_chats SET last_message_at = ?, last_message_id = ? WHERE id = ?`).run(message.timestamp, message.id, message.chatId);
1415
+ })();
1358
1416
  }
1359
- /**
1360
- * Get recent messages from a chat
1361
- */
1362
1417
  getRecentMessages(chatId, limit = 20) {
1363
1418
  const rows = this.db.prepare(
1364
1419
  `
@@ -1388,9 +1443,6 @@ var ChatStore = class {
1388
1443
  constructor(db) {
1389
1444
  this.db = db;
1390
1445
  }
1391
- /**
1392
- * Create or update a chat
1393
- */
1394
1446
  upsertChat(chat) {
1395
1447
  const now = Math.floor(Date.now() / 1e3);
1396
1448
  this.db.prepare(
@@ -1422,9 +1474,6 @@ var ChatStore = class {
1422
1474
  now
1423
1475
  );
1424
1476
  }
1425
- /**
1426
- * Get a chat by ID
1427
- */
1428
1477
  getChat(id) {
1429
1478
  const row = this.db.prepare(
1430
1479
  `
@@ -1435,20 +1484,17 @@ var ChatStore = class {
1435
1484
  return {
1436
1485
  id: row.id,
1437
1486
  type: row.type,
1438
- title: row.title,
1439
- username: row.username,
1440
- memberCount: row.member_count,
1487
+ title: row.title ?? void 0,
1488
+ username: row.username ?? void 0,
1489
+ memberCount: row.member_count ?? void 0,
1441
1490
  isMonitored: Boolean(row.is_monitored),
1442
1491
  isArchived: Boolean(row.is_archived),
1443
- lastMessageId: row.last_message_id,
1492
+ lastMessageId: row.last_message_id ?? void 0,
1444
1493
  lastMessageAt: row.last_message_at ? new Date(row.last_message_at * 1e3) : void 0,
1445
1494
  createdAt: new Date(row.created_at * 1e3),
1446
1495
  updatedAt: new Date(row.updated_at * 1e3)
1447
1496
  };
1448
1497
  }
1449
- /**
1450
- * Get active (monitored, non-archived) chats
1451
- */
1452
1498
  getActiveChats(limit = 50) {
1453
1499
  const rows = this.db.prepare(
1454
1500
  `
@@ -1461,20 +1507,17 @@ var ChatStore = class {
1461
1507
  return rows.map((row) => ({
1462
1508
  id: row.id,
1463
1509
  type: row.type,
1464
- title: row.title,
1465
- username: row.username,
1466
- memberCount: row.member_count,
1510
+ title: row.title ?? void 0,
1511
+ username: row.username ?? void 0,
1512
+ memberCount: row.member_count ?? void 0,
1467
1513
  isMonitored: Boolean(row.is_monitored),
1468
1514
  isArchived: Boolean(row.is_archived),
1469
- lastMessageId: row.last_message_id,
1515
+ lastMessageId: row.last_message_id ?? void 0,
1470
1516
  lastMessageAt: row.last_message_at ? new Date(row.last_message_at * 1e3) : void 0,
1471
1517
  createdAt: new Date(row.created_at * 1e3),
1472
1518
  updatedAt: new Date(row.updated_at * 1e3)
1473
1519
  }));
1474
1520
  }
1475
- /**
1476
- * Update last message info
1477
- */
1478
1521
  updateLastMessage(chatId, messageId, timestamp) {
1479
1522
  this.db.prepare(
1480
1523
  `
@@ -1484,9 +1527,6 @@ var ChatStore = class {
1484
1527
  `
1485
1528
  ).run(messageId, Math.floor(timestamp.getTime() / 1e3), chatId);
1486
1529
  }
1487
- /**
1488
- * Archive a chat
1489
- */
1490
1530
  archiveChat(chatId) {
1491
1531
  this.db.prepare(
1492
1532
  `
@@ -1496,9 +1536,6 @@ var ChatStore = class {
1496
1536
  `
1497
1537
  ).run(chatId);
1498
1538
  }
1499
- /**
1500
- * Unarchive a chat
1501
- */
1502
1539
  unarchiveChat(chatId) {
1503
1540
  this.db.prepare(
1504
1541
  `
@@ -1508,9 +1545,6 @@ var ChatStore = class {
1508
1545
  `
1509
1546
  ).run(chatId);
1510
1547
  }
1511
- /**
1512
- * Set monitoring status
1513
- */
1514
1548
  setMonitored(chatId, monitored) {
1515
1549
  this.db.prepare(
1516
1550
  `
@@ -1527,9 +1561,6 @@ var UserStore = class {
1527
1561
  constructor(db) {
1528
1562
  this.db = db;
1529
1563
  }
1530
- /**
1531
- * Create or update a user
1532
- */
1533
1564
  upsertUser(user) {
1534
1565
  const now = Math.floor(Date.now() / 1e3);
1535
1566
  const existing = this.db.prepare(`SELECT id FROM tg_users WHERE id = ?`).get(user.id);
@@ -1568,17 +1599,14 @@ var UserStore = class {
1568
1599
  );
1569
1600
  }
1570
1601
  }
1571
- /**
1572
- * Get a user by ID
1573
- */
1574
1602
  getUser(id) {
1575
1603
  const row = this.db.prepare(`SELECT * FROM tg_users WHERE id = ?`).get(id);
1576
1604
  if (!row) return void 0;
1577
1605
  return {
1578
1606
  id: row.id,
1579
- username: row.username,
1580
- firstName: row.first_name,
1581
- lastName: row.last_name,
1607
+ username: row.username ?? void 0,
1608
+ firstName: row.first_name ?? void 0,
1609
+ lastName: row.last_name ?? void 0,
1582
1610
  isBot: Boolean(row.is_bot),
1583
1611
  isAdmin: Boolean(row.is_admin),
1584
1612
  isAllowed: Boolean(row.is_allowed),
@@ -1587,17 +1615,14 @@ var UserStore = class {
1587
1615
  messageCount: row.message_count
1588
1616
  };
1589
1617
  }
1590
- /**
1591
- * Get a user by username
1592
- */
1593
1618
  getUserByUsername(username) {
1594
1619
  const row = this.db.prepare(`SELECT * FROM tg_users WHERE username = ?`).get(username.replace("@", ""));
1595
1620
  if (!row) return void 0;
1596
1621
  return {
1597
1622
  id: row.id,
1598
- username: row.username,
1599
- firstName: row.first_name,
1600
- lastName: row.last_name,
1623
+ username: row.username ?? void 0,
1624
+ firstName: row.first_name ?? void 0,
1625
+ lastName: row.last_name ?? void 0,
1601
1626
  isBot: Boolean(row.is_bot),
1602
1627
  isAdmin: Boolean(row.is_admin),
1603
1628
  isAllowed: Boolean(row.is_allowed),
@@ -1606,9 +1631,6 @@ var UserStore = class {
1606
1631
  messageCount: row.message_count
1607
1632
  };
1608
1633
  }
1609
- /**
1610
- * Update last seen timestamp
1611
- */
1612
1634
  updateLastSeen(userId) {
1613
1635
  this.db.prepare(
1614
1636
  `
@@ -1618,9 +1640,6 @@ var UserStore = class {
1618
1640
  `
1619
1641
  ).run(userId);
1620
1642
  }
1621
- /**
1622
- * Increment message count
1623
- */
1624
1643
  incrementMessageCount(userId) {
1625
1644
  this.db.prepare(
1626
1645
  `
@@ -1630,9 +1649,6 @@ var UserStore = class {
1630
1649
  `
1631
1650
  ).run(userId);
1632
1651
  }
1633
- /**
1634
- * Set admin status
1635
- */
1636
1652
  setAdmin(userId, isAdmin) {
1637
1653
  this.db.prepare(
1638
1654
  `
@@ -1642,9 +1658,6 @@ var UserStore = class {
1642
1658
  `
1643
1659
  ).run(isAdmin ? 1 : 0, userId);
1644
1660
  }
1645
- /**
1646
- * Set allowed status
1647
- */
1648
1661
  setAllowed(userId, isAllowed) {
1649
1662
  this.db.prepare(
1650
1663
  `
@@ -1654,9 +1667,6 @@ var UserStore = class {
1654
1667
  `
1655
1668
  ).run(isAllowed ? 1 : 0, userId);
1656
1669
  }
1657
- /**
1658
- * Get all admins
1659
- */
1660
1670
  getAdmins() {
1661
1671
  const rows = this.db.prepare(
1662
1672
  `
@@ -1667,9 +1677,9 @@ var UserStore = class {
1667
1677
  ).all();
1668
1678
  return rows.map((row) => ({
1669
1679
  id: row.id,
1670
- username: row.username,
1671
- firstName: row.first_name,
1672
- lastName: row.last_name,
1680
+ username: row.username ?? void 0,
1681
+ firstName: row.first_name ?? void 0,
1682
+ lastName: row.last_name ?? void 0,
1673
1683
  isBot: Boolean(row.is_bot),
1674
1684
  isAdmin: Boolean(row.is_admin),
1675
1685
  isAllowed: Boolean(row.is_allowed),
@@ -1678,9 +1688,6 @@ var UserStore = class {
1678
1688
  messageCount: row.message_count
1679
1689
  }));
1680
1690
  }
1681
- /**
1682
- * Get recently active users
1683
- */
1684
1691
  getRecentUsers(limit = 50) {
1685
1692
  const rows = this.db.prepare(
1686
1693
  `
@@ -1691,9 +1698,9 @@ var UserStore = class {
1691
1698
  ).all(limit);
1692
1699
  return rows.map((row) => ({
1693
1700
  id: row.id,
1694
- username: row.username,
1695
- firstName: row.first_name,
1696
- lastName: row.last_name,
1701
+ username: row.username ?? void 0,
1702
+ firstName: row.first_name ?? void 0,
1703
+ lastName: row.last_name ?? void 0,
1697
1704
  isBot: Boolean(row.is_bot),
1698
1705
  isAdmin: Boolean(row.is_admin),
1699
1706
  isAllowed: Boolean(row.is_allowed),
@@ -1713,26 +1720,20 @@ var HybridSearch = class {
1713
1720
  this.db = db;
1714
1721
  this.vectorEnabled = vectorEnabled;
1715
1722
  }
1716
- /**
1717
- * Search in knowledge base
1718
- */
1719
1723
  async searchKnowledge(query, queryEmbedding, options = {}) {
1720
1724
  const limit = options.limit ?? 10;
1721
- const vectorWeight = options.vectorWeight ?? 0.7;
1722
- const keywordWeight = options.keywordWeight ?? 0.3;
1723
- const vectorResults = this.vectorEnabled ? this.vectorSearchKnowledge(queryEmbedding, limit * 2) : [];
1724
- const keywordResults = this.keywordSearchKnowledge(query, limit * 2);
1725
+ const vectorWeight = options.vectorWeight ?? 0.5;
1726
+ const keywordWeight = options.keywordWeight ?? 0.5;
1727
+ const vectorResults = this.vectorEnabled ? this.vectorSearchKnowledge(queryEmbedding, Math.ceil(limit * 3)) : [];
1728
+ const keywordResults = this.keywordSearchKnowledge(query, Math.ceil(limit * 3));
1725
1729
  return this.mergeResults(vectorResults, keywordResults, vectorWeight, keywordWeight, limit);
1726
1730
  }
1727
- /**
1728
- * Search in Telegram messages
1729
- */
1730
1731
  async searchMessages(query, queryEmbedding, options = {}) {
1731
1732
  const limit = options.limit ?? 10;
1732
- const vectorWeight = options.vectorWeight ?? 0.7;
1733
- const keywordWeight = options.keywordWeight ?? 0.3;
1734
- const vectorResults = this.vectorEnabled ? this.vectorSearchMessages(queryEmbedding, limit * 2, options.chatId) : [];
1735
- const keywordResults = this.keywordSearchMessages(query, limit * 2, options.chatId);
1733
+ const vectorWeight = options.vectorWeight ?? 0.5;
1734
+ const keywordWeight = options.keywordWeight ?? 0.5;
1735
+ const vectorResults = this.vectorEnabled ? this.vectorSearchMessages(queryEmbedding, Math.ceil(limit * 3), options.chatId) : [];
1736
+ const keywordResults = this.keywordSearchMessages(query, Math.ceil(limit * 3), options.chatId);
1736
1737
  return this.mergeResults(vectorResults, keywordResults, vectorWeight, keywordWeight, limit);
1737
1738
  }
1738
1739
  vectorSearchKnowledge(embedding, limit) {
@@ -1864,10 +1865,14 @@ var HybridSearch = class {
1864
1865
  byId.set(r.id, { ...r, score: keywordWeight * (r.keywordScore ?? 0) });
1865
1866
  }
1866
1867
  }
1867
- return Array.from(byId.values()).sort((a, b) => b.score - a.score).slice(0, limit);
1868
+ return Array.from(byId.values()).filter((r) => r.score >= HYBRID_SEARCH_MIN_SCORE).sort((a, b) => b.score - a.score).slice(0, limit);
1868
1869
  }
1870
+ /**
1871
+ * Convert BM25 rank to normalized score.
1872
+ * FTS5 rank is negative; more negative = better match.
1873
+ */
1869
1874
  bm25ToScore(rank) {
1870
- return 1 / (1 + Math.abs(rank));
1875
+ return 1 / (1 + Math.exp(rank));
1871
1876
  }
1872
1877
  };
1873
1878
 
@@ -1908,6 +1913,9 @@ var ContextBuilder = class {
1908
1913
  console.warn("Knowledge search failed:", error);
1909
1914
  }
1910
1915
  }
1916
+ const recentTextsSet = new Set(
1917
+ recentTgMessages.filter((m) => m.text && m.text.length > 0).map((m) => m.text)
1918
+ );
1911
1919
  const relevantFeed = [];
1912
1920
  if (includeFeedHistory) {
1913
1921
  try {
@@ -1915,10 +1923,13 @@ var ContextBuilder = class {
1915
1923
  chatId,
1916
1924
  limit: maxRelevantChunks
1917
1925
  });
1918
- relevantFeed.push(...feedResults.map((r) => r.text));
1926
+ for (const r of feedResults) {
1927
+ if (!recentTextsSet.has(r.text)) {
1928
+ relevantFeed.push(r.text);
1929
+ }
1930
+ }
1919
1931
  if (searchAllChats) {
1920
1932
  const globalResults = await this.hybridSearch.searchMessages(query, queryEmbedding, {
1921
- // No chatId = search all chats
1922
1933
  limit: maxRelevantChunks
1923
1934
  });
1924
1935
  const existingTexts = new Set(relevantFeed);
@@ -1982,6 +1993,14 @@ export {
1982
1993
  getDatabase,
1983
1994
  closeDatabase,
1984
1995
  NoopEmbeddingProvider,
1996
+ fetchWithTimeout,
1997
+ setTonapiKey,
1998
+ tonapiFetch,
1999
+ STONFI_API_BASE_URL,
2000
+ GECKOTERMINAL_API_URL,
2001
+ COINGECKO_API_URL,
2002
+ OPENAI_TTS_URL,
2003
+ ELEVENLABS_TTS_URL,
1985
2004
  AnthropicEmbeddingProvider,
1986
2005
  LocalEmbeddingProvider,
1987
2006
  CachedEmbeddingProvider,