teleton 0.4.0 → 0.5.2
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 +88 -13
- package/dist/BigInteger-DQ33LTTE.js +5 -0
- package/dist/chunk-4DU3C27M.js +30 -0
- package/dist/chunk-5WWR4CU3.js +124 -0
- package/dist/{chunk-E2NXSWOS.js → chunk-NUGDTPE4.js} +24 -64
- package/dist/{chunk-OA5L7GM6.js → chunk-O4R7V5Y2.js} +37 -5
- package/dist/chunk-QUAPFI2N.js +42 -0
- package/dist/chunk-TSKJCWQQ.js +1263 -0
- package/dist/{chunk-B2PRMXOH.js → chunk-WL2Q3VRD.js} +0 -2
- package/dist/{chunk-QU4ZOR35.js → chunk-WOXBZOQX.js} +3179 -3368
- package/dist/{chunk-7UPH62J2.js → chunk-WUTMT6DW.js} +293 -261
- package/dist/{chunk-OQGNS2FV.js → chunk-YBA6IBGT.js} +20 -5
- package/dist/cli/index.js +41 -172
- package/dist/{endpoint-FT2B2RZ2.js → endpoint-FLYNEZ2F.js} +1 -1
- package/dist/{get-my-gifts-AFKBG4YQ.js → get-my-gifts-KVULMBJ3.js} +1 -1
- package/dist/index.js +12 -12
- package/dist/{memory-SYTQ5P7P.js → memory-Y5J7CXAR.js} +4 -5
- package/dist/{migrate-ITXMRRSZ.js → migrate-UEQCDWL2.js} +4 -5
- package/dist/server-BQY7CM2N.js +1120 -0
- package/dist/{task-dependency-resolver-GY6PEBIS.js → task-dependency-resolver-TRPILAHM.js} +2 -2
- package/dist/{task-executor-4QKTZZ3P.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 -6
- package/dist/chunk-67QC5FBN.js +0 -60
- package/dist/chunk-A64NPEFL.js +0 -74
- package/dist/chunk-DUW5VBAZ.js +0 -133
- package/dist/chunk-QBGUCUOW.js +0 -16
- package/dist/scraper-SH7GS7TO.js +0 -282
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "./chunk-A64NPEFL.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";
|
|
@@ -428,7 +428,7 @@ function setSchemaVersion(db, version) {
|
|
|
428
428
|
`
|
|
429
429
|
).run(version);
|
|
430
430
|
}
|
|
431
|
-
var CURRENT_SCHEMA_VERSION = "1.
|
|
431
|
+
var CURRENT_SCHEMA_VERSION = "1.10.1";
|
|
432
432
|
function runMigrations(db) {
|
|
433
433
|
const currentVersion = getSchemaVersion(db);
|
|
434
434
|
if (!currentVersion || versionLessThan(currentVersion, "1.1.0")) {
|
|
@@ -535,6 +535,47 @@ function runMigrations(db) {
|
|
|
535
535
|
throw error;
|
|
536
536
|
}
|
|
537
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
|
+
}
|
|
538
579
|
setSchemaVersion(db, CURRENT_SCHEMA_VERSION);
|
|
539
580
|
}
|
|
540
581
|
|
|
@@ -603,33 +644,18 @@ var MemoryDatabase = class {
|
|
|
603
644
|
ensureSchema(this.db);
|
|
604
645
|
console.log("Migration complete");
|
|
605
646
|
}
|
|
606
|
-
/**
|
|
607
|
-
* Get the underlying better-sqlite3 database
|
|
608
|
-
*/
|
|
609
647
|
getDb() {
|
|
610
648
|
return this.db;
|
|
611
649
|
}
|
|
612
|
-
/**
|
|
613
|
-
* Check if vector search is available
|
|
614
|
-
*/
|
|
615
650
|
isVectorSearchReady() {
|
|
616
651
|
return this.vectorReady;
|
|
617
652
|
}
|
|
618
|
-
/**
|
|
619
|
-
* Get vector dimensions
|
|
620
|
-
*/
|
|
621
653
|
getVectorDimensions() {
|
|
622
654
|
return this.config.vectorDimensions;
|
|
623
655
|
}
|
|
624
|
-
/**
|
|
625
|
-
* Execute a function in a transaction
|
|
626
|
-
*/
|
|
627
656
|
transaction(fn) {
|
|
628
657
|
return this.db.transaction(fn)();
|
|
629
658
|
}
|
|
630
|
-
/**
|
|
631
|
-
* Execute an async function in a transaction
|
|
632
|
-
*/
|
|
633
659
|
async asyncTransaction(fn) {
|
|
634
660
|
const beginTrans = this.db.prepare("BEGIN");
|
|
635
661
|
const commitTrans = this.db.prepare("COMMIT");
|
|
@@ -644,9 +670,6 @@ var MemoryDatabase = class {
|
|
|
644
670
|
throw error;
|
|
645
671
|
}
|
|
646
672
|
}
|
|
647
|
-
/**
|
|
648
|
-
* Get database stats
|
|
649
|
-
*/
|
|
650
673
|
getStats() {
|
|
651
674
|
const knowledge = this.db.prepare(`SELECT COUNT(*) as c FROM knowledge`).get();
|
|
652
675
|
const sessions = this.db.prepare(`SELECT COUNT(*) as c FROM sessions`).get();
|
|
@@ -666,21 +689,15 @@ var MemoryDatabase = class {
|
|
|
666
689
|
vectorSearchEnabled: this.vectorReady
|
|
667
690
|
};
|
|
668
691
|
}
|
|
669
|
-
/**
|
|
670
|
-
* Vacuum the database to reclaim space
|
|
671
|
-
*/
|
|
672
692
|
vacuum() {
|
|
673
693
|
this.db.exec("VACUUM");
|
|
674
694
|
}
|
|
675
|
-
/**
|
|
676
|
-
* Optimize the database (ANALYZE)
|
|
677
|
-
*/
|
|
678
695
|
optimize() {
|
|
679
696
|
this.db.exec("ANALYZE");
|
|
680
697
|
}
|
|
681
698
|
/**
|
|
682
|
-
* Rebuild FTS indexes from existing data
|
|
683
|
-
* 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.
|
|
684
701
|
*/
|
|
685
702
|
rebuildFtsIndexes() {
|
|
686
703
|
this.db.exec(`DELETE FROM knowledge_fts`);
|
|
@@ -703,9 +720,6 @@ var MemoryDatabase = class {
|
|
|
703
720
|
}
|
|
704
721
|
return { knowledge: knowledgeRows.length, messages: messageRows.length };
|
|
705
722
|
}
|
|
706
|
-
/**
|
|
707
|
-
* Close the database connection
|
|
708
|
-
*/
|
|
709
723
|
close() {
|
|
710
724
|
if (this.db.open) {
|
|
711
725
|
this.db.close();
|
|
@@ -745,6 +759,63 @@ var NoopEmbeddingProvider = class {
|
|
|
745
759
|
}
|
|
746
760
|
};
|
|
747
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
|
+
|
|
748
819
|
// src/memory/embeddings/anthropic.ts
|
|
749
820
|
var AnthropicEmbeddingProvider = class {
|
|
750
821
|
id = "anthropic";
|
|
@@ -803,16 +874,22 @@ var AnthropicEmbeddingProvider = class {
|
|
|
803
874
|
};
|
|
804
875
|
|
|
805
876
|
// src/memory/embeddings/local.ts
|
|
806
|
-
import { pipeline } from "@huggingface/transformers";
|
|
877
|
+
import { pipeline, env } from "@huggingface/transformers";
|
|
878
|
+
import { join as join2 } from "path";
|
|
879
|
+
env.cacheDir = join2(TELETON_ROOT, "models");
|
|
807
880
|
var extractorPromise = null;
|
|
808
881
|
function getExtractor(model) {
|
|
809
882
|
if (!extractorPromise) {
|
|
810
|
-
console.log(`\u{1F4E6} Loading local embedding model: ${model}
|
|
883
|
+
console.log(`\u{1F4E6} Loading local embedding model: ${model} (cache: ${env.cacheDir})`);
|
|
811
884
|
extractorPromise = pipeline("feature-extraction", model, {
|
|
812
885
|
dtype: "fp32"
|
|
813
886
|
}).then((ext) => {
|
|
814
887
|
console.log(`\u2705 Local embedding model ready`);
|
|
815
888
|
return ext;
|
|
889
|
+
}).catch((err) => {
|
|
890
|
+
console.error(`\u274C Failed to load embedding model: ${err.message}`);
|
|
891
|
+
extractorPromise = null;
|
|
892
|
+
throw err;
|
|
816
893
|
});
|
|
817
894
|
}
|
|
818
895
|
return extractorPromise;
|
|
@@ -821,16 +898,37 @@ var LocalEmbeddingProvider = class {
|
|
|
821
898
|
id = "local";
|
|
822
899
|
model;
|
|
823
900
|
dimensions;
|
|
901
|
+
_disabled = false;
|
|
824
902
|
constructor(config) {
|
|
825
903
|
this.model = config.model || "Xenova/all-MiniLM-L6-v2";
|
|
826
904
|
this.dimensions = 384;
|
|
827
905
|
}
|
|
906
|
+
/**
|
|
907
|
+
* Pre-download and load the model at startup.
|
|
908
|
+
* If loading fails, marks this provider as disabled (returns empty embeddings).
|
|
909
|
+
* Call this once during app init — avoids retry spam on every message.
|
|
910
|
+
* @returns true if model loaded successfully, false if fallback to noop
|
|
911
|
+
*/
|
|
912
|
+
async warmup() {
|
|
913
|
+
try {
|
|
914
|
+
await getExtractor(this.model);
|
|
915
|
+
return true;
|
|
916
|
+
} catch (err) {
|
|
917
|
+
console.warn(
|
|
918
|
+
`\u26A0\uFE0F Local embedding model unavailable \u2014 falling back to FTS5-only search (no vector embeddings)`
|
|
919
|
+
);
|
|
920
|
+
this._disabled = true;
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
828
924
|
async embedQuery(text) {
|
|
925
|
+
if (this._disabled) return [];
|
|
829
926
|
const extractor = await getExtractor(this.model);
|
|
830
927
|
const output = await extractor(text, { pooling: "mean", normalize: true });
|
|
831
928
|
return Array.from(output.data);
|
|
832
929
|
}
|
|
833
930
|
async embedBatch(texts) {
|
|
931
|
+
if (this._disabled) return [];
|
|
834
932
|
if (texts.length === 0) return [];
|
|
835
933
|
const extractor = await getExtractor(this.model);
|
|
836
934
|
const output = await extractor(texts, { pooling: "mean", normalize: true });
|
|
@@ -875,6 +973,9 @@ var CachedEmbeddingProvider = class {
|
|
|
875
973
|
`UPDATE embedding_cache SET accessed_at = unixepoch() WHERE hash = ? AND model = ? AND provider = ?`
|
|
876
974
|
).run(hash, this.model, this.id);
|
|
877
975
|
}
|
|
976
|
+
async warmup() {
|
|
977
|
+
return this.inner.warmup?.() ?? true;
|
|
978
|
+
}
|
|
878
979
|
async embedQuery(text) {
|
|
879
980
|
const hash = hashText(text);
|
|
880
981
|
const row = this.cacheGet(hash);
|
|
@@ -945,7 +1046,7 @@ var CachedEmbeddingProvider = class {
|
|
|
945
1046
|
this.db.prepare(`DELETE FROM embedding_cache WHERE accessed_at < ?`).run(cutoff);
|
|
946
1047
|
const count = this.db.prepare(`SELECT COUNT(*) as cnt FROM embedding_cache`).get().cnt;
|
|
947
1048
|
if (count > EMBEDDING_CACHE_MAX_ENTRIES) {
|
|
948
|
-
const toDelete = Math.ceil(count *
|
|
1049
|
+
const toDelete = Math.ceil(count * EMBEDDING_CACHE_EVICTION_RATIO);
|
|
949
1050
|
this.db.prepare(
|
|
950
1051
|
`DELETE FROM embedding_cache WHERE (hash, model, provider) IN (
|
|
951
1052
|
SELECT hash, model, provider FROM embedding_cache ORDER BY accessed_at ASC LIMIT ?
|
|
@@ -1000,7 +1101,7 @@ function deserializeEmbedding(data) {
|
|
|
1000
1101
|
|
|
1001
1102
|
// src/memory/agent/knowledge.ts
|
|
1002
1103
|
import { readFileSync, existsSync as existsSync3, readdirSync, statSync } from "fs";
|
|
1003
|
-
import { join as
|
|
1104
|
+
import { join as join3 } from "path";
|
|
1004
1105
|
var KnowledgeIndexer = class {
|
|
1005
1106
|
constructor(db, workspaceDir, embedder, vectorEnabled) {
|
|
1006
1107
|
this.db = db;
|
|
@@ -1008,9 +1109,6 @@ var KnowledgeIndexer = class {
|
|
|
1008
1109
|
this.embedder = embedder;
|
|
1009
1110
|
this.vectorEnabled = vectorEnabled;
|
|
1010
1111
|
}
|
|
1011
|
-
/**
|
|
1012
|
-
* Index all memory files
|
|
1013
|
-
*/
|
|
1014
1112
|
async indexAll() {
|
|
1015
1113
|
const files = this.listMemoryFiles();
|
|
1016
1114
|
let indexed = 0;
|
|
@@ -1025,9 +1123,6 @@ var KnowledgeIndexer = class {
|
|
|
1025
1123
|
}
|
|
1026
1124
|
return { indexed, skipped };
|
|
1027
1125
|
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Index a single file
|
|
1030
|
-
*/
|
|
1031
1126
|
async indexFile(absPath) {
|
|
1032
1127
|
if (!existsSync3(absPath) || !absPath.endsWith(".md")) {
|
|
1033
1128
|
return false;
|
|
@@ -1039,54 +1134,53 @@ var KnowledgeIndexer = class {
|
|
|
1039
1134
|
if (existing?.hash === fileHash) {
|
|
1040
1135
|
return false;
|
|
1041
1136
|
}
|
|
1042
|
-
if (this.vectorEnabled) {
|
|
1043
|
-
this.db.prepare(
|
|
1044
|
-
`DELETE FROM knowledge_vec WHERE id IN (
|
|
1045
|
-
SELECT id FROM knowledge WHERE path = ? AND source = 'memory'
|
|
1046
|
-
)`
|
|
1047
|
-
).run(relPath);
|
|
1048
|
-
}
|
|
1049
|
-
this.db.prepare(`DELETE FROM knowledge WHERE path = ? AND source = 'memory'`).run(relPath);
|
|
1050
1137
|
const chunks = this.chunkMarkdown(content, relPath);
|
|
1051
1138
|
const texts = chunks.map((c) => c.text);
|
|
1052
1139
|
const embeddings = await this.embedder.embedBatch(texts);
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
const embedding = embeddings[i] ?? [];
|
|
1061
|
-
insert.run(
|
|
1062
|
-
chunk.id,
|
|
1063
|
-
chunk.path,
|
|
1064
|
-
chunk.text,
|
|
1065
|
-
serializeEmbedding(embedding),
|
|
1066
|
-
chunk.startLine,
|
|
1067
|
-
chunk.endLine,
|
|
1068
|
-
chunk.hash
|
|
1069
|
-
);
|
|
1070
|
-
if (insertVec && embedding.length > 0) {
|
|
1071
|
-
insertVec.run(chunk.id, serializeEmbedding(embedding));
|
|
1140
|
+
this.db.transaction(() => {
|
|
1141
|
+
if (this.vectorEnabled) {
|
|
1142
|
+
this.db.prepare(
|
|
1143
|
+
`DELETE FROM knowledge_vec WHERE id IN (
|
|
1144
|
+
SELECT id FROM knowledge WHERE path = ? AND source = 'memory'
|
|
1145
|
+
)`
|
|
1146
|
+
).run(relPath);
|
|
1072
1147
|
}
|
|
1073
|
-
|
|
1148
|
+
this.db.prepare(`DELETE FROM knowledge WHERE path = ? AND source = 'memory'`).run(relPath);
|
|
1149
|
+
const insert = this.db.prepare(`
|
|
1150
|
+
INSERT INTO knowledge (id, source, path, text, embedding, start_line, end_line, hash)
|
|
1151
|
+
VALUES (?, 'memory', ?, ?, ?, ?, ?, ?)
|
|
1152
|
+
`);
|
|
1153
|
+
const insertVec = this.vectorEnabled ? this.db.prepare(`INSERT INTO knowledge_vec (id, embedding) VALUES (?, ?)`) : null;
|
|
1154
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
1155
|
+
const chunk = chunks[i];
|
|
1156
|
+
const embedding = embeddings[i] ?? [];
|
|
1157
|
+
insert.run(
|
|
1158
|
+
chunk.id,
|
|
1159
|
+
chunk.path,
|
|
1160
|
+
chunk.text,
|
|
1161
|
+
serializeEmbedding(embedding),
|
|
1162
|
+
chunk.startLine,
|
|
1163
|
+
chunk.endLine,
|
|
1164
|
+
chunk.hash
|
|
1165
|
+
);
|
|
1166
|
+
if (insertVec && embedding.length > 0) {
|
|
1167
|
+
insertVec.run(chunk.id, serializeEmbedding(embedding));
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
})();
|
|
1074
1171
|
return true;
|
|
1075
1172
|
}
|
|
1076
|
-
/**
|
|
1077
|
-
* List all memory files
|
|
1078
|
-
*/
|
|
1079
1173
|
listMemoryFiles() {
|
|
1080
1174
|
const files = [];
|
|
1081
|
-
const memoryMd =
|
|
1175
|
+
const memoryMd = join3(this.workspaceDir, "MEMORY.md");
|
|
1082
1176
|
if (existsSync3(memoryMd)) {
|
|
1083
1177
|
files.push(memoryMd);
|
|
1084
1178
|
}
|
|
1085
|
-
const memoryDir =
|
|
1179
|
+
const memoryDir = join3(this.workspaceDir, "memory");
|
|
1086
1180
|
if (existsSync3(memoryDir)) {
|
|
1087
1181
|
const entries = readdirSync(memoryDir);
|
|
1088
1182
|
for (const entry of entries) {
|
|
1089
|
-
const absPath =
|
|
1183
|
+
const absPath = join3(memoryDir, entry);
|
|
1090
1184
|
if (statSync(absPath).isFile() && entry.endsWith(".md")) {
|
|
1091
1185
|
files.push(absPath);
|
|
1092
1186
|
}
|
|
@@ -1095,49 +1189,58 @@ var KnowledgeIndexer = class {
|
|
|
1095
1189
|
return files;
|
|
1096
1190
|
}
|
|
1097
1191
|
/**
|
|
1098
|
-
* Chunk markdown content
|
|
1192
|
+
* Chunk markdown content with structure awareness.
|
|
1193
|
+
* Respects heading boundaries, code blocks, and list groups.
|
|
1194
|
+
* Target: KNOWLEDGE_CHUNK_SIZE chars, hard max: 2x target.
|
|
1099
1195
|
*/
|
|
1100
1196
|
chunkMarkdown(content, path) {
|
|
1101
1197
|
const lines = content.split("\n");
|
|
1102
1198
|
const chunks = [];
|
|
1103
|
-
const
|
|
1104
|
-
const
|
|
1199
|
+
const targetSize = KNOWLEDGE_CHUNK_SIZE;
|
|
1200
|
+
const hardMax = targetSize * 2;
|
|
1105
1201
|
let currentChunk = "";
|
|
1106
1202
|
let startLine = 1;
|
|
1107
1203
|
let currentLine = 1;
|
|
1204
|
+
let inCodeBlock = false;
|
|
1205
|
+
const flushChunk = () => {
|
|
1206
|
+
const text = currentChunk.trim();
|
|
1207
|
+
if (text.length > 0) {
|
|
1208
|
+
chunks.push({
|
|
1209
|
+
id: hashText(`${path}:${startLine}:${currentLine - 1}`),
|
|
1210
|
+
source: "memory",
|
|
1211
|
+
path,
|
|
1212
|
+
text,
|
|
1213
|
+
startLine,
|
|
1214
|
+
endLine: currentLine - 1,
|
|
1215
|
+
hash: hashText(text)
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
currentChunk = "";
|
|
1219
|
+
startLine = currentLine;
|
|
1220
|
+
};
|
|
1108
1221
|
for (const line of lines) {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1222
|
+
if (line.trimStart().startsWith("```")) {
|
|
1223
|
+
inCodeBlock = !inCodeBlock;
|
|
1224
|
+
}
|
|
1225
|
+
if (!inCodeBlock && currentChunk.length >= targetSize) {
|
|
1226
|
+
const isHeading = /^#{1,6}\s/.test(line);
|
|
1227
|
+
const isBlankLine = line.trim() === "";
|
|
1228
|
+
const isHorizontalRule = /^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trim());
|
|
1229
|
+
if (isHeading) {
|
|
1230
|
+
flushChunk();
|
|
1231
|
+
} else if ((isBlankLine || isHorizontalRule) && currentChunk.length >= targetSize) {
|
|
1232
|
+
currentChunk += line + "\n";
|
|
1233
|
+
currentLine++;
|
|
1234
|
+
flushChunk();
|
|
1235
|
+
continue;
|
|
1236
|
+
} else if (currentChunk.length >= hardMax) {
|
|
1237
|
+
flushChunk();
|
|
1122
1238
|
}
|
|
1123
|
-
const overlapText = currentChunk.slice(-overlap);
|
|
1124
|
-
currentChunk = overlapText;
|
|
1125
|
-
startLine = currentLine + 1;
|
|
1126
1239
|
}
|
|
1240
|
+
currentChunk += line + "\n";
|
|
1127
1241
|
currentLine++;
|
|
1128
1242
|
}
|
|
1129
|
-
|
|
1130
|
-
if (text.length > 0) {
|
|
1131
|
-
chunks.push({
|
|
1132
|
-
id: hashText(`${path}:${startLine}:${currentLine}`),
|
|
1133
|
-
source: "memory",
|
|
1134
|
-
path,
|
|
1135
|
-
text,
|
|
1136
|
-
startLine,
|
|
1137
|
-
endLine: currentLine,
|
|
1138
|
-
hash: hashText(text)
|
|
1139
|
-
});
|
|
1140
|
-
}
|
|
1243
|
+
flushChunk();
|
|
1141
1244
|
return chunks;
|
|
1142
1245
|
}
|
|
1143
1246
|
};
|
|
@@ -1150,9 +1253,6 @@ var SessionStore = class {
|
|
|
1150
1253
|
this.embedder = embedder;
|
|
1151
1254
|
this.vectorEnabled = vectorEnabled;
|
|
1152
1255
|
}
|
|
1153
|
-
/**
|
|
1154
|
-
* Create a new session
|
|
1155
|
-
*/
|
|
1156
1256
|
createSession(chatId) {
|
|
1157
1257
|
const id = randomUUID();
|
|
1158
1258
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -1170,9 +1270,6 @@ var SessionStore = class {
|
|
|
1170
1270
|
tokensUsed: 0
|
|
1171
1271
|
};
|
|
1172
1272
|
}
|
|
1173
|
-
/**
|
|
1174
|
-
* End a session with summary
|
|
1175
|
-
*/
|
|
1176
1273
|
endSession(sessionId, summary, tokensUsed = 0) {
|
|
1177
1274
|
const now = Math.floor(Date.now() / 1e3);
|
|
1178
1275
|
this.db.prepare(
|
|
@@ -1183,9 +1280,6 @@ var SessionStore = class {
|
|
|
1183
1280
|
`
|
|
1184
1281
|
).run(now, summary, tokensUsed, sessionId);
|
|
1185
1282
|
}
|
|
1186
|
-
/**
|
|
1187
|
-
* Update message count for a session
|
|
1188
|
-
*/
|
|
1189
1283
|
incrementMessageCount(sessionId, count = 1) {
|
|
1190
1284
|
this.db.prepare(
|
|
1191
1285
|
`
|
|
@@ -1195,9 +1289,6 @@ var SessionStore = class {
|
|
|
1195
1289
|
`
|
|
1196
1290
|
).run(count, sessionId);
|
|
1197
1291
|
}
|
|
1198
|
-
/**
|
|
1199
|
-
* Get a session by ID
|
|
1200
|
-
*/
|
|
1201
1292
|
getSession(id) {
|
|
1202
1293
|
const row = this.db.prepare(`SELECT * FROM sessions WHERE id = ?`).get(id);
|
|
1203
1294
|
if (!row) return void 0;
|
|
@@ -1206,14 +1297,11 @@ var SessionStore = class {
|
|
|
1206
1297
|
chatId: row.chat_id,
|
|
1207
1298
|
startedAt: new Date(row.started_at * 1e3),
|
|
1208
1299
|
endedAt: row.ended_at ? new Date(row.ended_at * 1e3) : void 0,
|
|
1209
|
-
summary: row.summary,
|
|
1300
|
+
summary: row.summary ?? void 0,
|
|
1210
1301
|
messageCount: row.message_count,
|
|
1211
1302
|
tokensUsed: row.tokens_used
|
|
1212
1303
|
};
|
|
1213
1304
|
}
|
|
1214
|
-
/**
|
|
1215
|
-
* Get active (not ended) sessions
|
|
1216
|
-
*/
|
|
1217
1305
|
getActiveSessions() {
|
|
1218
1306
|
const rows = this.db.prepare(
|
|
1219
1307
|
`
|
|
@@ -1227,14 +1315,11 @@ var SessionStore = class {
|
|
|
1227
1315
|
chatId: row.chat_id,
|
|
1228
1316
|
startedAt: new Date(row.started_at * 1e3),
|
|
1229
1317
|
endedAt: void 0,
|
|
1230
|
-
summary: row.summary,
|
|
1318
|
+
summary: row.summary ?? void 0,
|
|
1231
1319
|
messageCount: row.message_count,
|
|
1232
1320
|
tokensUsed: row.tokens_used
|
|
1233
1321
|
}));
|
|
1234
1322
|
}
|
|
1235
|
-
/**
|
|
1236
|
-
* Get sessions for a specific chat
|
|
1237
|
-
*/
|
|
1238
1323
|
getSessionsByChat(chatId, limit = 50) {
|
|
1239
1324
|
const rows = this.db.prepare(
|
|
1240
1325
|
`
|
|
@@ -1249,14 +1334,14 @@ var SessionStore = class {
|
|
|
1249
1334
|
chatId: row.chat_id,
|
|
1250
1335
|
startedAt: new Date(row.started_at * 1e3),
|
|
1251
1336
|
endedAt: row.ended_at ? new Date(row.ended_at * 1e3) : void 0,
|
|
1252
|
-
summary: row.summary,
|
|
1337
|
+
summary: row.summary ?? void 0,
|
|
1253
1338
|
messageCount: row.message_count,
|
|
1254
1339
|
tokensUsed: row.tokens_used
|
|
1255
1340
|
}));
|
|
1256
1341
|
}
|
|
1257
1342
|
/**
|
|
1258
|
-
* Index a session for search
|
|
1259
|
-
*
|
|
1343
|
+
* Index a session for search after ending.
|
|
1344
|
+
* Creates a knowledge entry from the session summary for future retrieval.
|
|
1260
1345
|
*/
|
|
1261
1346
|
async indexSession(sessionId) {
|
|
1262
1347
|
const session = this.getSession(sessionId);
|
|
@@ -1291,9 +1376,6 @@ ${session.summary}`;
|
|
|
1291
1376
|
console.error("Error indexing session:", error);
|
|
1292
1377
|
}
|
|
1293
1378
|
}
|
|
1294
|
-
/**
|
|
1295
|
-
* Delete a session
|
|
1296
|
-
*/
|
|
1297
1379
|
deleteSession(sessionId) {
|
|
1298
1380
|
const knowledgeId = `session:${sessionId}`;
|
|
1299
1381
|
if (this.vectorEnabled) {
|
|
@@ -1311,18 +1393,12 @@ var MessageStore = class {
|
|
|
1311
1393
|
this.embedder = embedder;
|
|
1312
1394
|
this.vectorEnabled = vectorEnabled;
|
|
1313
1395
|
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Ensure chat exists in database
|
|
1316
|
-
*/
|
|
1317
1396
|
ensureChat(chatId, isGroup = false) {
|
|
1318
1397
|
const existing = this.db.prepare(`SELECT id FROM tg_chats WHERE id = ?`).get(chatId);
|
|
1319
1398
|
if (!existing) {
|
|
1320
1399
|
this.db.prepare(`INSERT INTO tg_chats (id, type, is_monitored) VALUES (?, ?, 1)`).run(chatId, isGroup ? "group" : "dm");
|
|
1321
1400
|
}
|
|
1322
1401
|
}
|
|
1323
|
-
/**
|
|
1324
|
-
* Ensure user exists in database
|
|
1325
|
-
*/
|
|
1326
1402
|
ensureUser(userId) {
|
|
1327
1403
|
if (!userId) return;
|
|
1328
1404
|
const existing = this.db.prepare(`SELECT id FROM tg_users WHERE id = ?`).get(userId);
|
|
@@ -1330,9 +1406,6 @@ var MessageStore = class {
|
|
|
1330
1406
|
this.db.prepare(`INSERT INTO tg_users (id) VALUES (?)`).run(userId);
|
|
1331
1407
|
}
|
|
1332
1408
|
}
|
|
1333
|
-
/**
|
|
1334
|
-
* Store a message
|
|
1335
|
-
*/
|
|
1336
1409
|
async storeMessage(message) {
|
|
1337
1410
|
this.ensureChat(message.chatId);
|
|
1338
1411
|
if (message.senderId) {
|
|
@@ -1340,34 +1413,33 @@ var MessageStore = class {
|
|
|
1340
1413
|
}
|
|
1341
1414
|
const embedding = this.vectorEnabled && message.text ? await this.embedder.embedQuery(message.text) : [];
|
|
1342
1415
|
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1343
|
-
this.db.
|
|
1416
|
+
this.db.transaction(() => {
|
|
1417
|
+
this.db.prepare(
|
|
1418
|
+
`
|
|
1419
|
+
INSERT OR REPLACE INTO tg_messages (
|
|
1420
|
+
id, chat_id, sender_id, text, embedding, reply_to_id,
|
|
1421
|
+
is_from_agent, has_media, media_type, timestamp
|
|
1422
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1344
1423
|
`
|
|
1345
|
-
|
|
1346
|
-
id,
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
this.db.prepare(`DELETE FROM tg_messages_vec WHERE id = ?`).run(message.id);
|
|
1364
|
-
this.db.prepare(`INSERT INTO tg_messages_vec (id, embedding) VALUES (?, ?)`).run(message.id, embeddingBuffer);
|
|
1365
|
-
}
|
|
1366
|
-
this.db.prepare(`UPDATE tg_chats SET last_message_at = ?, last_message_id = ? WHERE id = ?`).run(message.timestamp, message.id, message.chatId);
|
|
1424
|
+
).run(
|
|
1425
|
+
message.id,
|
|
1426
|
+
message.chatId,
|
|
1427
|
+
message.senderId,
|
|
1428
|
+
message.text,
|
|
1429
|
+
embeddingBuffer,
|
|
1430
|
+
message.replyToId,
|
|
1431
|
+
message.isFromAgent ? 1 : 0,
|
|
1432
|
+
message.hasMedia ? 1 : 0,
|
|
1433
|
+
message.mediaType,
|
|
1434
|
+
message.timestamp
|
|
1435
|
+
);
|
|
1436
|
+
if (this.vectorEnabled && embedding.length > 0 && message.text) {
|
|
1437
|
+
this.db.prepare(`DELETE FROM tg_messages_vec WHERE id = ?`).run(message.id);
|
|
1438
|
+
this.db.prepare(`INSERT INTO tg_messages_vec (id, embedding) VALUES (?, ?)`).run(message.id, embeddingBuffer);
|
|
1439
|
+
}
|
|
1440
|
+
this.db.prepare(`UPDATE tg_chats SET last_message_at = ?, last_message_id = ? WHERE id = ?`).run(message.timestamp, message.id, message.chatId);
|
|
1441
|
+
})();
|
|
1367
1442
|
}
|
|
1368
|
-
/**
|
|
1369
|
-
* Get recent messages from a chat
|
|
1370
|
-
*/
|
|
1371
1443
|
getRecentMessages(chatId, limit = 20) {
|
|
1372
1444
|
const rows = this.db.prepare(
|
|
1373
1445
|
`
|
|
@@ -1397,9 +1469,6 @@ var ChatStore = class {
|
|
|
1397
1469
|
constructor(db) {
|
|
1398
1470
|
this.db = db;
|
|
1399
1471
|
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Create or update a chat
|
|
1402
|
-
*/
|
|
1403
1472
|
upsertChat(chat) {
|
|
1404
1473
|
const now = Math.floor(Date.now() / 1e3);
|
|
1405
1474
|
this.db.prepare(
|
|
@@ -1431,9 +1500,6 @@ var ChatStore = class {
|
|
|
1431
1500
|
now
|
|
1432
1501
|
);
|
|
1433
1502
|
}
|
|
1434
|
-
/**
|
|
1435
|
-
* Get a chat by ID
|
|
1436
|
-
*/
|
|
1437
1503
|
getChat(id) {
|
|
1438
1504
|
const row = this.db.prepare(
|
|
1439
1505
|
`
|
|
@@ -1444,20 +1510,17 @@ var ChatStore = class {
|
|
|
1444
1510
|
return {
|
|
1445
1511
|
id: row.id,
|
|
1446
1512
|
type: row.type,
|
|
1447
|
-
title: row.title,
|
|
1448
|
-
username: row.username,
|
|
1449
|
-
memberCount: row.member_count,
|
|
1513
|
+
title: row.title ?? void 0,
|
|
1514
|
+
username: row.username ?? void 0,
|
|
1515
|
+
memberCount: row.member_count ?? void 0,
|
|
1450
1516
|
isMonitored: Boolean(row.is_monitored),
|
|
1451
1517
|
isArchived: Boolean(row.is_archived),
|
|
1452
|
-
lastMessageId: row.last_message_id,
|
|
1518
|
+
lastMessageId: row.last_message_id ?? void 0,
|
|
1453
1519
|
lastMessageAt: row.last_message_at ? new Date(row.last_message_at * 1e3) : void 0,
|
|
1454
1520
|
createdAt: new Date(row.created_at * 1e3),
|
|
1455
1521
|
updatedAt: new Date(row.updated_at * 1e3)
|
|
1456
1522
|
};
|
|
1457
1523
|
}
|
|
1458
|
-
/**
|
|
1459
|
-
* Get active (monitored, non-archived) chats
|
|
1460
|
-
*/
|
|
1461
1524
|
getActiveChats(limit = 50) {
|
|
1462
1525
|
const rows = this.db.prepare(
|
|
1463
1526
|
`
|
|
@@ -1470,20 +1533,17 @@ var ChatStore = class {
|
|
|
1470
1533
|
return rows.map((row) => ({
|
|
1471
1534
|
id: row.id,
|
|
1472
1535
|
type: row.type,
|
|
1473
|
-
title: row.title,
|
|
1474
|
-
username: row.username,
|
|
1475
|
-
memberCount: row.member_count,
|
|
1536
|
+
title: row.title ?? void 0,
|
|
1537
|
+
username: row.username ?? void 0,
|
|
1538
|
+
memberCount: row.member_count ?? void 0,
|
|
1476
1539
|
isMonitored: Boolean(row.is_monitored),
|
|
1477
1540
|
isArchived: Boolean(row.is_archived),
|
|
1478
|
-
lastMessageId: row.last_message_id,
|
|
1541
|
+
lastMessageId: row.last_message_id ?? void 0,
|
|
1479
1542
|
lastMessageAt: row.last_message_at ? new Date(row.last_message_at * 1e3) : void 0,
|
|
1480
1543
|
createdAt: new Date(row.created_at * 1e3),
|
|
1481
1544
|
updatedAt: new Date(row.updated_at * 1e3)
|
|
1482
1545
|
}));
|
|
1483
1546
|
}
|
|
1484
|
-
/**
|
|
1485
|
-
* Update last message info
|
|
1486
|
-
*/
|
|
1487
1547
|
updateLastMessage(chatId, messageId, timestamp) {
|
|
1488
1548
|
this.db.prepare(
|
|
1489
1549
|
`
|
|
@@ -1493,9 +1553,6 @@ var ChatStore = class {
|
|
|
1493
1553
|
`
|
|
1494
1554
|
).run(messageId, Math.floor(timestamp.getTime() / 1e3), chatId);
|
|
1495
1555
|
}
|
|
1496
|
-
/**
|
|
1497
|
-
* Archive a chat
|
|
1498
|
-
*/
|
|
1499
1556
|
archiveChat(chatId) {
|
|
1500
1557
|
this.db.prepare(
|
|
1501
1558
|
`
|
|
@@ -1505,9 +1562,6 @@ var ChatStore = class {
|
|
|
1505
1562
|
`
|
|
1506
1563
|
).run(chatId);
|
|
1507
1564
|
}
|
|
1508
|
-
/**
|
|
1509
|
-
* Unarchive a chat
|
|
1510
|
-
*/
|
|
1511
1565
|
unarchiveChat(chatId) {
|
|
1512
1566
|
this.db.prepare(
|
|
1513
1567
|
`
|
|
@@ -1517,9 +1571,6 @@ var ChatStore = class {
|
|
|
1517
1571
|
`
|
|
1518
1572
|
).run(chatId);
|
|
1519
1573
|
}
|
|
1520
|
-
/**
|
|
1521
|
-
* Set monitoring status
|
|
1522
|
-
*/
|
|
1523
1574
|
setMonitored(chatId, monitored) {
|
|
1524
1575
|
this.db.prepare(
|
|
1525
1576
|
`
|
|
@@ -1536,9 +1587,6 @@ var UserStore = class {
|
|
|
1536
1587
|
constructor(db) {
|
|
1537
1588
|
this.db = db;
|
|
1538
1589
|
}
|
|
1539
|
-
/**
|
|
1540
|
-
* Create or update a user
|
|
1541
|
-
*/
|
|
1542
1590
|
upsertUser(user) {
|
|
1543
1591
|
const now = Math.floor(Date.now() / 1e3);
|
|
1544
1592
|
const existing = this.db.prepare(`SELECT id FROM tg_users WHERE id = ?`).get(user.id);
|
|
@@ -1577,17 +1625,14 @@ var UserStore = class {
|
|
|
1577
1625
|
);
|
|
1578
1626
|
}
|
|
1579
1627
|
}
|
|
1580
|
-
/**
|
|
1581
|
-
* Get a user by ID
|
|
1582
|
-
*/
|
|
1583
1628
|
getUser(id) {
|
|
1584
1629
|
const row = this.db.prepare(`SELECT * FROM tg_users WHERE id = ?`).get(id);
|
|
1585
1630
|
if (!row) return void 0;
|
|
1586
1631
|
return {
|
|
1587
1632
|
id: row.id,
|
|
1588
|
-
username: row.username,
|
|
1589
|
-
firstName: row.first_name,
|
|
1590
|
-
lastName: row.last_name,
|
|
1633
|
+
username: row.username ?? void 0,
|
|
1634
|
+
firstName: row.first_name ?? void 0,
|
|
1635
|
+
lastName: row.last_name ?? void 0,
|
|
1591
1636
|
isBot: Boolean(row.is_bot),
|
|
1592
1637
|
isAdmin: Boolean(row.is_admin),
|
|
1593
1638
|
isAllowed: Boolean(row.is_allowed),
|
|
@@ -1596,17 +1641,14 @@ var UserStore = class {
|
|
|
1596
1641
|
messageCount: row.message_count
|
|
1597
1642
|
};
|
|
1598
1643
|
}
|
|
1599
|
-
/**
|
|
1600
|
-
* Get a user by username
|
|
1601
|
-
*/
|
|
1602
1644
|
getUserByUsername(username) {
|
|
1603
1645
|
const row = this.db.prepare(`SELECT * FROM tg_users WHERE username = ?`).get(username.replace("@", ""));
|
|
1604
1646
|
if (!row) return void 0;
|
|
1605
1647
|
return {
|
|
1606
1648
|
id: row.id,
|
|
1607
|
-
username: row.username,
|
|
1608
|
-
firstName: row.first_name,
|
|
1609
|
-
lastName: row.last_name,
|
|
1649
|
+
username: row.username ?? void 0,
|
|
1650
|
+
firstName: row.first_name ?? void 0,
|
|
1651
|
+
lastName: row.last_name ?? void 0,
|
|
1610
1652
|
isBot: Boolean(row.is_bot),
|
|
1611
1653
|
isAdmin: Boolean(row.is_admin),
|
|
1612
1654
|
isAllowed: Boolean(row.is_allowed),
|
|
@@ -1615,9 +1657,6 @@ var UserStore = class {
|
|
|
1615
1657
|
messageCount: row.message_count
|
|
1616
1658
|
};
|
|
1617
1659
|
}
|
|
1618
|
-
/**
|
|
1619
|
-
* Update last seen timestamp
|
|
1620
|
-
*/
|
|
1621
1660
|
updateLastSeen(userId) {
|
|
1622
1661
|
this.db.prepare(
|
|
1623
1662
|
`
|
|
@@ -1627,9 +1666,6 @@ var UserStore = class {
|
|
|
1627
1666
|
`
|
|
1628
1667
|
).run(userId);
|
|
1629
1668
|
}
|
|
1630
|
-
/**
|
|
1631
|
-
* Increment message count
|
|
1632
|
-
*/
|
|
1633
1669
|
incrementMessageCount(userId) {
|
|
1634
1670
|
this.db.prepare(
|
|
1635
1671
|
`
|
|
@@ -1639,9 +1675,6 @@ var UserStore = class {
|
|
|
1639
1675
|
`
|
|
1640
1676
|
).run(userId);
|
|
1641
1677
|
}
|
|
1642
|
-
/**
|
|
1643
|
-
* Set admin status
|
|
1644
|
-
*/
|
|
1645
1678
|
setAdmin(userId, isAdmin) {
|
|
1646
1679
|
this.db.prepare(
|
|
1647
1680
|
`
|
|
@@ -1651,9 +1684,6 @@ var UserStore = class {
|
|
|
1651
1684
|
`
|
|
1652
1685
|
).run(isAdmin ? 1 : 0, userId);
|
|
1653
1686
|
}
|
|
1654
|
-
/**
|
|
1655
|
-
* Set allowed status
|
|
1656
|
-
*/
|
|
1657
1687
|
setAllowed(userId, isAllowed) {
|
|
1658
1688
|
this.db.prepare(
|
|
1659
1689
|
`
|
|
@@ -1663,9 +1693,6 @@ var UserStore = class {
|
|
|
1663
1693
|
`
|
|
1664
1694
|
).run(isAllowed ? 1 : 0, userId);
|
|
1665
1695
|
}
|
|
1666
|
-
/**
|
|
1667
|
-
* Get all admins
|
|
1668
|
-
*/
|
|
1669
1696
|
getAdmins() {
|
|
1670
1697
|
const rows = this.db.prepare(
|
|
1671
1698
|
`
|
|
@@ -1676,9 +1703,9 @@ var UserStore = class {
|
|
|
1676
1703
|
).all();
|
|
1677
1704
|
return rows.map((row) => ({
|
|
1678
1705
|
id: row.id,
|
|
1679
|
-
username: row.username,
|
|
1680
|
-
firstName: row.first_name,
|
|
1681
|
-
lastName: row.last_name,
|
|
1706
|
+
username: row.username ?? void 0,
|
|
1707
|
+
firstName: row.first_name ?? void 0,
|
|
1708
|
+
lastName: row.last_name ?? void 0,
|
|
1682
1709
|
isBot: Boolean(row.is_bot),
|
|
1683
1710
|
isAdmin: Boolean(row.is_admin),
|
|
1684
1711
|
isAllowed: Boolean(row.is_allowed),
|
|
@@ -1687,9 +1714,6 @@ var UserStore = class {
|
|
|
1687
1714
|
messageCount: row.message_count
|
|
1688
1715
|
}));
|
|
1689
1716
|
}
|
|
1690
|
-
/**
|
|
1691
|
-
* Get recently active users
|
|
1692
|
-
*/
|
|
1693
1717
|
getRecentUsers(limit = 50) {
|
|
1694
1718
|
const rows = this.db.prepare(
|
|
1695
1719
|
`
|
|
@@ -1700,9 +1724,9 @@ var UserStore = class {
|
|
|
1700
1724
|
).all(limit);
|
|
1701
1725
|
return rows.map((row) => ({
|
|
1702
1726
|
id: row.id,
|
|
1703
|
-
username: row.username,
|
|
1704
|
-
firstName: row.first_name,
|
|
1705
|
-
lastName: row.last_name,
|
|
1727
|
+
username: row.username ?? void 0,
|
|
1728
|
+
firstName: row.first_name ?? void 0,
|
|
1729
|
+
lastName: row.last_name ?? void 0,
|
|
1706
1730
|
isBot: Boolean(row.is_bot),
|
|
1707
1731
|
isAdmin: Boolean(row.is_admin),
|
|
1708
1732
|
isAllowed: Boolean(row.is_allowed),
|
|
@@ -1722,30 +1746,20 @@ var HybridSearch = class {
|
|
|
1722
1746
|
this.db = db;
|
|
1723
1747
|
this.vectorEnabled = vectorEnabled;
|
|
1724
1748
|
}
|
|
1725
|
-
/**
|
|
1726
|
-
* Search in knowledge base
|
|
1727
|
-
*/
|
|
1728
1749
|
async searchKnowledge(query, queryEmbedding, options = {}) {
|
|
1729
1750
|
const limit = options.limit ?? 10;
|
|
1730
|
-
const vectorWeight = options.vectorWeight ?? 0.
|
|
1731
|
-
const keywordWeight = options.keywordWeight ?? 0.
|
|
1732
|
-
const vectorResults = this.vectorEnabled ? this.vectorSearchKnowledge(queryEmbedding, Math.ceil(limit *
|
|
1733
|
-
const keywordResults = this.keywordSearchKnowledge(query, Math.ceil(limit *
|
|
1751
|
+
const vectorWeight = options.vectorWeight ?? 0.5;
|
|
1752
|
+
const keywordWeight = options.keywordWeight ?? 0.5;
|
|
1753
|
+
const vectorResults = this.vectorEnabled ? this.vectorSearchKnowledge(queryEmbedding, Math.ceil(limit * 3)) : [];
|
|
1754
|
+
const keywordResults = this.keywordSearchKnowledge(query, Math.ceil(limit * 3));
|
|
1734
1755
|
return this.mergeResults(vectorResults, keywordResults, vectorWeight, keywordWeight, limit);
|
|
1735
1756
|
}
|
|
1736
|
-
/**
|
|
1737
|
-
* Search in Telegram messages
|
|
1738
|
-
*/
|
|
1739
1757
|
async searchMessages(query, queryEmbedding, options = {}) {
|
|
1740
1758
|
const limit = options.limit ?? 10;
|
|
1741
|
-
const vectorWeight = options.vectorWeight ?? 0.
|
|
1742
|
-
const keywordWeight = options.keywordWeight ?? 0.
|
|
1743
|
-
const vectorResults = this.vectorEnabled ? this.vectorSearchMessages(queryEmbedding, Math.ceil(limit *
|
|
1744
|
-
const keywordResults = this.keywordSearchMessages(
|
|
1745
|
-
query,
|
|
1746
|
-
Math.ceil(limit * 1.5),
|
|
1747
|
-
options.chatId
|
|
1748
|
-
);
|
|
1759
|
+
const vectorWeight = options.vectorWeight ?? 0.5;
|
|
1760
|
+
const keywordWeight = options.keywordWeight ?? 0.5;
|
|
1761
|
+
const vectorResults = this.vectorEnabled ? this.vectorSearchMessages(queryEmbedding, Math.ceil(limit * 3), options.chatId) : [];
|
|
1762
|
+
const keywordResults = this.keywordSearchMessages(query, Math.ceil(limit * 3), options.chatId);
|
|
1749
1763
|
return this.mergeResults(vectorResults, keywordResults, vectorWeight, keywordWeight, limit);
|
|
1750
1764
|
}
|
|
1751
1765
|
vectorSearchKnowledge(embedding, limit) {
|
|
@@ -1877,10 +1891,14 @@ var HybridSearch = class {
|
|
|
1877
1891
|
byId.set(r.id, { ...r, score: keywordWeight * (r.keywordScore ?? 0) });
|
|
1878
1892
|
}
|
|
1879
1893
|
}
|
|
1880
|
-
return Array.from(byId.values()).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
1894
|
+
return Array.from(byId.values()).filter((r) => r.score >= HYBRID_SEARCH_MIN_SCORE).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
1881
1895
|
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Convert BM25 rank to normalized score.
|
|
1898
|
+
* FTS5 rank is negative; more negative = better match.
|
|
1899
|
+
*/
|
|
1882
1900
|
bm25ToScore(rank) {
|
|
1883
|
-
return 1 / (1 + Math.
|
|
1901
|
+
return 1 / (1 + Math.exp(rank));
|
|
1884
1902
|
}
|
|
1885
1903
|
};
|
|
1886
1904
|
|
|
@@ -1921,6 +1939,9 @@ var ContextBuilder = class {
|
|
|
1921
1939
|
console.warn("Knowledge search failed:", error);
|
|
1922
1940
|
}
|
|
1923
1941
|
}
|
|
1942
|
+
const recentTextsSet = new Set(
|
|
1943
|
+
recentTgMessages.filter((m) => m.text && m.text.length > 0).map((m) => m.text)
|
|
1944
|
+
);
|
|
1924
1945
|
const relevantFeed = [];
|
|
1925
1946
|
if (includeFeedHistory) {
|
|
1926
1947
|
try {
|
|
@@ -1928,10 +1949,13 @@ var ContextBuilder = class {
|
|
|
1928
1949
|
chatId,
|
|
1929
1950
|
limit: maxRelevantChunks
|
|
1930
1951
|
});
|
|
1931
|
-
|
|
1952
|
+
for (const r of feedResults) {
|
|
1953
|
+
if (!recentTextsSet.has(r.text)) {
|
|
1954
|
+
relevantFeed.push(r.text);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1932
1957
|
if (searchAllChats) {
|
|
1933
1958
|
const globalResults = await this.hybridSearch.searchMessages(query, queryEmbedding, {
|
|
1934
|
-
// No chatId = search all chats
|
|
1935
1959
|
limit: maxRelevantChunks
|
|
1936
1960
|
});
|
|
1937
1961
|
const existingTexts = new Set(relevantFeed);
|
|
@@ -1995,6 +2019,14 @@ export {
|
|
|
1995
2019
|
getDatabase,
|
|
1996
2020
|
closeDatabase,
|
|
1997
2021
|
NoopEmbeddingProvider,
|
|
2022
|
+
fetchWithTimeout,
|
|
2023
|
+
setTonapiKey,
|
|
2024
|
+
tonapiFetch,
|
|
2025
|
+
STONFI_API_BASE_URL,
|
|
2026
|
+
GECKOTERMINAL_API_URL,
|
|
2027
|
+
COINGECKO_API_URL,
|
|
2028
|
+
OPENAI_TTS_URL,
|
|
2029
|
+
ELEVENLABS_TTS_URL,
|
|
1998
2030
|
AnthropicEmbeddingProvider,
|
|
1999
2031
|
LocalEmbeddingProvider,
|
|
2000
2032
|
CachedEmbeddingProvider,
|