teleton 0.8.0 → 0.8.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 +28 -11
- package/dist/{chunk-H36RFKRI.js → chunk-2IZU3REP.js} +572 -174
- package/dist/chunk-3UFPFWYP.js +12 -0
- package/dist/{chunk-NUGDTPE4.js → chunk-4L66JHQE.js} +2 -1
- package/dist/{chunk-TVRZJIZX.js → chunk-55SKE6YH.js} +4 -4
- package/dist/{setup-server-QXED3D2L.js → chunk-57URFK6M.js} +161 -210
- package/dist/chunk-5SEMA47R.js +75 -0
- package/dist/{chunk-JHYZYFZJ.js → chunk-7YKSXOQQ.js} +17 -2
- package/dist/{chunk-IJBWWQE4.js → chunk-C4NKJT2Z.js} +12 -0
- package/dist/{chunk-RQBAMUCV.js → chunk-GGXJLMOH.js} +1451 -743
- package/dist/{chunk-WIKM24GZ.js → chunk-H7MFXJZK.js} +7 -2
- package/dist/{chunk-U56QTM46.js → chunk-HEDJCLA6.js} +85 -44
- package/dist/{chunk-QVBSUYVX.js → chunk-J73TA3UM.js} +17 -9
- package/dist/{chunk-P36I6OIV.js → chunk-LC4TV3KL.js} +13 -2
- package/dist/{chunk-RCMD3U65.js → chunk-NQ6FZKCE.js} +13 -0
- package/dist/{chunk-SD4NLLYG.js → chunk-VYKW7FMV.js} +224 -93
- package/dist/chunk-W25Z7CM6.js +487 -0
- package/dist/{chunk-OJCLKU5Z.js → chunk-WFTC3JJW.js} +16 -0
- package/dist/{server-H3QA252W.js → chunk-XBSCYMKM.js} +369 -374
- package/dist/{chunk-PHSAHTK4.js → chunk-YOSUPUAJ.js} +75 -7
- package/dist/cli/index.js +67 -22
- package/dist/{client-LNZTDQSA.js → client-YOOHI776.js} +4 -4
- package/dist/{get-my-gifts-OMGKOEPM.js → get-my-gifts-Y7EN7RK4.js} +3 -3
- package/dist/index.js +15 -14
- package/dist/{memory-AS7WKGTW.js → memory-Q6EWGK2S.js} +7 -5
- package/dist/memory-hook-WUXJNVT5.js +18 -0
- package/dist/{migrate-POHWYEIW.js → migrate-WFU6COBN.js} +5 -5
- package/dist/server-GYZXKIKU.js +787 -0
- package/dist/server-YODFBZKG.js +392 -0
- package/dist/setup-server-IZBUOJRU.js +215 -0
- package/dist/{store-GAFULOOX.js → store-7M4XV6M5.js} +6 -6
- package/dist/{task-dependency-resolver-3FIKQ7Z6.js → task-dependency-resolver-L6UUMTHK.js} +3 -3
- package/dist/{task-executor-RUTFG6VG.js → task-executor-XBNJLUCS.js} +3 -3
- package/dist/{tasks-BEZ4QRI2.js → tasks-WQIKXDX5.js} +1 -1
- package/dist/{tool-adapter-IH5VGBOO.js → tool-adapter-IVX2XQJE.js} +1 -1
- package/dist/{tool-index-H3SHOJC3.js → tool-index-NYH57UWP.js} +9 -6
- package/dist/{transcript-IMNE6KU3.js → transcript-IM7G25OS.js} +2 -2
- package/dist/web/assets/index-BfYCdwLI.js +80 -0
- package/dist/web/assets/{index-BrVqauzj.css → index-DmlyQVhR.css} +1 -1
- package/dist/web/assets/{index.es-DkU1GvWU.js → index.es-DitvF-9H.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +14 -5
- package/dist/chunk-XBE4JB7C.js +0 -8
- package/dist/web/assets/index-DYeEkvJ6.js +0 -72
|
@@ -3,20 +3,24 @@ import {
|
|
|
3
3
|
createEmbeddingProvider,
|
|
4
4
|
hashText,
|
|
5
5
|
serializeEmbedding
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HEDJCLA6.js";
|
|
7
7
|
import {
|
|
8
8
|
FEED_MESSAGE_MAX_CHARS,
|
|
9
9
|
HYBRID_SEARCH_MIN_SCORE,
|
|
10
10
|
KNOWLEDGE_CHUNK_SIZE,
|
|
11
|
+
RECENCY_DECAY_FACTOR,
|
|
12
|
+
RECENCY_WEIGHT,
|
|
13
|
+
SECONDS_PER_DAY,
|
|
14
|
+
SECONDS_PER_HOUR,
|
|
11
15
|
SQLITE_CACHE_SIZE_KB,
|
|
12
16
|
SQLITE_MMAP_SIZE
|
|
13
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-C4NKJT2Z.js";
|
|
14
18
|
import {
|
|
15
19
|
TELETON_ROOT
|
|
16
20
|
} from "./chunk-EYWNOHMJ.js";
|
|
17
21
|
import {
|
|
18
22
|
createLogger
|
|
19
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-NQ6FZKCE.js";
|
|
20
24
|
|
|
21
25
|
// src/memory/database.ts
|
|
22
26
|
import Database2 from "better-sqlite3";
|
|
@@ -133,11 +137,7 @@ function migrateFromMainDb(moduleDb, tables) {
|
|
|
133
137
|
`INSERT OR IGNORE INTO ${table} (${cols}) SELECT ${cols} FROM main_db.${table}`
|
|
134
138
|
);
|
|
135
139
|
totalMigrated += src.c;
|
|
136
|
-
|
|
137
|
-
moduleDb.exec(`DROP TABLE main_db.${table}`);
|
|
138
|
-
} catch {
|
|
139
|
-
}
|
|
140
|
-
log.info(`Migrated ${src.c} rows from memory.db \u2192 ${table} (source dropped)`);
|
|
140
|
+
log.info(`Migrated ${src.c} rows from memory.db \u2192 ${table}`);
|
|
141
141
|
} catch (e) {
|
|
142
142
|
log.warn({ err: e }, `Could not migrate table ${table}`);
|
|
143
143
|
}
|
|
@@ -427,6 +427,26 @@ function ensureSchema(db) {
|
|
|
427
427
|
CREATE INDEX IF NOT EXISTS idx_exec_audit_timestamp ON exec_audit(timestamp DESC);
|
|
428
428
|
CREATE INDEX IF NOT EXISTS idx_exec_audit_user ON exec_audit(user_id);
|
|
429
429
|
|
|
430
|
+
-- =====================================================
|
|
431
|
+
-- PLUGIN CONFIG (Plugin Priority Order)
|
|
432
|
+
-- =====================================================
|
|
433
|
+
|
|
434
|
+
CREATE TABLE IF NOT EXISTS plugin_config (
|
|
435
|
+
plugin_name TEXT PRIMARY KEY,
|
|
436
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
437
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
-- =====================================================
|
|
441
|
+
-- USER HOOK CONFIG (Keyword Blocklist + Context Triggers)
|
|
442
|
+
-- =====================================================
|
|
443
|
+
|
|
444
|
+
CREATE TABLE IF NOT EXISTS user_hook_config (
|
|
445
|
+
key TEXT PRIMARY KEY,
|
|
446
|
+
value TEXT NOT NULL,
|
|
447
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
448
|
+
);
|
|
449
|
+
|
|
430
450
|
-- =====================================================
|
|
431
451
|
-- JOURNAL (Trading & Business Operations)
|
|
432
452
|
-- =====================================================
|
|
@@ -440,9 +460,11 @@ function ensureVectorTables(db, dimensions) {
|
|
|
440
460
|
WHERE type='table' AND name='knowledge_vec'
|
|
441
461
|
`
|
|
442
462
|
).get();
|
|
463
|
+
let dimensionsChanged = false;
|
|
443
464
|
if (existingDims?.sql && !existingDims.sql.includes(`[${dimensions}]`)) {
|
|
444
465
|
db.exec(`DROP TABLE IF EXISTS knowledge_vec`);
|
|
445
466
|
db.exec(`DROP TABLE IF EXISTS tg_messages_vec`);
|
|
467
|
+
dimensionsChanged = true;
|
|
446
468
|
}
|
|
447
469
|
db.exec(`
|
|
448
470
|
CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_vec USING vec0(
|
|
@@ -455,6 +477,7 @@ function ensureVectorTables(db, dimensions) {
|
|
|
455
477
|
embedding FLOAT[${dimensions}] distance_metric=cosine
|
|
456
478
|
);
|
|
457
479
|
`);
|
|
480
|
+
return dimensionsChanged;
|
|
458
481
|
}
|
|
459
482
|
function getSchemaVersion(db) {
|
|
460
483
|
const row = db.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get();
|
|
@@ -469,7 +492,7 @@ function setSchemaVersion(db, version) {
|
|
|
469
492
|
`
|
|
470
493
|
).run(version);
|
|
471
494
|
}
|
|
472
|
-
var CURRENT_SCHEMA_VERSION = "1.
|
|
495
|
+
var CURRENT_SCHEMA_VERSION = "1.15.0";
|
|
473
496
|
function runMigrations(db) {
|
|
474
497
|
const currentVersion = getSchemaVersion(db);
|
|
475
498
|
if (!currentVersion || versionLessThan(currentVersion, "1.1.0")) {
|
|
@@ -523,7 +546,7 @@ function runMigrations(db) {
|
|
|
523
546
|
try {
|
|
524
547
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
525
548
|
} catch (e) {
|
|
526
|
-
if (!e.message.includes("duplicate column name")) {
|
|
549
|
+
if (!(e instanceof Error) || !e.message.includes("duplicate column name")) {
|
|
527
550
|
throw e;
|
|
528
551
|
}
|
|
529
552
|
}
|
|
@@ -692,7 +715,7 @@ function runMigrations(db) {
|
|
|
692
715
|
try {
|
|
693
716
|
db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
|
|
694
717
|
} catch (e) {
|
|
695
|
-
if (!e.message.includes("duplicate column name")) {
|
|
718
|
+
if (!(e instanceof Error) || !e.message.includes("duplicate column name")) {
|
|
696
719
|
throw e;
|
|
697
720
|
}
|
|
698
721
|
}
|
|
@@ -705,6 +728,38 @@ function runMigrations(db) {
|
|
|
705
728
|
throw error;
|
|
706
729
|
}
|
|
707
730
|
}
|
|
731
|
+
if (!currentVersion || versionLessThan(currentVersion, "1.14.0")) {
|
|
732
|
+
log2.info("Running migration 1.14.0: Add plugin_config table for plugin priority");
|
|
733
|
+
try {
|
|
734
|
+
db.exec(`
|
|
735
|
+
CREATE TABLE IF NOT EXISTS plugin_config (
|
|
736
|
+
plugin_name TEXT PRIMARY KEY,
|
|
737
|
+
priority INTEGER NOT NULL DEFAULT 0,
|
|
738
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
739
|
+
);
|
|
740
|
+
`);
|
|
741
|
+
log2.info("Migration 1.14.0 complete: plugin_config table created");
|
|
742
|
+
} catch (error) {
|
|
743
|
+
log2.error({ err: error }, "Migration 1.14.0 failed");
|
|
744
|
+
throw error;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (!currentVersion || versionLessThan(currentVersion, "1.15.0")) {
|
|
748
|
+
log2.info("Running migration 1.15.0: Add user_hook_config table");
|
|
749
|
+
try {
|
|
750
|
+
db.exec(`
|
|
751
|
+
CREATE TABLE IF NOT EXISTS user_hook_config (
|
|
752
|
+
key TEXT PRIMARY KEY,
|
|
753
|
+
value TEXT NOT NULL,
|
|
754
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
755
|
+
);
|
|
756
|
+
`);
|
|
757
|
+
log2.info("Migration 1.15.0 complete: user_hook_config table created");
|
|
758
|
+
} catch (error) {
|
|
759
|
+
log2.error({ err: error }, "Migration 1.15.0 failed");
|
|
760
|
+
throw error;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
708
763
|
setSchemaVersion(db, CURRENT_SCHEMA_VERSION);
|
|
709
764
|
}
|
|
710
765
|
|
|
@@ -714,6 +769,7 @@ var MemoryDatabase = class {
|
|
|
714
769
|
db;
|
|
715
770
|
config;
|
|
716
771
|
vectorReady = false;
|
|
772
|
+
_dimensionsChanged = false;
|
|
717
773
|
constructor(config) {
|
|
718
774
|
this.config = config;
|
|
719
775
|
const dir = dirname2(config.path);
|
|
@@ -725,7 +781,8 @@ var MemoryDatabase = class {
|
|
|
725
781
|
});
|
|
726
782
|
try {
|
|
727
783
|
chmodSync2(config.path, 384);
|
|
728
|
-
} catch {
|
|
784
|
+
} catch (err) {
|
|
785
|
+
log3.warn({ err, path: config.path }, "Failed to set DB file permissions to 0o600");
|
|
729
786
|
}
|
|
730
787
|
this.db.pragma("journal_mode = WAL");
|
|
731
788
|
this.db.pragma("synchronous = NORMAL");
|
|
@@ -739,7 +796,8 @@ var MemoryDatabase = class {
|
|
|
739
796
|
let currentVersion = null;
|
|
740
797
|
try {
|
|
741
798
|
currentVersion = getSchemaVersion(this.db);
|
|
742
|
-
} catch {
|
|
799
|
+
} catch (err) {
|
|
800
|
+
log3.warn({ err }, "Could not read schema version, assuming fresh database");
|
|
743
801
|
currentVersion = null;
|
|
744
802
|
}
|
|
745
803
|
if (!currentVersion) {
|
|
@@ -758,7 +816,7 @@ var MemoryDatabase = class {
|
|
|
758
816
|
sqliteVec.load(this.db);
|
|
759
817
|
this.db.prepare("SELECT vec_version() as vec_version").get();
|
|
760
818
|
const dims = this.config.vectorDimensions ?? 512;
|
|
761
|
-
ensureVectorTables(this.db, dims);
|
|
819
|
+
this._dimensionsChanged = ensureVectorTables(this.db, dims);
|
|
762
820
|
this.vectorReady = true;
|
|
763
821
|
} catch (error) {
|
|
764
822
|
log3.warn(`sqlite-vec not available, vector search disabled: ${error.message}`);
|
|
@@ -778,6 +836,9 @@ var MemoryDatabase = class {
|
|
|
778
836
|
isVectorSearchReady() {
|
|
779
837
|
return this.vectorReady;
|
|
780
838
|
}
|
|
839
|
+
didDimensionsChange() {
|
|
840
|
+
return this._dimensionsChanged;
|
|
841
|
+
}
|
|
781
842
|
getVectorDimensions() {
|
|
782
843
|
return this.config.vectorDimensions;
|
|
783
844
|
}
|
|
@@ -799,21 +860,24 @@ var MemoryDatabase = class {
|
|
|
799
860
|
}
|
|
800
861
|
}
|
|
801
862
|
getStats() {
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
863
|
+
const counts = this.db.prepare(
|
|
864
|
+
`SELECT
|
|
865
|
+
(SELECT COUNT(*) FROM knowledge) as knowledge,
|
|
866
|
+
(SELECT COUNT(*) FROM sessions) as sessions,
|
|
867
|
+
(SELECT COUNT(*) FROM tasks) as tasks,
|
|
868
|
+
(SELECT COUNT(*) FROM tg_chats) as tg_chats,
|
|
869
|
+
(SELECT COUNT(*) FROM tg_users) as tg_users,
|
|
870
|
+
(SELECT COUNT(*) FROM tg_messages) as tg_messages,
|
|
871
|
+
(SELECT COUNT(*) FROM embedding_cache) as embedding_cache`
|
|
872
|
+
).get();
|
|
809
873
|
return {
|
|
810
|
-
knowledge: knowledge
|
|
811
|
-
sessions: sessions
|
|
812
|
-
tasks: tasks
|
|
813
|
-
tgChats:
|
|
814
|
-
tgUsers:
|
|
815
|
-
tgMessages:
|
|
816
|
-
embeddingCache:
|
|
874
|
+
knowledge: counts.knowledge,
|
|
875
|
+
sessions: counts.sessions,
|
|
876
|
+
tasks: counts.tasks,
|
|
877
|
+
tgChats: counts.tg_chats,
|
|
878
|
+
tgUsers: counts.tg_users,
|
|
879
|
+
tgMessages: counts.tg_messages,
|
|
880
|
+
embeddingCache: counts.embedding_cache,
|
|
817
881
|
vectorSearchEnabled: this.vectorReady
|
|
818
882
|
};
|
|
819
883
|
}
|
|
@@ -881,12 +945,12 @@ var KnowledgeIndexer = class {
|
|
|
881
945
|
this.embedder = embedder;
|
|
882
946
|
this.vectorEnabled = vectorEnabled;
|
|
883
947
|
}
|
|
884
|
-
async indexAll() {
|
|
948
|
+
async indexAll(options) {
|
|
885
949
|
const files = this.listMemoryFiles();
|
|
886
950
|
let indexed = 0;
|
|
887
951
|
let skipped = 0;
|
|
888
952
|
for (const file of files) {
|
|
889
|
-
const wasIndexed = await this.indexFile(file);
|
|
953
|
+
const wasIndexed = await this.indexFile(file, options?.force);
|
|
890
954
|
if (wasIndexed) {
|
|
891
955
|
indexed++;
|
|
892
956
|
} else {
|
|
@@ -895,16 +959,18 @@ var KnowledgeIndexer = class {
|
|
|
895
959
|
}
|
|
896
960
|
return { indexed, skipped };
|
|
897
961
|
}
|
|
898
|
-
async indexFile(absPath) {
|
|
962
|
+
async indexFile(absPath, force) {
|
|
899
963
|
if (!existsSync3(absPath) || !absPath.endsWith(".md")) {
|
|
900
964
|
return false;
|
|
901
965
|
}
|
|
902
966
|
const content = readFileSync(absPath, "utf-8");
|
|
903
967
|
const relPath = absPath.replace(this.workspaceDir + "/", "");
|
|
904
968
|
const fileHash = hashText(content);
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
969
|
+
if (!force) {
|
|
970
|
+
const existing = this.db.prepare(`SELECT hash FROM knowledge WHERE path = ? AND source = 'memory' LIMIT 1`).get(relPath);
|
|
971
|
+
if (existing?.hash === fileHash) {
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
908
974
|
}
|
|
909
975
|
const chunks = this.chunkMarkdown(content, relPath);
|
|
910
976
|
const texts = chunks.map((c) => c.text);
|
|
@@ -933,7 +999,7 @@ var KnowledgeIndexer = class {
|
|
|
933
999
|
serializeEmbedding(embedding),
|
|
934
1000
|
chunk.startLine,
|
|
935
1001
|
chunk.endLine,
|
|
936
|
-
|
|
1002
|
+
fileHash
|
|
937
1003
|
);
|
|
938
1004
|
if (insertVec && embedding.length > 0) {
|
|
939
1005
|
insertVec.run(chunk.id, serializeEmbedding(embedding));
|
|
@@ -974,6 +1040,7 @@ var KnowledgeIndexer = class {
|
|
|
974
1040
|
let startLine = 1;
|
|
975
1041
|
let currentLine = 1;
|
|
976
1042
|
let inCodeBlock = false;
|
|
1043
|
+
let overlapPrefix = "";
|
|
977
1044
|
const flushChunk = () => {
|
|
978
1045
|
const text = currentChunk.trim();
|
|
979
1046
|
if (text.length > 0) {
|
|
@@ -986,8 +1053,10 @@ var KnowledgeIndexer = class {
|
|
|
986
1053
|
endLine: currentLine - 1,
|
|
987
1054
|
hash: hashText(text)
|
|
988
1055
|
});
|
|
1056
|
+
const nonEmpty = text.split("\n").filter((l) => l.trim());
|
|
1057
|
+
overlapPrefix = nonEmpty.length > 0 ? nonEmpty.slice(-2).join("\n") + "\n" : "";
|
|
989
1058
|
}
|
|
990
|
-
currentChunk =
|
|
1059
|
+
currentChunk = overlapPrefix;
|
|
991
1060
|
startLine = currentLine;
|
|
992
1061
|
};
|
|
993
1062
|
for (const line of lines) {
|
|
@@ -1511,6 +1580,33 @@ var UserStore = class {
|
|
|
1511
1580
|
|
|
1512
1581
|
// src/memory/search/hybrid.ts
|
|
1513
1582
|
var log5 = createLogger("Memory");
|
|
1583
|
+
var UNIT_SECONDS = {
|
|
1584
|
+
hour: SECONDS_PER_HOUR,
|
|
1585
|
+
day: SECONDS_PER_DAY,
|
|
1586
|
+
week: 7 * SECONDS_PER_DAY,
|
|
1587
|
+
month: 30 * SECONDS_PER_DAY
|
|
1588
|
+
};
|
|
1589
|
+
function parseTemporalIntent(query) {
|
|
1590
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1591
|
+
const lower = query.toLowerCase();
|
|
1592
|
+
const agoMatch = lower.match(/(\d+)\s*(day|hour|week|month)s?\s*ago/);
|
|
1593
|
+
if (agoMatch) {
|
|
1594
|
+
const n = parseInt(agoMatch[1], 10);
|
|
1595
|
+
return { afterTimestamp: now - n * (UNIT_SECONDS[agoMatch[2]] ?? SECONDS_PER_DAY) };
|
|
1596
|
+
}
|
|
1597
|
+
const lastNMatch = lower.match(/last\s+(\d+)\s*(day|hour|week|month)s?/);
|
|
1598
|
+
if (lastNMatch) {
|
|
1599
|
+
const n = parseInt(lastNMatch[1], 10);
|
|
1600
|
+
return { afterTimestamp: now - n * (UNIT_SECONDS[lastNMatch[2]] ?? SECONDS_PER_DAY) };
|
|
1601
|
+
}
|
|
1602
|
+
if (/\btoday\b/.test(lower)) return { afterTimestamp: now - SECONDS_PER_DAY };
|
|
1603
|
+
if (/\byesterday\b/.test(lower)) return { afterTimestamp: now - 2 * SECONDS_PER_DAY };
|
|
1604
|
+
if (/\blast\s+week\b/.test(lower)) return { afterTimestamp: now - 7 * SECONDS_PER_DAY };
|
|
1605
|
+
if (/\bthis\s+week\b/.test(lower)) return { afterTimestamp: now - 7 * SECONDS_PER_DAY };
|
|
1606
|
+
if (/\blast\s+month\b/.test(lower)) return { afterTimestamp: now - 30 * SECONDS_PER_DAY };
|
|
1607
|
+
if (/\brecently?\b/.test(lower)) return { afterTimestamp: now - 3 * SECONDS_PER_DAY };
|
|
1608
|
+
return {};
|
|
1609
|
+
}
|
|
1514
1610
|
function escapeFts5Query(query) {
|
|
1515
1611
|
return query.replace(/["\*\-\+\(\)\:\^\~\?\.\@\#\$\%\&\!\[\]\{\}\|\\\/<>=,;'`]/g, " ").replace(/\s+/g, " ").trim();
|
|
1516
1612
|
}
|
|
@@ -1531,8 +1627,18 @@ var HybridSearch = class {
|
|
|
1531
1627
|
const limit = options.limit ?? 10;
|
|
1532
1628
|
const vectorWeight = options.vectorWeight ?? 0.5;
|
|
1533
1629
|
const keywordWeight = options.keywordWeight ?? 0.5;
|
|
1534
|
-
const vectorResults = this.vectorEnabled ? this.vectorSearchMessages(
|
|
1535
|
-
|
|
1630
|
+
const vectorResults = this.vectorEnabled ? this.vectorSearchMessages(
|
|
1631
|
+
queryEmbedding,
|
|
1632
|
+
Math.ceil(limit * 3),
|
|
1633
|
+
options.chatId,
|
|
1634
|
+
options.afterTimestamp
|
|
1635
|
+
) : [];
|
|
1636
|
+
const keywordResults = this.keywordSearchMessages(
|
|
1637
|
+
query,
|
|
1638
|
+
Math.ceil(limit * 3),
|
|
1639
|
+
options.chatId,
|
|
1640
|
+
options.afterTimestamp
|
|
1641
|
+
);
|
|
1536
1642
|
return this.mergeResults(vectorResults, keywordResults, vectorWeight, keywordWeight, limit);
|
|
1537
1643
|
}
|
|
1538
1644
|
vectorSearchKnowledge(embedding, limit) {
|
|
@@ -1541,7 +1647,7 @@ var HybridSearch = class {
|
|
|
1541
1647
|
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1542
1648
|
const rows = this.db.prepare(
|
|
1543
1649
|
`
|
|
1544
|
-
SELECT kv.id, k.text, k.source, kv.distance
|
|
1650
|
+
SELECT kv.id, k.text, k.source, kv.distance, k.created_at
|
|
1545
1651
|
FROM (
|
|
1546
1652
|
SELECT id, distance
|
|
1547
1653
|
FROM knowledge_vec
|
|
@@ -1555,7 +1661,8 @@ var HybridSearch = class {
|
|
|
1555
1661
|
text: row.text,
|
|
1556
1662
|
source: row.source,
|
|
1557
1663
|
score: 1 - row.distance,
|
|
1558
|
-
vectorScore: 1 - row.distance
|
|
1664
|
+
vectorScore: 1 - row.distance,
|
|
1665
|
+
createdAt: row.created_at ?? void 0
|
|
1559
1666
|
}));
|
|
1560
1667
|
} catch (error) {
|
|
1561
1668
|
log5.error({ err: error }, "Vector search error (knowledge)");
|
|
@@ -1568,7 +1675,7 @@ var HybridSearch = class {
|
|
|
1568
1675
|
try {
|
|
1569
1676
|
const rows = this.db.prepare(
|
|
1570
1677
|
`
|
|
1571
|
-
SELECT k.id, k.text, k.source, rank as score
|
|
1678
|
+
SELECT k.id, k.text, k.source, rank as score, k.created_at
|
|
1572
1679
|
FROM knowledge_fts kf
|
|
1573
1680
|
JOIN knowledge k ON k.rowid = kf.rowid
|
|
1574
1681
|
WHERE knowledge_fts MATCH ?
|
|
@@ -1578,72 +1685,82 @@ var HybridSearch = class {
|
|
|
1578
1685
|
).all(safeQuery, limit);
|
|
1579
1686
|
return rows.map((row) => ({
|
|
1580
1687
|
...row,
|
|
1581
|
-
keywordScore: this.bm25ToScore(row.score)
|
|
1688
|
+
keywordScore: this.bm25ToScore(row.score),
|
|
1689
|
+
createdAt: row.created_at ?? void 0
|
|
1582
1690
|
}));
|
|
1583
1691
|
} catch (error) {
|
|
1584
1692
|
log5.error({ err: error }, "FTS5 search error (knowledge)");
|
|
1585
1693
|
return [];
|
|
1586
1694
|
}
|
|
1587
1695
|
}
|
|
1588
|
-
vectorSearchMessages(embedding, limit, chatId) {
|
|
1696
|
+
vectorSearchMessages(embedding, limit, chatId, afterTimestamp) {
|
|
1589
1697
|
if (!this.vectorEnabled || embedding.length === 0) return [];
|
|
1590
1698
|
try {
|
|
1591
1699
|
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1592
|
-
const
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1700
|
+
const conditions = [];
|
|
1701
|
+
const params = [embeddingBuffer, limit];
|
|
1702
|
+
if (chatId) {
|
|
1703
|
+
conditions.push("m.chat_id = ?");
|
|
1704
|
+
params.push(chatId);
|
|
1705
|
+
}
|
|
1706
|
+
if (afterTimestamp) {
|
|
1707
|
+
conditions.push("m.timestamp >= ?");
|
|
1708
|
+
params.push(afterTimestamp);
|
|
1709
|
+
}
|
|
1710
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1711
|
+
const sql = `
|
|
1712
|
+
SELECT mv.id, m.text, m.chat_id as source, mv.distance, m.timestamp
|
|
1603
1713
|
FROM (
|
|
1604
1714
|
SELECT id, distance
|
|
1605
1715
|
FROM tg_messages_vec
|
|
1606
1716
|
WHERE embedding MATCH ? AND k = ?
|
|
1607
1717
|
) mv
|
|
1608
1718
|
JOIN tg_messages m ON m.id = mv.id
|
|
1719
|
+
${whereClause}
|
|
1609
1720
|
`;
|
|
1610
|
-
const rows =
|
|
1721
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1611
1722
|
return rows.map((row) => ({
|
|
1612
1723
|
id: row.id,
|
|
1613
1724
|
text: row.text ?? "",
|
|
1614
1725
|
source: row.source,
|
|
1615
1726
|
score: 1 - row.distance,
|
|
1616
|
-
vectorScore: 1 - row.distance
|
|
1727
|
+
vectorScore: 1 - row.distance,
|
|
1728
|
+
createdAt: row.timestamp ?? void 0
|
|
1617
1729
|
}));
|
|
1618
1730
|
} catch (error) {
|
|
1619
1731
|
log5.error({ err: error }, "Vector search error (messages)");
|
|
1620
1732
|
return [];
|
|
1621
1733
|
}
|
|
1622
1734
|
}
|
|
1623
|
-
keywordSearchMessages(query, limit, chatId) {
|
|
1735
|
+
keywordSearchMessages(query, limit, chatId, afterTimestamp) {
|
|
1624
1736
|
const safeQuery = escapeFts5Query(query);
|
|
1625
1737
|
if (!safeQuery) return [];
|
|
1626
1738
|
try {
|
|
1627
|
-
const
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1739
|
+
const conditions = ["tg_messages_fts MATCH ?"];
|
|
1740
|
+
const params = [safeQuery];
|
|
1741
|
+
if (chatId) {
|
|
1742
|
+
conditions.push("m.chat_id = ?");
|
|
1743
|
+
params.push(chatId);
|
|
1744
|
+
}
|
|
1745
|
+
if (afterTimestamp) {
|
|
1746
|
+
conditions.push("m.timestamp >= ?");
|
|
1747
|
+
params.push(afterTimestamp);
|
|
1748
|
+
}
|
|
1749
|
+
params.push(limit);
|
|
1750
|
+
const sql = `
|
|
1751
|
+
SELECT m.id, m.text, m.chat_id as source, rank as score, m.timestamp
|
|
1636
1752
|
FROM tg_messages_fts mf
|
|
1637
1753
|
JOIN tg_messages m ON m.rowid = mf.rowid
|
|
1638
|
-
WHERE
|
|
1754
|
+
WHERE ${conditions.join(" AND ")}
|
|
1639
1755
|
ORDER BY rank
|
|
1640
1756
|
LIMIT ?
|
|
1641
1757
|
`;
|
|
1642
|
-
const rows =
|
|
1758
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1643
1759
|
return rows.map((row) => ({
|
|
1644
1760
|
...row,
|
|
1645
1761
|
text: row.text ?? "",
|
|
1646
|
-
keywordScore: this.bm25ToScore(row.score)
|
|
1762
|
+
keywordScore: this.bm25ToScore(row.score),
|
|
1763
|
+
createdAt: row.timestamp ?? void 0
|
|
1647
1764
|
}));
|
|
1648
1765
|
} catch (error) {
|
|
1649
1766
|
log5.error({ err: error }, "FTS5 search error (messages)");
|
|
@@ -1664,7 +1781,16 @@ var HybridSearch = class {
|
|
|
1664
1781
|
byId.set(r.id, { ...r, score: keywordWeight * (r.keywordScore ?? 0) });
|
|
1665
1782
|
}
|
|
1666
1783
|
}
|
|
1667
|
-
|
|
1784
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1785
|
+
const results = Array.from(byId.values());
|
|
1786
|
+
for (const r of results) {
|
|
1787
|
+
if (r.createdAt) {
|
|
1788
|
+
const ageDays = Math.max(0, (now - r.createdAt) / SECONDS_PER_DAY);
|
|
1789
|
+
const boost = 1 / (1 + ageDays * RECENCY_DECAY_FACTOR);
|
|
1790
|
+
r.score *= 1 - RECENCY_WEIGHT + RECENCY_WEIGHT * boost;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
return results.filter((r) => r.score >= HYBRID_SEARCH_MIN_SCORE).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
1668
1794
|
}
|
|
1669
1795
|
/**
|
|
1670
1796
|
* Convert BM25 rank to normalized score.
|
|
@@ -1720,33 +1846,37 @@ var ContextBuilder = class {
|
|
|
1720
1846
|
role: m.isFromAgent ? "assistant" : "user",
|
|
1721
1847
|
content: m.text ?? ""
|
|
1722
1848
|
}));
|
|
1723
|
-
const relevantKnowledge = [];
|
|
1724
|
-
if (includeAgentMemory) {
|
|
1725
|
-
try {
|
|
1726
|
-
const knowledgeResults = await this.hybridSearch.searchKnowledge(query, queryEmbedding, {
|
|
1727
|
-
limit: maxRelevantChunks
|
|
1728
|
-
});
|
|
1729
|
-
relevantKnowledge.push(...reorderForEdges(knowledgeResults.map((r) => r.text)));
|
|
1730
|
-
} catch (error) {
|
|
1731
|
-
log6.warn({ err: error }, "Knowledge search failed");
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
1849
|
const recentTextsSet = new Set(
|
|
1850
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- filtered for non-null text above
|
|
1735
1851
|
recentTgMessages.filter((m) => m.text && m.text.length > 0).map((m) => m.text)
|
|
1736
1852
|
);
|
|
1853
|
+
const knowledgePromise = includeAgentMemory ? this.hybridSearch.searchKnowledge(query, queryEmbedding, { limit: maxRelevantChunks }).catch((error) => {
|
|
1854
|
+
log6.warn({ err: error }, "Knowledge search failed");
|
|
1855
|
+
return [];
|
|
1856
|
+
}) : Promise.resolve([]);
|
|
1857
|
+
const { afterTimestamp } = parseTemporalIntent(query);
|
|
1858
|
+
const feedPromise = includeFeedHistory ? this.hybridSearch.searchMessages(query, queryEmbedding, {
|
|
1859
|
+
chatId,
|
|
1860
|
+
limit: maxRelevantChunks,
|
|
1861
|
+
afterTimestamp
|
|
1862
|
+
}).catch((error) => {
|
|
1863
|
+
log6.warn({ err: error }, "Feed search failed");
|
|
1864
|
+
return [];
|
|
1865
|
+
}) : Promise.resolve([]);
|
|
1866
|
+
const [knowledgeResults, feedResults] = await Promise.all([knowledgePromise, feedPromise]);
|
|
1867
|
+
const relevantKnowledge = [];
|
|
1868
|
+
if (knowledgeResults.length > 0) {
|
|
1869
|
+
relevantKnowledge.push(...reorderForEdges(knowledgeResults.map((r) => r.text)));
|
|
1870
|
+
}
|
|
1737
1871
|
const relevantFeed = [];
|
|
1738
1872
|
if (includeFeedHistory) {
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
limit: maxRelevantChunks
|
|
1743
|
-
});
|
|
1744
|
-
for (const r of feedResults) {
|
|
1745
|
-
if (!recentTextsSet.has(r.text)) {
|
|
1746
|
-
relevantFeed.push(truncateFeedMessage(r.text));
|
|
1747
|
-
}
|
|
1873
|
+
for (const r of feedResults) {
|
|
1874
|
+
if (!recentTextsSet.has(r.text)) {
|
|
1875
|
+
relevantFeed.push(truncateFeedMessage(r.text));
|
|
1748
1876
|
}
|
|
1749
|
-
|
|
1877
|
+
}
|
|
1878
|
+
if (searchAllChats) {
|
|
1879
|
+
try {
|
|
1750
1880
|
const globalResults = await this.hybridSearch.searchMessages(query, queryEmbedding, {
|
|
1751
1881
|
limit: maxRelevantChunks
|
|
1752
1882
|
});
|
|
@@ -1757,9 +1887,9 @@ var ContextBuilder = class {
|
|
|
1757
1887
|
relevantFeed.push(`[From chat ${r.source}]: ${truncated}`);
|
|
1758
1888
|
}
|
|
1759
1889
|
}
|
|
1890
|
+
} catch (error) {
|
|
1891
|
+
log6.warn({ err: error }, "Global feed search failed");
|
|
1760
1892
|
}
|
|
1761
|
-
} catch (error) {
|
|
1762
|
-
log6.warn({ err: error }, "Feed search failed");
|
|
1763
1893
|
}
|
|
1764
1894
|
if (relevantFeed.length === 0 && recentTgMessages.length > 0) {
|
|
1765
1895
|
const recentTexts = recentTgMessages.filter((m) => m.text && m.text.length > 0).slice(-maxRelevantChunks).map((m) => {
|
|
@@ -1816,6 +1946,7 @@ export {
|
|
|
1816
1946
|
MessageStore,
|
|
1817
1947
|
ChatStore,
|
|
1818
1948
|
UserStore,
|
|
1949
|
+
parseTemporalIntent,
|
|
1819
1950
|
HybridSearch,
|
|
1820
1951
|
ContextBuilder,
|
|
1821
1952
|
initializeMemory
|