teleton 0.8.1 → 0.8.3
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/dist/bootstrap-DDFVEMYI.js +128 -0
- package/dist/{server-3FHI2SEB.js → chunk-2ERTYRHA.js} +26 -372
- package/dist/{chunk-5FNWBZ5K.js → chunk-33Z47EXI.js} +264 -274
- package/dist/{chunk-3S4GGLLR.js → chunk-35MX4ZUI.js} +23 -104
- package/dist/chunk-3UFPFWYP.js +12 -0
- package/dist/chunk-5SEMA47R.js +75 -0
- package/dist/{chunk-PHSAHTK4.js → chunk-6OOHHJ4N.js} +3 -108
- package/dist/{chunk-CGOXE4WP.js → chunk-7MWKT67G.js} +467 -914
- package/dist/chunk-AEHTQI3H.js +142 -0
- package/dist/{chunk-S6PHGKOC.js → chunk-AERHOXGC.js} +88 -322
- package/dist/chunk-ALKAAG4O.js +487 -0
- package/dist/{chunk-UP55PXFH.js → chunk-C4NKJT2Z.js} +8 -0
- package/dist/chunk-CUE4UZXR.js +129 -0
- package/dist/chunk-FUNF6H4W.js +251 -0
- package/dist/{chunk-7U7BOHCL.js → chunk-GHMXWAXI.js} +147 -63
- package/dist/{chunk-QBHRXLZS.js → chunk-H7MFXJZK.js} +2 -2
- package/dist/{chunk-QV2GLOTK.js → chunk-LC4TV3KL.js} +1 -1
- package/dist/{chunk-AYWEJCDB.js → chunk-LVTKJQ7O.js} +12 -10
- package/dist/{chunk-RCMD3U65.js → chunk-NQ6FZKCE.js} +13 -0
- package/dist/chunk-NVKBBTI6.js +128 -0
- package/dist/{setup-server-32XGDPE6.js → chunk-OIMAE24Q.js} +55 -216
- package/dist/{chunk-OJCLKU5Z.js → chunk-WFTC3JJW.js} +16 -0
- package/dist/chunk-WTDAICGT.js +175 -0
- package/dist/{chunk-KVXV7EF7.js → chunk-XDZDOKIF.js} +2 -2
- package/dist/cli/index.js +91 -27
- package/dist/{client-MPHPIZB6.js → client-5KD25NOP.js} +5 -4
- package/dist/{get-my-gifts-CC6HAVWB.js → get-my-gifts-Y7EN7RK4.js} +3 -3
- package/dist/index.js +19 -13
- package/dist/local-IHKJFQJS.js +9 -0
- package/dist/{memory-UBHM7ILG.js → memory-QMJRM3XJ.js} +9 -5
- package/dist/memory-hook-VUNWZ3NY.js +19 -0
- package/dist/{migrate-UBBEJ5BL.js → migrate-5VBAP52B.js} +5 -4
- package/dist/server-JF6FX772.js +813 -0
- package/dist/server-N4T7E25M.js +396 -0
- package/dist/setup-server-IX3BFPPH.js +217 -0
- package/dist/{store-M5IMUQCL.js → store-BY7S6IFN.js} +6 -5
- package/dist/{task-dependency-resolver-RR2O5S7B.js → task-dependency-resolver-L6UUMTHK.js} +2 -2
- package/dist/{task-executor-6W5HRX5C.js → task-executor-XBNJLUCS.js} +2 -2
- package/dist/{tool-adapter-IH5VGBOO.js → tool-adapter-IVX2XQJE.js} +1 -1
- package/dist/{tool-index-PMAOXWUA.js → tool-index-FTERJSZK.js} +4 -3
- package/dist/{transcript-NGDPSNIH.js → transcript-IM7G25OS.js} +2 -2
- package/package.json +4 -2
- package/dist/chunk-XBE4JB7C.js +0 -8
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COINGECKO_API_URL,
|
|
3
|
+
tonapiFetch
|
|
4
|
+
} from "./chunk-VFA7QMCZ.js";
|
|
5
|
+
import {
|
|
6
|
+
fetchWithTimeout
|
|
7
|
+
} from "./chunk-XQUHC3JZ.js";
|
|
8
|
+
import {
|
|
9
|
+
TELETON_ROOT
|
|
10
|
+
} from "./chunk-EYWNOHMJ.js";
|
|
11
|
+
import {
|
|
12
|
+
createLogger
|
|
13
|
+
} from "./chunk-NQ6FZKCE.js";
|
|
14
|
+
|
|
15
|
+
// src/ton/endpoint.ts
|
|
16
|
+
var ENDPOINT_CACHE_TTL_MS = 6e4;
|
|
17
|
+
var ORBS_HOST = "ton.access.orbs.network";
|
|
18
|
+
var ORBS_TOPOLOGY_URL = `https://${ORBS_HOST}/mngr/nodes?npm_version=2.3.3`;
|
|
19
|
+
var TONCENTER_URL = `https://toncenter.com/api/v2/jsonRPC`;
|
|
20
|
+
var _cache = null;
|
|
21
|
+
var _toncenterApiKey;
|
|
22
|
+
function setToncenterApiKey(key) {
|
|
23
|
+
_toncenterApiKey = key;
|
|
24
|
+
}
|
|
25
|
+
function getToncenterApiKey() {
|
|
26
|
+
return _toncenterApiKey;
|
|
27
|
+
}
|
|
28
|
+
async function discoverOrbsEndpoint() {
|
|
29
|
+
const res = await fetch(ORBS_TOPOLOGY_URL, { signal: AbortSignal.timeout(5e3) });
|
|
30
|
+
const nodes = await res.json();
|
|
31
|
+
const healthy = nodes.filter(
|
|
32
|
+
(n) => n.Healthy === "1" && n.Weight > 0 && n.Mngr?.health?.["v2-mainnet"]
|
|
33
|
+
);
|
|
34
|
+
if (healthy.length === 0) throw new Error("no healthy orbs nodes");
|
|
35
|
+
const totalWeight = healthy.reduce((sum, n) => sum + n.Weight, 0);
|
|
36
|
+
let r = Math.floor(Math.random() * totalWeight);
|
|
37
|
+
let chosen = healthy[0];
|
|
38
|
+
for (const node of healthy) {
|
|
39
|
+
r -= node.Weight;
|
|
40
|
+
if (r < 0) {
|
|
41
|
+
chosen = node;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return `https://${ORBS_HOST}/${chosen.NodeId}/1/mainnet/toncenter-api-v2/jsonRPC`;
|
|
46
|
+
}
|
|
47
|
+
async function getCachedHttpEndpoint() {
|
|
48
|
+
if (_cache && Date.now() - _cache.ts < ENDPOINT_CACHE_TTL_MS) {
|
|
49
|
+
return _cache.url;
|
|
50
|
+
}
|
|
51
|
+
let url;
|
|
52
|
+
if (_toncenterApiKey) {
|
|
53
|
+
try {
|
|
54
|
+
const testUrl = `https://toncenter.com/api/v2/getAddressInformation?address=EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c`;
|
|
55
|
+
const res = await fetch(testUrl, {
|
|
56
|
+
headers: { "X-API-Key": _toncenterApiKey },
|
|
57
|
+
signal: AbortSignal.timeout(5e3)
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) throw new Error(`TonCenter ${res.status}`);
|
|
60
|
+
url = TONCENTER_URL;
|
|
61
|
+
} catch {
|
|
62
|
+
try {
|
|
63
|
+
url = await discoverOrbsEndpoint();
|
|
64
|
+
} catch {
|
|
65
|
+
url = TONCENTER_URL;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
try {
|
|
70
|
+
url = await discoverOrbsEndpoint();
|
|
71
|
+
} catch {
|
|
72
|
+
url = TONCENTER_URL;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
_cache = { url, ts: Date.now() };
|
|
76
|
+
return url;
|
|
77
|
+
}
|
|
78
|
+
function invalidateEndpointCache() {
|
|
79
|
+
_cache = null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/ton/wallet-service.ts
|
|
83
|
+
import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto";
|
|
84
|
+
import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
|
|
85
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
86
|
+
import { join, dirname } from "path";
|
|
87
|
+
var log = createLogger("TON");
|
|
88
|
+
var WALLET_FILE = join(TELETON_ROOT, "wallet.json");
|
|
89
|
+
var _walletCache;
|
|
90
|
+
var _keyPairCache = null;
|
|
91
|
+
var _tonClientCache = null;
|
|
92
|
+
async function generateWallet() {
|
|
93
|
+
const mnemonic = await mnemonicNew(24);
|
|
94
|
+
const keyPair = await mnemonicToPrivateKey(mnemonic);
|
|
95
|
+
const wallet = WalletContractV5R1.create({
|
|
96
|
+
workchain: 0,
|
|
97
|
+
publicKey: keyPair.publicKey
|
|
98
|
+
});
|
|
99
|
+
const address = wallet.address.toString({ bounceable: true, testOnly: false });
|
|
100
|
+
return {
|
|
101
|
+
version: "w5r1",
|
|
102
|
+
address,
|
|
103
|
+
publicKey: keyPair.publicKey.toString("hex"),
|
|
104
|
+
mnemonic,
|
|
105
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function saveWallet(wallet) {
|
|
109
|
+
const dir = dirname(WALLET_FILE);
|
|
110
|
+
if (!existsSync(dir)) {
|
|
111
|
+
mkdirSync(dir, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
writeFileSync(WALLET_FILE, JSON.stringify(wallet, null, 2), { encoding: "utf-8", mode: 384 });
|
|
114
|
+
_walletCache = void 0;
|
|
115
|
+
_keyPairCache = null;
|
|
116
|
+
}
|
|
117
|
+
function loadWallet() {
|
|
118
|
+
if (_walletCache !== void 0) return _walletCache;
|
|
119
|
+
if (!existsSync(WALLET_FILE)) {
|
|
120
|
+
_walletCache = null;
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const content = readFileSync(WALLET_FILE, "utf-8");
|
|
125
|
+
const parsed = JSON.parse(content);
|
|
126
|
+
if (!parsed.mnemonic || !Array.isArray(parsed.mnemonic) || parsed.mnemonic.length !== 24) {
|
|
127
|
+
throw new Error("Invalid wallet.json: mnemonic must be a 24-word array");
|
|
128
|
+
}
|
|
129
|
+
_walletCache = parsed;
|
|
130
|
+
return _walletCache;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
log.error({ err: error }, "Failed to load wallet");
|
|
133
|
+
_walletCache = null;
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function walletExists() {
|
|
138
|
+
return existsSync(WALLET_FILE);
|
|
139
|
+
}
|
|
140
|
+
async function importWallet(mnemonic) {
|
|
141
|
+
const valid = await mnemonicValidate(mnemonic);
|
|
142
|
+
if (!valid) {
|
|
143
|
+
throw new Error("Invalid mnemonic: words do not form a valid TON seed phrase");
|
|
144
|
+
}
|
|
145
|
+
const keyPair = await mnemonicToPrivateKey(mnemonic);
|
|
146
|
+
const wallet = WalletContractV5R1.create({
|
|
147
|
+
workchain: 0,
|
|
148
|
+
publicKey: keyPair.publicKey
|
|
149
|
+
});
|
|
150
|
+
const address = wallet.address.toString({ bounceable: true, testOnly: false });
|
|
151
|
+
return {
|
|
152
|
+
version: "w5r1",
|
|
153
|
+
address,
|
|
154
|
+
publicKey: keyPair.publicKey.toString("hex"),
|
|
155
|
+
mnemonic,
|
|
156
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function getWalletAddress() {
|
|
160
|
+
const wallet = loadWallet();
|
|
161
|
+
return wallet?.address || null;
|
|
162
|
+
}
|
|
163
|
+
async function getCachedTonClient() {
|
|
164
|
+
const endpoint = await getCachedHttpEndpoint();
|
|
165
|
+
if (_tonClientCache && _tonClientCache.endpoint === endpoint) {
|
|
166
|
+
return _tonClientCache.client;
|
|
167
|
+
}
|
|
168
|
+
const apiKey = getToncenterApiKey();
|
|
169
|
+
const client = new TonClient({ endpoint, ...apiKey && { apiKey } });
|
|
170
|
+
_tonClientCache = { client, endpoint };
|
|
171
|
+
return client;
|
|
172
|
+
}
|
|
173
|
+
function invalidateTonClientCache() {
|
|
174
|
+
_tonClientCache = null;
|
|
175
|
+
invalidateEndpointCache();
|
|
176
|
+
}
|
|
177
|
+
async function getKeyPair() {
|
|
178
|
+
if (_keyPairCache) return _keyPairCache;
|
|
179
|
+
const wallet = loadWallet();
|
|
180
|
+
if (!wallet) return null;
|
|
181
|
+
_keyPairCache = await mnemonicToPrivateKey(wallet.mnemonic);
|
|
182
|
+
return _keyPairCache;
|
|
183
|
+
}
|
|
184
|
+
async function getWalletBalance(address) {
|
|
185
|
+
try {
|
|
186
|
+
const client = await getCachedTonClient();
|
|
187
|
+
const { Address } = await import("@ton/core");
|
|
188
|
+
const addressObj = Address.parse(address);
|
|
189
|
+
const balance = await client.getBalance(addressObj);
|
|
190
|
+
const balanceFormatted = fromNano(balance);
|
|
191
|
+
return {
|
|
192
|
+
balance: balanceFormatted,
|
|
193
|
+
balanceNano: balance.toString()
|
|
194
|
+
};
|
|
195
|
+
} catch (error) {
|
|
196
|
+
log.error({ err: error }, "Failed to get balance");
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
var TON_PRICE_CACHE_TTL_MS = 3e4;
|
|
201
|
+
var _tonPriceCache = null;
|
|
202
|
+
async function getTonPrice() {
|
|
203
|
+
if (_tonPriceCache && Date.now() - _tonPriceCache.timestamp < TON_PRICE_CACHE_TTL_MS) {
|
|
204
|
+
return { ..._tonPriceCache };
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const response = await tonapiFetch(`/rates?tokens=ton¤cies=usd`);
|
|
208
|
+
if (response.ok) {
|
|
209
|
+
const data = await response.json();
|
|
210
|
+
const price = data?.rates?.TON?.prices?.USD;
|
|
211
|
+
if (typeof price === "number" && price > 0) {
|
|
212
|
+
_tonPriceCache = { usd: price, source: "TonAPI", timestamp: Date.now() };
|
|
213
|
+
return _tonPriceCache;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const response = await fetchWithTimeout(
|
|
220
|
+
`${COINGECKO_API_URL}/simple/price?ids=the-open-network&vs_currencies=usd`
|
|
221
|
+
);
|
|
222
|
+
if (!response.ok) {
|
|
223
|
+
throw new Error(`CoinGecko API error: ${response.status}`);
|
|
224
|
+
}
|
|
225
|
+
const data = await response.json();
|
|
226
|
+
const price = data["the-open-network"]?.usd;
|
|
227
|
+
if (typeof price === "number" && price > 0) {
|
|
228
|
+
_tonPriceCache = { usd: price, source: "CoinGecko", timestamp: Date.now() };
|
|
229
|
+
return _tonPriceCache;
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
log.error({ err: error }, "Failed to get TON price");
|
|
233
|
+
}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export {
|
|
238
|
+
setToncenterApiKey,
|
|
239
|
+
invalidateEndpointCache,
|
|
240
|
+
generateWallet,
|
|
241
|
+
saveWallet,
|
|
242
|
+
loadWallet,
|
|
243
|
+
walletExists,
|
|
244
|
+
importWallet,
|
|
245
|
+
getWalletAddress,
|
|
246
|
+
getCachedTonClient,
|
|
247
|
+
invalidateTonClientCache,
|
|
248
|
+
getKeyPair,
|
|
249
|
+
getWalletBalance,
|
|
250
|
+
getTonPrice
|
|
251
|
+
};
|
|
@@ -3,20 +3,24 @@ import {
|
|
|
3
3
|
createEmbeddingProvider,
|
|
4
4
|
hashText,
|
|
5
5
|
serializeEmbedding
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-35MX4ZUI.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";
|
|
@@ -456,9 +460,11 @@ function ensureVectorTables(db, dimensions) {
|
|
|
456
460
|
WHERE type='table' AND name='knowledge_vec'
|
|
457
461
|
`
|
|
458
462
|
).get();
|
|
463
|
+
let dimensionsChanged = false;
|
|
459
464
|
if (existingDims?.sql && !existingDims.sql.includes(`[${dimensions}]`)) {
|
|
460
465
|
db.exec(`DROP TABLE IF EXISTS knowledge_vec`);
|
|
461
466
|
db.exec(`DROP TABLE IF EXISTS tg_messages_vec`);
|
|
467
|
+
dimensionsChanged = true;
|
|
462
468
|
}
|
|
463
469
|
db.exec(`
|
|
464
470
|
CREATE VIRTUAL TABLE IF NOT EXISTS knowledge_vec USING vec0(
|
|
@@ -471,6 +477,7 @@ function ensureVectorTables(db, dimensions) {
|
|
|
471
477
|
embedding FLOAT[${dimensions}] distance_metric=cosine
|
|
472
478
|
);
|
|
473
479
|
`);
|
|
480
|
+
return dimensionsChanged;
|
|
474
481
|
}
|
|
475
482
|
function getSchemaVersion(db) {
|
|
476
483
|
const row = db.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get();
|
|
@@ -762,6 +769,7 @@ var MemoryDatabase = class {
|
|
|
762
769
|
db;
|
|
763
770
|
config;
|
|
764
771
|
vectorReady = false;
|
|
772
|
+
_dimensionsChanged = false;
|
|
765
773
|
constructor(config) {
|
|
766
774
|
this.config = config;
|
|
767
775
|
const dir = dirname2(config.path);
|
|
@@ -773,7 +781,8 @@ var MemoryDatabase = class {
|
|
|
773
781
|
});
|
|
774
782
|
try {
|
|
775
783
|
chmodSync2(config.path, 384);
|
|
776
|
-
} catch {
|
|
784
|
+
} catch (err) {
|
|
785
|
+
log3.warn({ err, path: config.path }, "Failed to set DB file permissions to 0o600");
|
|
777
786
|
}
|
|
778
787
|
this.db.pragma("journal_mode = WAL");
|
|
779
788
|
this.db.pragma("synchronous = NORMAL");
|
|
@@ -787,7 +796,8 @@ var MemoryDatabase = class {
|
|
|
787
796
|
let currentVersion = null;
|
|
788
797
|
try {
|
|
789
798
|
currentVersion = getSchemaVersion(this.db);
|
|
790
|
-
} catch {
|
|
799
|
+
} catch (err) {
|
|
800
|
+
log3.warn({ err }, "Could not read schema version, assuming fresh database");
|
|
791
801
|
currentVersion = null;
|
|
792
802
|
}
|
|
793
803
|
if (!currentVersion) {
|
|
@@ -806,7 +816,7 @@ var MemoryDatabase = class {
|
|
|
806
816
|
sqliteVec.load(this.db);
|
|
807
817
|
this.db.prepare("SELECT vec_version() as vec_version").get();
|
|
808
818
|
const dims = this.config.vectorDimensions ?? 512;
|
|
809
|
-
ensureVectorTables(this.db, dims);
|
|
819
|
+
this._dimensionsChanged = ensureVectorTables(this.db, dims);
|
|
810
820
|
this.vectorReady = true;
|
|
811
821
|
} catch (error) {
|
|
812
822
|
log3.warn(`sqlite-vec not available, vector search disabled: ${error.message}`);
|
|
@@ -826,6 +836,9 @@ var MemoryDatabase = class {
|
|
|
826
836
|
isVectorSearchReady() {
|
|
827
837
|
return this.vectorReady;
|
|
828
838
|
}
|
|
839
|
+
didDimensionsChange() {
|
|
840
|
+
return this._dimensionsChanged;
|
|
841
|
+
}
|
|
829
842
|
getVectorDimensions() {
|
|
830
843
|
return this.config.vectorDimensions;
|
|
831
844
|
}
|
|
@@ -847,21 +860,24 @@ var MemoryDatabase = class {
|
|
|
847
860
|
}
|
|
848
861
|
}
|
|
849
862
|
getStats() {
|
|
850
|
-
const
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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();
|
|
857
873
|
return {
|
|
858
|
-
knowledge: knowledge
|
|
859
|
-
sessions: sessions
|
|
860
|
-
tasks: tasks
|
|
861
|
-
tgChats:
|
|
862
|
-
tgUsers:
|
|
863
|
-
tgMessages:
|
|
864
|
-
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,
|
|
865
881
|
vectorSearchEnabled: this.vectorReady
|
|
866
882
|
};
|
|
867
883
|
}
|
|
@@ -929,12 +945,12 @@ var KnowledgeIndexer = class {
|
|
|
929
945
|
this.embedder = embedder;
|
|
930
946
|
this.vectorEnabled = vectorEnabled;
|
|
931
947
|
}
|
|
932
|
-
async indexAll() {
|
|
948
|
+
async indexAll(options) {
|
|
933
949
|
const files = this.listMemoryFiles();
|
|
934
950
|
let indexed = 0;
|
|
935
951
|
let skipped = 0;
|
|
936
952
|
for (const file of files) {
|
|
937
|
-
const wasIndexed = await this.indexFile(file);
|
|
953
|
+
const wasIndexed = await this.indexFile(file, options?.force);
|
|
938
954
|
if (wasIndexed) {
|
|
939
955
|
indexed++;
|
|
940
956
|
} else {
|
|
@@ -943,16 +959,18 @@ var KnowledgeIndexer = class {
|
|
|
943
959
|
}
|
|
944
960
|
return { indexed, skipped };
|
|
945
961
|
}
|
|
946
|
-
async indexFile(absPath) {
|
|
962
|
+
async indexFile(absPath, force) {
|
|
947
963
|
if (!existsSync3(absPath) || !absPath.endsWith(".md")) {
|
|
948
964
|
return false;
|
|
949
965
|
}
|
|
950
966
|
const content = readFileSync(absPath, "utf-8");
|
|
951
967
|
const relPath = absPath.replace(this.workspaceDir + "/", "");
|
|
952
968
|
const fileHash = hashText(content);
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
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
|
+
}
|
|
956
974
|
}
|
|
957
975
|
const chunks = this.chunkMarkdown(content, relPath);
|
|
958
976
|
const texts = chunks.map((c) => c.text);
|
|
@@ -981,7 +999,7 @@ var KnowledgeIndexer = class {
|
|
|
981
999
|
serializeEmbedding(embedding),
|
|
982
1000
|
chunk.startLine,
|
|
983
1001
|
chunk.endLine,
|
|
984
|
-
|
|
1002
|
+
fileHash
|
|
985
1003
|
);
|
|
986
1004
|
if (insertVec && embedding.length > 0) {
|
|
987
1005
|
insertVec.run(chunk.id, serializeEmbedding(embedding));
|
|
@@ -1022,6 +1040,7 @@ var KnowledgeIndexer = class {
|
|
|
1022
1040
|
let startLine = 1;
|
|
1023
1041
|
let currentLine = 1;
|
|
1024
1042
|
let inCodeBlock = false;
|
|
1043
|
+
let overlapPrefix = "";
|
|
1025
1044
|
const flushChunk = () => {
|
|
1026
1045
|
const text = currentChunk.trim();
|
|
1027
1046
|
if (text.length > 0) {
|
|
@@ -1034,8 +1053,10 @@ var KnowledgeIndexer = class {
|
|
|
1034
1053
|
endLine: currentLine - 1,
|
|
1035
1054
|
hash: hashText(text)
|
|
1036
1055
|
});
|
|
1056
|
+
const nonEmpty = text.split("\n").filter((l) => l.trim());
|
|
1057
|
+
overlapPrefix = nonEmpty.length > 0 ? nonEmpty.slice(-2).join("\n") + "\n" : "";
|
|
1037
1058
|
}
|
|
1038
|
-
currentChunk =
|
|
1059
|
+
currentChunk = overlapPrefix;
|
|
1039
1060
|
startLine = currentLine;
|
|
1040
1061
|
};
|
|
1041
1062
|
for (const line of lines) {
|
|
@@ -1559,6 +1580,33 @@ var UserStore = class {
|
|
|
1559
1580
|
|
|
1560
1581
|
// src/memory/search/hybrid.ts
|
|
1561
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
|
+
}
|
|
1562
1610
|
function escapeFts5Query(query) {
|
|
1563
1611
|
return query.replace(/["\*\-\+\(\)\:\^\~\?\.\@\#\$\%\&\!\[\]\{\}\|\\\/<>=,;'`]/g, " ").replace(/\s+/g, " ").trim();
|
|
1564
1612
|
}
|
|
@@ -1579,8 +1627,18 @@ var HybridSearch = class {
|
|
|
1579
1627
|
const limit = options.limit ?? 10;
|
|
1580
1628
|
const vectorWeight = options.vectorWeight ?? 0.5;
|
|
1581
1629
|
const keywordWeight = options.keywordWeight ?? 0.5;
|
|
1582
|
-
const vectorResults = this.vectorEnabled ? this.vectorSearchMessages(
|
|
1583
|
-
|
|
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
|
+
);
|
|
1584
1642
|
return this.mergeResults(vectorResults, keywordResults, vectorWeight, keywordWeight, limit);
|
|
1585
1643
|
}
|
|
1586
1644
|
vectorSearchKnowledge(embedding, limit) {
|
|
@@ -1589,7 +1647,7 @@ var HybridSearch = class {
|
|
|
1589
1647
|
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1590
1648
|
const rows = this.db.prepare(
|
|
1591
1649
|
`
|
|
1592
|
-
SELECT kv.id, k.text, k.source, kv.distance
|
|
1650
|
+
SELECT kv.id, k.text, k.source, kv.distance, k.created_at
|
|
1593
1651
|
FROM (
|
|
1594
1652
|
SELECT id, distance
|
|
1595
1653
|
FROM knowledge_vec
|
|
@@ -1603,7 +1661,8 @@ var HybridSearch = class {
|
|
|
1603
1661
|
text: row.text,
|
|
1604
1662
|
source: row.source,
|
|
1605
1663
|
score: 1 - row.distance,
|
|
1606
|
-
vectorScore: 1 - row.distance
|
|
1664
|
+
vectorScore: 1 - row.distance,
|
|
1665
|
+
createdAt: row.created_at ?? void 0
|
|
1607
1666
|
}));
|
|
1608
1667
|
} catch (error) {
|
|
1609
1668
|
log5.error({ err: error }, "Vector search error (knowledge)");
|
|
@@ -1616,7 +1675,7 @@ var HybridSearch = class {
|
|
|
1616
1675
|
try {
|
|
1617
1676
|
const rows = this.db.prepare(
|
|
1618
1677
|
`
|
|
1619
|
-
SELECT k.id, k.text, k.source, rank as score
|
|
1678
|
+
SELECT k.id, k.text, k.source, rank as score, k.created_at
|
|
1620
1679
|
FROM knowledge_fts kf
|
|
1621
1680
|
JOIN knowledge k ON k.rowid = kf.rowid
|
|
1622
1681
|
WHERE knowledge_fts MATCH ?
|
|
@@ -1626,72 +1685,82 @@ var HybridSearch = class {
|
|
|
1626
1685
|
).all(safeQuery, limit);
|
|
1627
1686
|
return rows.map((row) => ({
|
|
1628
1687
|
...row,
|
|
1629
|
-
keywordScore: this.bm25ToScore(row.score)
|
|
1688
|
+
keywordScore: this.bm25ToScore(row.score),
|
|
1689
|
+
createdAt: row.created_at ?? void 0
|
|
1630
1690
|
}));
|
|
1631
1691
|
} catch (error) {
|
|
1632
1692
|
log5.error({ err: error }, "FTS5 search error (knowledge)");
|
|
1633
1693
|
return [];
|
|
1634
1694
|
}
|
|
1635
1695
|
}
|
|
1636
|
-
vectorSearchMessages(embedding, limit, chatId) {
|
|
1696
|
+
vectorSearchMessages(embedding, limit, chatId, afterTimestamp) {
|
|
1637
1697
|
if (!this.vectorEnabled || embedding.length === 0) return [];
|
|
1638
1698
|
try {
|
|
1639
1699
|
const embeddingBuffer = serializeEmbedding(embedding);
|
|
1640
|
-
const
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
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
|
|
1651
1713
|
FROM (
|
|
1652
1714
|
SELECT id, distance
|
|
1653
1715
|
FROM tg_messages_vec
|
|
1654
1716
|
WHERE embedding MATCH ? AND k = ?
|
|
1655
1717
|
) mv
|
|
1656
1718
|
JOIN tg_messages m ON m.id = mv.id
|
|
1719
|
+
${whereClause}
|
|
1657
1720
|
`;
|
|
1658
|
-
const rows =
|
|
1721
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1659
1722
|
return rows.map((row) => ({
|
|
1660
1723
|
id: row.id,
|
|
1661
1724
|
text: row.text ?? "",
|
|
1662
1725
|
source: row.source,
|
|
1663
1726
|
score: 1 - row.distance,
|
|
1664
|
-
vectorScore: 1 - row.distance
|
|
1727
|
+
vectorScore: 1 - row.distance,
|
|
1728
|
+
createdAt: row.timestamp ?? void 0
|
|
1665
1729
|
}));
|
|
1666
1730
|
} catch (error) {
|
|
1667
1731
|
log5.error({ err: error }, "Vector search error (messages)");
|
|
1668
1732
|
return [];
|
|
1669
1733
|
}
|
|
1670
1734
|
}
|
|
1671
|
-
keywordSearchMessages(query, limit, chatId) {
|
|
1735
|
+
keywordSearchMessages(query, limit, chatId, afterTimestamp) {
|
|
1672
1736
|
const safeQuery = escapeFts5Query(query);
|
|
1673
1737
|
if (!safeQuery) return [];
|
|
1674
1738
|
try {
|
|
1675
|
-
const
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
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
|
|
1684
1752
|
FROM tg_messages_fts mf
|
|
1685
1753
|
JOIN tg_messages m ON m.rowid = mf.rowid
|
|
1686
|
-
WHERE
|
|
1754
|
+
WHERE ${conditions.join(" AND ")}
|
|
1687
1755
|
ORDER BY rank
|
|
1688
1756
|
LIMIT ?
|
|
1689
1757
|
`;
|
|
1690
|
-
const rows =
|
|
1758
|
+
const rows = this.db.prepare(sql).all(...params);
|
|
1691
1759
|
return rows.map((row) => ({
|
|
1692
1760
|
...row,
|
|
1693
1761
|
text: row.text ?? "",
|
|
1694
|
-
keywordScore: this.bm25ToScore(row.score)
|
|
1762
|
+
keywordScore: this.bm25ToScore(row.score),
|
|
1763
|
+
createdAt: row.timestamp ?? void 0
|
|
1695
1764
|
}));
|
|
1696
1765
|
} catch (error) {
|
|
1697
1766
|
log5.error({ err: error }, "FTS5 search error (messages)");
|
|
@@ -1712,7 +1781,16 @@ var HybridSearch = class {
|
|
|
1712
1781
|
byId.set(r.id, { ...r, score: keywordWeight * (r.keywordScore ?? 0) });
|
|
1713
1782
|
}
|
|
1714
1783
|
}
|
|
1715
|
-
|
|
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);
|
|
1716
1794
|
}
|
|
1717
1795
|
/**
|
|
1718
1796
|
* Convert BM25 rank to normalized score.
|
|
@@ -1776,7 +1854,12 @@ var ContextBuilder = class {
|
|
|
1776
1854
|
log6.warn({ err: error }, "Knowledge search failed");
|
|
1777
1855
|
return [];
|
|
1778
1856
|
}) : Promise.resolve([]);
|
|
1779
|
-
const
|
|
1857
|
+
const { afterTimestamp } = parseTemporalIntent(query);
|
|
1858
|
+
const feedPromise = includeFeedHistory ? this.hybridSearch.searchMessages(query, queryEmbedding, {
|
|
1859
|
+
chatId,
|
|
1860
|
+
limit: maxRelevantChunks,
|
|
1861
|
+
afterTimestamp
|
|
1862
|
+
}).catch((error) => {
|
|
1780
1863
|
log6.warn({ err: error }, "Feed search failed");
|
|
1781
1864
|
return [];
|
|
1782
1865
|
}) : Promise.resolve([]);
|
|
@@ -1863,6 +1946,7 @@ export {
|
|
|
1863
1946
|
MessageStore,
|
|
1864
1947
|
ChatStore,
|
|
1865
1948
|
UserStore,
|
|
1949
|
+
parseTemporalIntent,
|
|
1866
1950
|
HybridSearch,
|
|
1867
1951
|
ContextBuilder,
|
|
1868
1952
|
initializeMemory
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getErrorMessage
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3UFPFWYP.js";
|
|
4
4
|
import {
|
|
5
5
|
createLogger
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-NQ6FZKCE.js";
|
|
7
7
|
|
|
8
8
|
// src/agent/tools/telegram/gifts/get-my-gifts.ts
|
|
9
9
|
import { Type } from "@sinclair/typebox";
|