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.
- package/README.md +219 -95
- package/dist/BigInteger-DQ33LTTE.js +5 -0
- package/dist/chunk-4DU3C27M.js +30 -0
- package/dist/chunk-5WWR4CU3.js +124 -0
- package/dist/{chunk-QQW6KE7Q.js → chunk-BYTHCDZA.js} +282 -263
- package/dist/chunk-EHEV7FJ7.js +157 -0
- package/dist/{chunk-E2NXSWOS.js → chunk-NUGDTPE4.js} +24 -64
- package/dist/{chunk-UYF4TT44.js → chunk-O4R7V5Y2.js} +38 -6
- package/dist/chunk-QUAPFI2N.js +42 -0
- package/dist/{chunk-ILDG4OPK.js → chunk-RRB6BWU7.js} +9411 -10083
- package/dist/chunk-TSKJCWQQ.js +1263 -0
- package/dist/{chunk-B2PRMXOH.js → chunk-WL2Q3VRD.js} +0 -2
- package/dist/{chunk-OQGNS2FV.js → chunk-YBA6IBGT.js} +20 -5
- package/dist/cli/index.js +42 -176
- package/dist/endpoint-FLYNEZ2F.js +7 -0
- package/dist/format-transactions-FD74HI5N.js +9 -0
- package/dist/{get-my-gifts-AFKBG4YQ.js → get-my-gifts-KVULMBJ3.js} +1 -1
- package/dist/index.js +13 -11
- package/dist/{memory-ZXDAJBL6.js → memory-657W5AS6.js} +4 -5
- package/dist/{migrate-7OG67FXP.js → migrate-PMB2JVXH.js} +4 -5
- package/dist/server-BQY7CM2N.js +1120 -0
- package/dist/{task-dependency-resolver-S45DFI5C.js → task-dependency-resolver-TRPILAHM.js} +4 -4
- package/dist/{task-executor-AUTT3VAL.js → task-executor-N7XNVK5N.js} +1 -1
- package/dist/{tasks-M3QDPTGY.js → tasks-QSCWSMPS.js} +1 -1
- package/dist/{transcript-DF2Y6CFY.js → transcript-7V4UNID4.js} +1 -1
- package/dist/web/assets/index-CDMbujHf.css +1 -0
- package/dist/web/assets/index-DDX8oQ2z.js +67 -0
- package/dist/web/index.html +16 -0
- package/dist/web/logo_dark.png +0 -0
- package/package.json +23 -4
- package/src/templates/IDENTITY.md +1 -1
- package/src/templates/MEMORY.md +1 -0
- package/src/templates/SECURITY.md +5 -0
- package/src/templates/SOUL.md +3 -2
- package/dist/chunk-DAMFGHXV.js +0 -74
- package/dist/chunk-DUW5VBAZ.js +0 -133
- package/dist/chunk-LCMHAUNK.js +0 -62
- package/dist/scraper-KXRBQMVQ.js +0 -282
- package/dist/timeouts-ZAK6NELA.js +0 -63
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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-
|
|
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.
|
|
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
|
-
|
|
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 *
|
|
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
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
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
|
|
1092
|
-
const
|
|
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
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
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
|
-
|
|
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
|
|
1247
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
1338
|
-
id,
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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.
|
|
1722
|
-
const keywordWeight = options.keywordWeight ?? 0.
|
|
1723
|
-
const vectorResults = this.vectorEnabled ? this.vectorSearchKnowledge(queryEmbedding, limit *
|
|
1724
|
-
const keywordResults = this.keywordSearchKnowledge(query, limit *
|
|
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.
|
|
1733
|
-
const keywordWeight = options.keywordWeight ?? 0.
|
|
1734
|
-
const vectorResults = this.vectorEnabled ? this.vectorSearchMessages(queryEmbedding, limit *
|
|
1735
|
-
const keywordResults = this.keywordSearchMessages(query, limit *
|
|
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.
|
|
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
|
-
|
|
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,
|