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
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
ConfigSchema,
|
|
3
2
|
getCachedTonClient,
|
|
4
3
|
getKeyPair,
|
|
5
4
|
getTonPrice,
|
|
@@ -7,7 +6,13 @@ import {
|
|
|
7
6
|
getWalletBalance,
|
|
8
7
|
invalidateTonClientCache,
|
|
9
8
|
loadWallet
|
|
10
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-FUNF6H4W.js";
|
|
10
|
+
import {
|
|
11
|
+
expandPath
|
|
12
|
+
} from "./chunk-AEHTQI3H.js";
|
|
13
|
+
import {
|
|
14
|
+
ConfigSchema
|
|
15
|
+
} from "./chunk-AERHOXGC.js";
|
|
11
16
|
import {
|
|
12
17
|
getOrCreateSession,
|
|
13
18
|
getSession,
|
|
@@ -15,7 +20,22 @@ import {
|
|
|
15
20
|
resetSessionWithPolicy,
|
|
16
21
|
shouldResetSession,
|
|
17
22
|
updateSession
|
|
18
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XDZDOKIF.js";
|
|
24
|
+
import {
|
|
25
|
+
ContextBuilder,
|
|
26
|
+
createDbWrapper,
|
|
27
|
+
getDatabase,
|
|
28
|
+
migrateFromMainDb,
|
|
29
|
+
openModuleDb
|
|
30
|
+
} from "./chunk-GHMXWAXI.js";
|
|
31
|
+
import {
|
|
32
|
+
saveSessionMemory,
|
|
33
|
+
summarizeWithFallback
|
|
34
|
+
} from "./chunk-ALKAAG4O.js";
|
|
35
|
+
import {
|
|
36
|
+
getErrorMessage,
|
|
37
|
+
isHttpError
|
|
38
|
+
} from "./chunk-3UFPFWYP.js";
|
|
19
39
|
import {
|
|
20
40
|
Mi,
|
|
21
41
|
Mn,
|
|
@@ -26,41 +46,11 @@ import {
|
|
|
26
46
|
ut,
|
|
27
47
|
ye
|
|
28
48
|
} from "./chunk-7TECSLJ4.js";
|
|
29
|
-
import {
|
|
30
|
-
getErrorMessage
|
|
31
|
-
} from "./chunk-XBE4JB7C.js";
|
|
32
|
-
import {
|
|
33
|
-
chatWithContext,
|
|
34
|
-
getEffectiveApiKey,
|
|
35
|
-
getProviderModel,
|
|
36
|
-
getUtilityModel,
|
|
37
|
-
loadContextFromTranscript
|
|
38
|
-
} from "./chunk-AYWEJCDB.js";
|
|
39
|
-
import {
|
|
40
|
-
getProviderMetadata,
|
|
41
|
-
getSupportedProviders
|
|
42
|
-
} from "./chunk-PHSAHTK4.js";
|
|
43
|
-
import {
|
|
44
|
-
appendToTranscript,
|
|
45
|
-
archiveTranscript,
|
|
46
|
-
transcriptExists
|
|
47
|
-
} from "./chunk-QV2GLOTK.js";
|
|
48
|
-
import {
|
|
49
|
-
ContextBuilder,
|
|
50
|
-
createDbWrapper,
|
|
51
|
-
getDatabase,
|
|
52
|
-
migrateFromMainDb,
|
|
53
|
-
openModuleDb
|
|
54
|
-
} from "./chunk-7U7BOHCL.js";
|
|
55
49
|
import {
|
|
56
50
|
GECKOTERMINAL_API_URL,
|
|
57
51
|
tonapiFetch
|
|
58
52
|
} from "./chunk-VFA7QMCZ.js";
|
|
59
53
|
import {
|
|
60
|
-
ADAPTIVE_CHUNK_RATIO_BASE,
|
|
61
|
-
ADAPTIVE_CHUNK_RATIO_MIN,
|
|
62
|
-
ADAPTIVE_CHUNK_RATIO_TRIGGER,
|
|
63
|
-
CHARS_PER_TOKEN_ESTIMATE,
|
|
64
54
|
COMPACTION_KEEP_RECENT,
|
|
65
55
|
COMPACTION_MAX_MESSAGES,
|
|
66
56
|
COMPACTION_MAX_TOKENS_RATIO,
|
|
@@ -72,21 +62,28 @@ import {
|
|
|
72
62
|
DEFAULT_MAX_SUMMARY_TOKENS,
|
|
73
63
|
DEFAULT_MAX_TOKENS,
|
|
74
64
|
DEFAULT_SOFT_THRESHOLD_TOKENS,
|
|
75
|
-
|
|
65
|
+
EMBEDDING_QUERY_MAX_CHARS,
|
|
76
66
|
FALLBACK_SOFT_THRESHOLD_TOKENS,
|
|
77
67
|
MASKING_KEEP_RECENT_COUNT,
|
|
78
68
|
MAX_TOOL_RESULT_SIZE,
|
|
79
69
|
MEMORY_FLUSH_RECENT_MESSAGES,
|
|
80
|
-
OVERSIZED_MESSAGE_RATIO,
|
|
81
70
|
PAYMENT_TOLERANCE_RATIO,
|
|
82
71
|
RATE_LIMIT_MAX_RETRIES,
|
|
83
72
|
RESULT_TRUNCATION_KEEP_CHARS,
|
|
84
73
|
RESULT_TRUNCATION_THRESHOLD,
|
|
85
74
|
SERVER_ERROR_MAX_RETRIES,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
TOOL_CONCURRENCY_LIMIT
|
|
76
|
+
} from "./chunk-C4NKJT2Z.js";
|
|
77
|
+
import {
|
|
78
|
+
chatWithContext,
|
|
79
|
+
getEffectiveApiKey,
|
|
80
|
+
getProviderModel,
|
|
81
|
+
loadContextFromTranscript
|
|
82
|
+
} from "./chunk-LVTKJQ7O.js";
|
|
83
|
+
import {
|
|
84
|
+
getProviderMetadata,
|
|
85
|
+
getSupportedProviders
|
|
86
|
+
} from "./chunk-6OOHHJ4N.js";
|
|
90
87
|
import {
|
|
91
88
|
fetchWithTimeout
|
|
92
89
|
} from "./chunk-XQUHC3JZ.js";
|
|
@@ -99,6 +96,11 @@ import {
|
|
|
99
96
|
RETRY_DEFAULT_MAX_DELAY_MS,
|
|
100
97
|
RETRY_DEFAULT_TIMEOUT_MS
|
|
101
98
|
} from "./chunk-R4YSJ4EY.js";
|
|
99
|
+
import {
|
|
100
|
+
appendToTranscript,
|
|
101
|
+
archiveTranscript,
|
|
102
|
+
transcriptExists
|
|
103
|
+
} from "./chunk-LC4TV3KL.js";
|
|
102
104
|
import {
|
|
103
105
|
ALLOWED_EXTENSIONS,
|
|
104
106
|
TELETON_ROOT,
|
|
@@ -107,131 +109,19 @@ import {
|
|
|
107
109
|
} from "./chunk-EYWNOHMJ.js";
|
|
108
110
|
import {
|
|
109
111
|
createLogger
|
|
110
|
-
} from "./chunk-
|
|
111
|
-
|
|
112
|
-
// src/config/loader.ts
|
|
113
|
-
import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
|
|
114
|
-
import { parse, stringify } from "yaml";
|
|
115
|
-
import { homedir } from "os";
|
|
116
|
-
import { dirname, join } from "path";
|
|
117
|
-
var log = createLogger("Config");
|
|
118
|
-
var DEFAULT_CONFIG_PATH = join(TELETON_ROOT, "config.yaml");
|
|
119
|
-
function expandPath(path) {
|
|
120
|
-
if (path.startsWith("~")) {
|
|
121
|
-
return join(homedir(), path.slice(1));
|
|
122
|
-
}
|
|
123
|
-
return path;
|
|
124
|
-
}
|
|
125
|
-
function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
|
|
126
|
-
const fullPath = expandPath(configPath);
|
|
127
|
-
if (!existsSync(fullPath)) {
|
|
128
|
-
throw new Error(`Config file not found: ${fullPath}
|
|
129
|
-
Run 'teleton setup' to create one.`);
|
|
130
|
-
}
|
|
131
|
-
let content;
|
|
132
|
-
try {
|
|
133
|
-
content = readFileSync(fullPath, "utf-8");
|
|
134
|
-
} catch (error) {
|
|
135
|
-
throw new Error(`Cannot read config file ${fullPath}: ${error.message}`);
|
|
136
|
-
}
|
|
137
|
-
let raw;
|
|
138
|
-
try {
|
|
139
|
-
raw = parse(content);
|
|
140
|
-
} catch (error) {
|
|
141
|
-
throw new Error(`Invalid YAML in ${fullPath}: ${error.message}`);
|
|
142
|
-
}
|
|
143
|
-
if (raw && typeof raw === "object" && "market" in raw) {
|
|
144
|
-
log.warn("config.market is deprecated and ignored. Use market-api plugin instead.");
|
|
145
|
-
delete raw.market;
|
|
146
|
-
}
|
|
147
|
-
const result = ConfigSchema.safeParse(raw);
|
|
148
|
-
if (!result.success) {
|
|
149
|
-
throw new Error(`Invalid config: ${result.error.message}`);
|
|
150
|
-
}
|
|
151
|
-
const config = result.data;
|
|
152
|
-
const provider = config.agent.provider;
|
|
153
|
-
if (provider !== "anthropic" && provider !== "claude-code" && !raw.agent?.model) {
|
|
154
|
-
const meta = getProviderMetadata(provider);
|
|
155
|
-
config.agent.model = meta.defaultModel;
|
|
156
|
-
}
|
|
157
|
-
config.telegram.session_path = expandPath(config.telegram.session_path);
|
|
158
|
-
config.storage.sessions_file = expandPath(config.storage.sessions_file);
|
|
159
|
-
config.storage.memory_file = expandPath(config.storage.memory_file);
|
|
160
|
-
if (process.env.TELETON_API_KEY) {
|
|
161
|
-
config.agent.api_key = process.env.TELETON_API_KEY;
|
|
162
|
-
}
|
|
163
|
-
if (process.env.TELETON_TG_API_ID) {
|
|
164
|
-
const apiId = parseInt(process.env.TELETON_TG_API_ID, 10);
|
|
165
|
-
if (isNaN(apiId)) {
|
|
166
|
-
throw new Error(
|
|
167
|
-
`Invalid TELETON_TG_API_ID environment variable: "${process.env.TELETON_TG_API_ID}" is not a valid integer`
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
config.telegram.api_id = apiId;
|
|
171
|
-
}
|
|
172
|
-
if (process.env.TELETON_TG_API_HASH) {
|
|
173
|
-
config.telegram.api_hash = process.env.TELETON_TG_API_HASH;
|
|
174
|
-
}
|
|
175
|
-
if (process.env.TELETON_TG_PHONE) {
|
|
176
|
-
config.telegram.phone = process.env.TELETON_TG_PHONE;
|
|
177
|
-
}
|
|
178
|
-
if (process.env.TELETON_WEBUI_ENABLED) {
|
|
179
|
-
config.webui.enabled = process.env.TELETON_WEBUI_ENABLED === "true";
|
|
180
|
-
}
|
|
181
|
-
if (process.env.TELETON_WEBUI_PORT) {
|
|
182
|
-
const port = parseInt(process.env.TELETON_WEBUI_PORT, 10);
|
|
183
|
-
if (!isNaN(port) && port >= 1024 && port <= 65535) {
|
|
184
|
-
config.webui.port = port;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
if (process.env.TELETON_WEBUI_HOST) {
|
|
188
|
-
config.webui.host = process.env.TELETON_WEBUI_HOST;
|
|
189
|
-
if (!["127.0.0.1", "localhost", "::1"].includes(config.webui.host)) {
|
|
190
|
-
log.warn(
|
|
191
|
-
{ host: config.webui.host },
|
|
192
|
-
"WebUI bound to non-loopback address \u2014 ensure auth_token is set"
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (process.env.TELETON_BASE_URL) {
|
|
197
|
-
try {
|
|
198
|
-
new URL(process.env.TELETON_BASE_URL);
|
|
199
|
-
config.agent.base_url = process.env.TELETON_BASE_URL;
|
|
200
|
-
} catch {
|
|
201
|
-
throw new Error(
|
|
202
|
-
`Invalid TELETON_BASE_URL: "${process.env.TELETON_BASE_URL}" is not a valid URL`
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (process.env.TELETON_TAVILY_API_KEY) {
|
|
207
|
-
config.tavily_api_key = process.env.TELETON_TAVILY_API_KEY;
|
|
208
|
-
}
|
|
209
|
-
if (process.env.TELETON_TONAPI_KEY) {
|
|
210
|
-
config.tonapi_key = process.env.TELETON_TONAPI_KEY;
|
|
211
|
-
}
|
|
212
|
-
if (process.env.TELETON_TONCENTER_API_KEY) {
|
|
213
|
-
config.toncenter_api_key = process.env.TELETON_TONCENTER_API_KEY;
|
|
214
|
-
}
|
|
215
|
-
return config;
|
|
216
|
-
}
|
|
217
|
-
function configExists(configPath = DEFAULT_CONFIG_PATH) {
|
|
218
|
-
return existsSync(expandPath(configPath));
|
|
219
|
-
}
|
|
220
|
-
function getDefaultConfigPath() {
|
|
221
|
-
return DEFAULT_CONFIG_PATH;
|
|
222
|
-
}
|
|
112
|
+
} from "./chunk-NQ6FZKCE.js";
|
|
223
113
|
|
|
224
114
|
// src/soul/loader.ts
|
|
225
|
-
import { readFileSync as
|
|
115
|
+
import { readFileSync as readFileSync2, existsSync as existsSync3 } from "fs";
|
|
226
116
|
|
|
227
117
|
// src/memory/daily-logs.ts
|
|
228
|
-
import { existsSync as
|
|
229
|
-
import { join
|
|
118
|
+
import { existsSync as existsSync2, mkdirSync, appendFileSync, readFileSync } from "fs";
|
|
119
|
+
import { join } from "path";
|
|
230
120
|
|
|
231
121
|
// src/workspace/validator.ts
|
|
232
|
-
import { existsSync
|
|
122
|
+
import { existsSync, lstatSync, readdirSync } from "fs";
|
|
233
123
|
import { resolve, normalize, relative, extname, basename } from "path";
|
|
234
|
-
import { homedir
|
|
124
|
+
import { homedir } from "os";
|
|
235
125
|
var WorkspaceSecurityError = class extends Error {
|
|
236
126
|
constructor(message, attemptedPath) {
|
|
237
127
|
super(message);
|
|
@@ -265,7 +155,7 @@ function validatePath(inputPath, allowCreate = false) {
|
|
|
265
155
|
if (decodedPath.startsWith("/")) {
|
|
266
156
|
absolutePath = resolve(normalize(decodedPath));
|
|
267
157
|
} else if (decodedPath.startsWith("~/")) {
|
|
268
|
-
const expanded = decodedPath.replace(/^~(?=$|[\\/])/,
|
|
158
|
+
const expanded = decodedPath.replace(/^~(?=$|[\\/])/, homedir());
|
|
269
159
|
absolutePath = resolve(expanded);
|
|
270
160
|
} else {
|
|
271
161
|
absolutePath = resolve(WORKSPACE_ROOT, normalize(decodedPath));
|
|
@@ -277,7 +167,7 @@ function validatePath(inputPath, allowCreate = false) {
|
|
|
277
167
|
inputPath
|
|
278
168
|
);
|
|
279
169
|
}
|
|
280
|
-
const exists =
|
|
170
|
+
const exists = existsSync(absolutePath);
|
|
281
171
|
if (!exists && !allowCreate) {
|
|
282
172
|
throw new WorkspaceSecurityError(
|
|
283
173
|
`File not found: '${inputPath}' does not exist in workspace.`,
|
|
@@ -341,7 +231,7 @@ function validateDirectory(inputPath) {
|
|
|
341
231
|
}
|
|
342
232
|
|
|
343
233
|
// src/memory/daily-logs.ts
|
|
344
|
-
var
|
|
234
|
+
var log = createLogger("Memory");
|
|
345
235
|
var MEMORY_DIR = WORKSPACE_PATHS.MEMORY_DIR;
|
|
346
236
|
function formatDate(date) {
|
|
347
237
|
const year = date.getFullYear();
|
|
@@ -350,11 +240,11 @@ function formatDate(date) {
|
|
|
350
240
|
return `${year}-${month}-${day}`;
|
|
351
241
|
}
|
|
352
242
|
function getDailyLogPath(date = /* @__PURE__ */ new Date()) {
|
|
353
|
-
return
|
|
243
|
+
return join(MEMORY_DIR, `${formatDate(date)}.md`);
|
|
354
244
|
}
|
|
355
245
|
function ensureMemoryDir() {
|
|
356
|
-
if (!
|
|
357
|
-
|
|
246
|
+
if (!existsSync2(MEMORY_DIR)) {
|
|
247
|
+
mkdirSync(MEMORY_DIR, { recursive: true });
|
|
358
248
|
}
|
|
359
249
|
}
|
|
360
250
|
function appendToDailyLog(content, date = /* @__PURE__ */ new Date()) {
|
|
@@ -362,11 +252,11 @@ function appendToDailyLog(content, date = /* @__PURE__ */ new Date()) {
|
|
|
362
252
|
ensureMemoryDir();
|
|
363
253
|
const logPath = getDailyLogPath(date);
|
|
364
254
|
const timestamp = date.toLocaleTimeString("en-US", { hour12: false });
|
|
365
|
-
if (!
|
|
255
|
+
if (!existsSync2(logPath)) {
|
|
366
256
|
const header = `# Daily Log - ${formatDate(date)}
|
|
367
257
|
|
|
368
258
|
`;
|
|
369
|
-
appendFileSync(logPath, header, "utf-8");
|
|
259
|
+
appendFileSync(logPath, header, { encoding: "utf-8", mode: 384 });
|
|
370
260
|
}
|
|
371
261
|
const entry = `## ${timestamp}
|
|
372
262
|
|
|
@@ -376,16 +266,16 @@ ${content}
|
|
|
376
266
|
|
|
377
267
|
`;
|
|
378
268
|
appendFileSync(logPath, entry, "utf-8");
|
|
379
|
-
|
|
269
|
+
log.info(`Daily log updated: ${logPath}`);
|
|
380
270
|
} catch (error) {
|
|
381
|
-
|
|
271
|
+
log.error({ err: error }, "Failed to write daily log");
|
|
382
272
|
}
|
|
383
273
|
}
|
|
384
274
|
function readDailyLog(date = /* @__PURE__ */ new Date()) {
|
|
385
275
|
try {
|
|
386
276
|
const logPath = getDailyLogPath(date);
|
|
387
|
-
if (!
|
|
388
|
-
return
|
|
277
|
+
if (!existsSync2(logPath)) return null;
|
|
278
|
+
return readFileSync(logPath, "utf-8");
|
|
389
279
|
} catch {
|
|
390
280
|
return null;
|
|
391
281
|
}
|
|
@@ -466,7 +356,7 @@ function cachedReadFile(path) {
|
|
|
466
356
|
if (cached && now < cached.expiry) return cached.content;
|
|
467
357
|
let content = null;
|
|
468
358
|
try {
|
|
469
|
-
if (
|
|
359
|
+
if (existsSync3(path)) content = readFileSync2(path, "utf-8");
|
|
470
360
|
} catch {
|
|
471
361
|
}
|
|
472
362
|
fileCache.set(path, { content, expiry: now + FILE_CACHE_TTL });
|
|
@@ -741,397 +631,6 @@ ${body}`;
|
|
|
741
631
|
|
|
742
632
|
// src/memory/compaction.ts
|
|
743
633
|
import { randomUUID } from "crypto";
|
|
744
|
-
|
|
745
|
-
// src/memory/ai-summarization.ts
|
|
746
|
-
import {
|
|
747
|
-
complete
|
|
748
|
-
} from "@mariozechner/pi-ai";
|
|
749
|
-
var log3 = createLogger("Memory");
|
|
750
|
-
function estimateMessageTokens(content) {
|
|
751
|
-
return Math.ceil(content.length / CHARS_PER_TOKEN_ESTIMATE * TOKEN_ESTIMATE_SAFETY_MARGIN);
|
|
752
|
-
}
|
|
753
|
-
function splitMessagesByTokens(messages, maxChunkTokens) {
|
|
754
|
-
if (messages.length === 0) {
|
|
755
|
-
return [];
|
|
756
|
-
}
|
|
757
|
-
const chunks = [];
|
|
758
|
-
let currentChunk = [];
|
|
759
|
-
let currentTokens = 0;
|
|
760
|
-
for (const message of messages) {
|
|
761
|
-
const content = extractMessageContent(message);
|
|
762
|
-
const messageTokens = estimateMessageTokens(content);
|
|
763
|
-
if (currentChunk.length > 0 && currentTokens + messageTokens > maxChunkTokens) {
|
|
764
|
-
chunks.push(currentChunk);
|
|
765
|
-
currentChunk = [];
|
|
766
|
-
currentTokens = 0;
|
|
767
|
-
}
|
|
768
|
-
currentChunk.push(message);
|
|
769
|
-
currentTokens += messageTokens;
|
|
770
|
-
if (messageTokens > maxChunkTokens && currentChunk.length === 1) {
|
|
771
|
-
chunks.push(currentChunk);
|
|
772
|
-
currentChunk = [];
|
|
773
|
-
currentTokens = 0;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
if (currentChunk.length > 0) {
|
|
777
|
-
chunks.push(currentChunk);
|
|
778
|
-
}
|
|
779
|
-
return chunks;
|
|
780
|
-
}
|
|
781
|
-
function extractMessageContent(message) {
|
|
782
|
-
if (message.role === "user") {
|
|
783
|
-
return typeof message.content === "string" ? message.content : "[complex content]";
|
|
784
|
-
} else if (message.role === "assistant") {
|
|
785
|
-
return message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
786
|
-
}
|
|
787
|
-
return "";
|
|
788
|
-
}
|
|
789
|
-
function formatMessagesForSummary(messages) {
|
|
790
|
-
const formatted = [];
|
|
791
|
-
for (const msg of messages) {
|
|
792
|
-
if (msg.role === "user") {
|
|
793
|
-
const content = typeof msg.content === "string" ? msg.content : "[complex]";
|
|
794
|
-
const bodyMatch = content.match(/\] (.+)/s);
|
|
795
|
-
const body = bodyMatch ? bodyMatch[1] : content;
|
|
796
|
-
formatted.push(`User: ${body}`);
|
|
797
|
-
} else if (msg.role === "assistant") {
|
|
798
|
-
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
799
|
-
if (textBlocks.length > 0) {
|
|
800
|
-
const text = textBlocks.map((b) => b.text).join("\n");
|
|
801
|
-
formatted.push(`Assistant: ${text}`);
|
|
802
|
-
}
|
|
803
|
-
const toolCalls = msg.content.filter((b) => b.type === "toolCall");
|
|
804
|
-
if (toolCalls.length > 0) {
|
|
805
|
-
const toolNames = toolCalls.map((b) => b.name).join(", ");
|
|
806
|
-
formatted.push(`[Used tools: ${toolNames}]`);
|
|
807
|
-
}
|
|
808
|
-
} else if (msg.role === "toolResult") {
|
|
809
|
-
formatted.push(`[Tool result: ${msg.toolName}]`);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
return formatted.join("\n\n");
|
|
813
|
-
}
|
|
814
|
-
function isOversizedForSummary(message, contextWindow) {
|
|
815
|
-
const content = extractMessageContent(message);
|
|
816
|
-
const tokens = estimateMessageTokens(content);
|
|
817
|
-
return tokens > contextWindow * OVERSIZED_MESSAGE_RATIO;
|
|
818
|
-
}
|
|
819
|
-
function computeAdaptiveChunkRatio(messages, contextWindow) {
|
|
820
|
-
const BASE_CHUNK_RATIO = ADAPTIVE_CHUNK_RATIO_BASE;
|
|
821
|
-
const MIN_CHUNK_RATIO = ADAPTIVE_CHUNK_RATIO_MIN;
|
|
822
|
-
if (messages.length === 0) {
|
|
823
|
-
return BASE_CHUNK_RATIO;
|
|
824
|
-
}
|
|
825
|
-
let totalTokens = 0;
|
|
826
|
-
for (const msg of messages) {
|
|
827
|
-
const content = extractMessageContent(msg);
|
|
828
|
-
totalTokens += estimateMessageTokens(content);
|
|
829
|
-
}
|
|
830
|
-
const avgTokens = totalTokens / messages.length;
|
|
831
|
-
const avgRatio = avgTokens / contextWindow;
|
|
832
|
-
if (avgRatio > ADAPTIVE_CHUNK_RATIO_TRIGGER) {
|
|
833
|
-
const reduction = Math.min(avgRatio * 2, BASE_CHUNK_RATIO - MIN_CHUNK_RATIO);
|
|
834
|
-
return Math.max(MIN_CHUNK_RATIO, BASE_CHUNK_RATIO - reduction);
|
|
835
|
-
}
|
|
836
|
-
return BASE_CHUNK_RATIO;
|
|
837
|
-
}
|
|
838
|
-
async function summarizeViaClaude(params) {
|
|
839
|
-
const provider = params.provider || "anthropic";
|
|
840
|
-
const model = getUtilityModel(provider, params.utilityModel);
|
|
841
|
-
const maxTokens = params.maxSummaryTokens ?? DEFAULT_SUMMARY_FALLBACK_TOKENS;
|
|
842
|
-
const formatted = formatMessagesForSummary(params.messages);
|
|
843
|
-
if (!formatted.trim()) {
|
|
844
|
-
return "No conversation content to summarize.";
|
|
845
|
-
}
|
|
846
|
-
const defaultInstructions = `Summarize this conversation concisely. Focus on:
|
|
847
|
-
- Key decisions made
|
|
848
|
-
- Action items and TODOs
|
|
849
|
-
- Open questions
|
|
850
|
-
- Important context and constraints
|
|
851
|
-
- Technical details that matter
|
|
852
|
-
|
|
853
|
-
Be specific but concise. Preserve critical information.`;
|
|
854
|
-
const instructions = params.customInstructions ? `${defaultInstructions}
|
|
855
|
-
|
|
856
|
-
Additional focus:
|
|
857
|
-
${params.customInstructions}` : defaultInstructions;
|
|
858
|
-
try {
|
|
859
|
-
const context = {
|
|
860
|
-
messages: [
|
|
861
|
-
{
|
|
862
|
-
role: "user",
|
|
863
|
-
content: `${instructions}
|
|
864
|
-
|
|
865
|
-
Conversation:
|
|
866
|
-
${formatted}`,
|
|
867
|
-
timestamp: Date.now()
|
|
868
|
-
}
|
|
869
|
-
]
|
|
870
|
-
};
|
|
871
|
-
const response = await complete(model, context, {
|
|
872
|
-
apiKey: params.apiKey,
|
|
873
|
-
maxTokens
|
|
874
|
-
});
|
|
875
|
-
const textContent = response.content.find((block) => block.type === "text");
|
|
876
|
-
const summary = textContent?.type === "text" ? textContent.text : "";
|
|
877
|
-
return summary.trim() || "Unable to generate summary.";
|
|
878
|
-
} catch (error) {
|
|
879
|
-
log3.error({ err: error }, "Summarization error");
|
|
880
|
-
throw new Error(`Summarization failed: ${getErrorMessage(error)}`);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
async function summarizeInChunks(params) {
|
|
884
|
-
if (params.messages.length === 0) {
|
|
885
|
-
return {
|
|
886
|
-
summary: "No messages to summarize.",
|
|
887
|
-
tokensUsed: 0,
|
|
888
|
-
chunksProcessed: 0
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
const chunks = splitMessagesByTokens(params.messages, params.maxChunkTokens);
|
|
892
|
-
log3.info(`Splitting into ${chunks.length} chunks for summarization`);
|
|
893
|
-
if (chunks.length === 1) {
|
|
894
|
-
const summary = await summarizeViaClaude({
|
|
895
|
-
messages: chunks[0],
|
|
896
|
-
apiKey: params.apiKey,
|
|
897
|
-
maxSummaryTokens: params.maxSummaryTokens,
|
|
898
|
-
customInstructions: params.customInstructions,
|
|
899
|
-
provider: params.provider,
|
|
900
|
-
utilityModel: params.utilityModel
|
|
901
|
-
});
|
|
902
|
-
return {
|
|
903
|
-
summary,
|
|
904
|
-
tokensUsed: estimateMessageTokens(summary),
|
|
905
|
-
chunksProcessed: 1
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
const partialSummaries = [];
|
|
909
|
-
for (let i = 0; i < chunks.length; i++) {
|
|
910
|
-
log3.info(`Summarizing chunk ${i + 1}/${chunks.length} (${chunks[i].length} messages)`);
|
|
911
|
-
const partial = await summarizeViaClaude({
|
|
912
|
-
messages: chunks[i],
|
|
913
|
-
apiKey: params.apiKey,
|
|
914
|
-
maxSummaryTokens: Math.floor(
|
|
915
|
-
(params.maxSummaryTokens ?? DEFAULT_SUMMARY_FALLBACK_TOKENS) / 2
|
|
916
|
-
),
|
|
917
|
-
customInstructions: params.customInstructions,
|
|
918
|
-
provider: params.provider,
|
|
919
|
-
utilityModel: params.utilityModel
|
|
920
|
-
});
|
|
921
|
-
partialSummaries.push(partial);
|
|
922
|
-
}
|
|
923
|
-
log3.info(`Merging ${partialSummaries.length} partial summaries`);
|
|
924
|
-
const provider = params.provider || "anthropic";
|
|
925
|
-
const model = getUtilityModel(provider, params.utilityModel);
|
|
926
|
-
const mergeContext = {
|
|
927
|
-
messages: [
|
|
928
|
-
{
|
|
929
|
-
role: "user",
|
|
930
|
-
content: `Merge these partial conversation summaries into one cohesive summary.
|
|
931
|
-
Preserve all key decisions, action items, open questions, and important context.
|
|
932
|
-
Do not add new information - only synthesize what's provided.
|
|
933
|
-
|
|
934
|
-
Partial summaries:
|
|
935
|
-
|
|
936
|
-
${partialSummaries.map((s, i) => `Part ${i + 1}:
|
|
937
|
-
${s}`).join("\n\n---\n\n")}`,
|
|
938
|
-
timestamp: Date.now()
|
|
939
|
-
}
|
|
940
|
-
]
|
|
941
|
-
};
|
|
942
|
-
const mergeResponse = await complete(model, mergeContext, {
|
|
943
|
-
apiKey: params.apiKey,
|
|
944
|
-
maxTokens: params.maxSummaryTokens ?? DEFAULT_SUMMARY_FALLBACK_TOKENS
|
|
945
|
-
});
|
|
946
|
-
const textContent = mergeResponse.content.find((block) => block.type === "text");
|
|
947
|
-
const merged = textContent?.type === "text" ? textContent.text : "";
|
|
948
|
-
return {
|
|
949
|
-
summary: merged.trim() || "Unable to merge summaries.",
|
|
950
|
-
tokensUsed: estimateMessageTokens(merged),
|
|
951
|
-
chunksProcessed: chunks.length
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
async function summarizeWithFallback(params) {
|
|
955
|
-
if (params.messages.length === 0) {
|
|
956
|
-
return {
|
|
957
|
-
summary: "No messages to summarize.",
|
|
958
|
-
tokensUsed: 0,
|
|
959
|
-
chunksProcessed: 0
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
const chunkRatio = computeAdaptiveChunkRatio(params.messages, params.contextWindow);
|
|
963
|
-
const maxChunkTokens = Math.floor(params.contextWindow * chunkRatio);
|
|
964
|
-
log3.info(
|
|
965
|
-
`AI Summarization: ${params.messages.length} messages, chunk ratio: ${(chunkRatio * 100).toFixed(0)}%`
|
|
966
|
-
);
|
|
967
|
-
try {
|
|
968
|
-
return await summarizeInChunks({
|
|
969
|
-
messages: params.messages,
|
|
970
|
-
apiKey: params.apiKey,
|
|
971
|
-
maxChunkTokens,
|
|
972
|
-
maxSummaryTokens: params.maxSummaryTokens,
|
|
973
|
-
customInstructions: params.customInstructions,
|
|
974
|
-
provider: params.provider,
|
|
975
|
-
utilityModel: params.utilityModel
|
|
976
|
-
});
|
|
977
|
-
} catch (fullError) {
|
|
978
|
-
log3.warn(
|
|
979
|
-
`Full summarization failed: ${fullError instanceof Error ? fullError.message : String(fullError)}`
|
|
980
|
-
);
|
|
981
|
-
}
|
|
982
|
-
const smallMessages = [];
|
|
983
|
-
const oversizedNotes = [];
|
|
984
|
-
for (const msg of params.messages) {
|
|
985
|
-
if (isOversizedForSummary(msg, params.contextWindow)) {
|
|
986
|
-
const content = extractMessageContent(msg);
|
|
987
|
-
const tokens = estimateMessageTokens(content);
|
|
988
|
-
oversizedNotes.push(
|
|
989
|
-
`[Large ${msg.role} message (~${Math.round(tokens / 1e3)}K tokens) omitted from summary]`
|
|
990
|
-
);
|
|
991
|
-
} else {
|
|
992
|
-
smallMessages.push(msg);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
log3.info(
|
|
996
|
-
`Fallback: Processing ${smallMessages.length} messages, skipping ${oversizedNotes.length} oversized`
|
|
997
|
-
);
|
|
998
|
-
if (smallMessages.length > 0) {
|
|
999
|
-
try {
|
|
1000
|
-
const result = await summarizeInChunks({
|
|
1001
|
-
messages: smallMessages,
|
|
1002
|
-
apiKey: params.apiKey,
|
|
1003
|
-
maxChunkTokens,
|
|
1004
|
-
maxSummaryTokens: params.maxSummaryTokens,
|
|
1005
|
-
customInstructions: params.customInstructions,
|
|
1006
|
-
provider: params.provider,
|
|
1007
|
-
utilityModel: params.utilityModel
|
|
1008
|
-
});
|
|
1009
|
-
const notes = oversizedNotes.length > 0 ? `
|
|
1010
|
-
|
|
1011
|
-
${oversizedNotes.join("\n")}` : "";
|
|
1012
|
-
return {
|
|
1013
|
-
summary: result.summary + notes,
|
|
1014
|
-
tokensUsed: result.tokensUsed,
|
|
1015
|
-
chunksProcessed: result.chunksProcessed
|
|
1016
|
-
};
|
|
1017
|
-
} catch (partialError) {
|
|
1018
|
-
log3.warn(
|
|
1019
|
-
`Partial summarization also failed: ${partialError instanceof Error ? partialError.message : String(partialError)}`
|
|
1020
|
-
);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
const note = `Context contained ${params.messages.length} messages (${oversizedNotes.length} were oversized). AI summarization unavailable due to size constraints. Recent conversation history was preserved.`;
|
|
1024
|
-
return {
|
|
1025
|
-
summary: note,
|
|
1026
|
-
tokensUsed: estimateMessageTokens(note),
|
|
1027
|
-
chunksProcessed: 0
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
// src/session/memory-hook.ts
|
|
1032
|
-
import { writeFile, mkdir } from "fs/promises";
|
|
1033
|
-
import { join as join3 } from "path";
|
|
1034
|
-
import { complete as complete2 } from "@mariozechner/pi-ai";
|
|
1035
|
-
var log4 = createLogger("Session");
|
|
1036
|
-
async function generateSlugViaClaude(params) {
|
|
1037
|
-
const provider = params.provider || "anthropic";
|
|
1038
|
-
const model = getUtilityModel(provider, params.utilityModel);
|
|
1039
|
-
const formatted = formatMessagesForSummary(params.messages.slice(-SESSION_SLUG_RECENT_MESSAGES));
|
|
1040
|
-
if (!formatted.trim()) {
|
|
1041
|
-
return "empty-session";
|
|
1042
|
-
}
|
|
1043
|
-
try {
|
|
1044
|
-
const context = {
|
|
1045
|
-
messages: [
|
|
1046
|
-
{
|
|
1047
|
-
role: "user",
|
|
1048
|
-
content: `Generate a short, descriptive slug (2-4 words, kebab-case) for this conversation.
|
|
1049
|
-
Examples: "gift-transfer-fix", "context-overflow-debug", "telegram-integration"
|
|
1050
|
-
|
|
1051
|
-
Conversation:
|
|
1052
|
-
${formatted}
|
|
1053
|
-
|
|
1054
|
-
Slug:`,
|
|
1055
|
-
timestamp: Date.now()
|
|
1056
|
-
}
|
|
1057
|
-
]
|
|
1058
|
-
};
|
|
1059
|
-
const response = await complete2(model, context, {
|
|
1060
|
-
apiKey: params.apiKey,
|
|
1061
|
-
maxTokens: SESSION_SLUG_MAX_TOKENS
|
|
1062
|
-
});
|
|
1063
|
-
const textContent = response.content.find((block) => block.type === "text");
|
|
1064
|
-
const slug = textContent?.type === "text" ? textContent.text.trim() : "";
|
|
1065
|
-
return slug.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").slice(0, 50) || "session";
|
|
1066
|
-
} catch (error) {
|
|
1067
|
-
log4.warn({ err: error }, "Slug generation failed, using fallback");
|
|
1068
|
-
const now = /* @__PURE__ */ new Date();
|
|
1069
|
-
return `session-${now.getHours().toString().padStart(2, "0")}${now.getMinutes().toString().padStart(2, "0")}`;
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
async function saveSessionMemory(params) {
|
|
1073
|
-
try {
|
|
1074
|
-
const { TELETON_ROOT: TELETON_ROOT2 } = await import("./paths-XA2RJH4S.js");
|
|
1075
|
-
const memoryDir = join3(TELETON_ROOT2, "memory");
|
|
1076
|
-
await mkdir(memoryDir, { recursive: true });
|
|
1077
|
-
const now = /* @__PURE__ */ new Date();
|
|
1078
|
-
const dateStr = now.toISOString().split("T")[0];
|
|
1079
|
-
log4.info("Generating semantic slug for session memory...");
|
|
1080
|
-
const slug = await generateSlugViaClaude({
|
|
1081
|
-
messages: params.context.messages,
|
|
1082
|
-
apiKey: params.apiKey,
|
|
1083
|
-
provider: params.provider,
|
|
1084
|
-
utilityModel: params.utilityModel
|
|
1085
|
-
});
|
|
1086
|
-
const filename = `${dateStr}-${slug}.md`;
|
|
1087
|
-
const filepath = join3(memoryDir, filename);
|
|
1088
|
-
const timeStr = now.toISOString().split("T")[1].split(".")[0];
|
|
1089
|
-
log4.info("Generating session summary...");
|
|
1090
|
-
let summary;
|
|
1091
|
-
try {
|
|
1092
|
-
summary = await summarizeViaClaude({
|
|
1093
|
-
messages: params.context.messages,
|
|
1094
|
-
apiKey: params.apiKey,
|
|
1095
|
-
maxSummaryTokens: DEFAULT_MAX_SUMMARY_TOKENS,
|
|
1096
|
-
customInstructions: "Summarize this session comprehensively. Include key topics, decisions made, problems solved, and important context.",
|
|
1097
|
-
provider: params.provider,
|
|
1098
|
-
utilityModel: params.utilityModel
|
|
1099
|
-
});
|
|
1100
|
-
} catch (error) {
|
|
1101
|
-
log4.warn({ err: error }, "Session summary generation failed");
|
|
1102
|
-
summary = `Session contained ${params.context.messages.length} messages. Summary generation failed.`;
|
|
1103
|
-
}
|
|
1104
|
-
const content = `# Session Memory: ${dateStr} ${timeStr} UTC
|
|
1105
|
-
|
|
1106
|
-
## Metadata
|
|
1107
|
-
|
|
1108
|
-
- **Old Session ID**: \`${params.oldSessionId}\`
|
|
1109
|
-
- **New Session ID**: \`${params.newSessionId}\`
|
|
1110
|
-
- **Chat ID**: \`${params.chatId}\`
|
|
1111
|
-
- **Timestamp**: ${now.toISOString()}
|
|
1112
|
-
- **Message Count**: ${params.context.messages.length}
|
|
1113
|
-
|
|
1114
|
-
## Session Summary
|
|
1115
|
-
|
|
1116
|
-
${summary}
|
|
1117
|
-
|
|
1118
|
-
## Context
|
|
1119
|
-
|
|
1120
|
-
This session was compacted and migrated to a new session ID. The summary above preserves key information for continuity.
|
|
1121
|
-
|
|
1122
|
-
---
|
|
1123
|
-
|
|
1124
|
-
*Generated automatically by Teleton-AI session memory hook*
|
|
1125
|
-
`;
|
|
1126
|
-
await writeFile(filepath, content, "utf-8");
|
|
1127
|
-
const relPath = filepath.replace(TELETON_ROOT2, "~/.teleton");
|
|
1128
|
-
log4.info(`Session memory saved: ${relPath}`);
|
|
1129
|
-
} catch (error) {
|
|
1130
|
-
log4.error({ err: error }, "Failed to save session memory");
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
// src/memory/compaction.ts
|
|
1135
634
|
var DEFAULT_COMPACTION_CONFIG = {
|
|
1136
635
|
enabled: true,
|
|
1137
636
|
maxMessages: COMPACTION_MAX_MESSAGES,
|
|
@@ -1140,7 +639,7 @@ var DEFAULT_COMPACTION_CONFIG = {
|
|
|
1140
639
|
memoryFlushEnabled: true,
|
|
1141
640
|
softThresholdTokens: DEFAULT_SOFT_THRESHOLD_TOKENS
|
|
1142
641
|
};
|
|
1143
|
-
var
|
|
642
|
+
var log2 = createLogger("Memory");
|
|
1144
643
|
function estimateContextTokens(context) {
|
|
1145
644
|
let charCount = 0;
|
|
1146
645
|
if (context.systemPrompt) {
|
|
@@ -1172,7 +671,7 @@ function shouldFlushMemory(context, config, tokenCount) {
|
|
|
1172
671
|
const tokens = tokenCount ?? estimateContextTokens(context);
|
|
1173
672
|
const softThreshold = config.softThresholdTokens ?? FALLBACK_SOFT_THRESHOLD_TOKENS;
|
|
1174
673
|
if (tokens >= softThreshold) {
|
|
1175
|
-
|
|
674
|
+
log2.info(`Memory flush needed: ~${tokens} tokens (soft threshold: ${softThreshold})`);
|
|
1176
675
|
return true;
|
|
1177
676
|
}
|
|
1178
677
|
return false;
|
|
@@ -1194,7 +693,7 @@ function flushMemoryToDailyLog(context) {
|
|
|
1194
693
|
}
|
|
1195
694
|
}
|
|
1196
695
|
writeSummaryToDailyLog(summary.join("\n"));
|
|
1197
|
-
|
|
696
|
+
log2.info(`Memory flushed to daily log`);
|
|
1198
697
|
}
|
|
1199
698
|
function shouldCompact(context, config, tokenCount) {
|
|
1200
699
|
if (!config.enabled) {
|
|
@@ -1202,13 +701,13 @@ function shouldCompact(context, config, tokenCount) {
|
|
|
1202
701
|
}
|
|
1203
702
|
const messageCount = context.messages.length;
|
|
1204
703
|
if (config.maxMessages && messageCount >= config.maxMessages) {
|
|
1205
|
-
|
|
704
|
+
log2.info(`Compaction needed: ${messageCount} messages (max: ${config.maxMessages})`);
|
|
1206
705
|
return true;
|
|
1207
706
|
}
|
|
1208
707
|
if (config.maxTokens) {
|
|
1209
708
|
const tokens = tokenCount ?? estimateContextTokens(context);
|
|
1210
709
|
if (tokens >= config.maxTokens) {
|
|
1211
|
-
|
|
710
|
+
log2.info(`Compaction needed: ~${tokens} tokens (max: ${config.maxTokens})`);
|
|
1212
711
|
return true;
|
|
1213
712
|
}
|
|
1214
713
|
}
|
|
@@ -1254,12 +753,12 @@ async function compactContext(context, config, apiKey, provider, utilityModel) {
|
|
|
1254
753
|
iterations++;
|
|
1255
754
|
}
|
|
1256
755
|
if (hasOrphanedToolResults(context.messages.slice(cutIndex))) {
|
|
1257
|
-
|
|
756
|
+
log2.warn(`Compaction: couldn't find clean cut point, keeping all messages`);
|
|
1258
757
|
return context;
|
|
1259
758
|
}
|
|
1260
759
|
const recentMessages = context.messages.slice(cutIndex);
|
|
1261
760
|
const oldMessages = context.messages.slice(0, cutIndex);
|
|
1262
|
-
|
|
761
|
+
log2.info(
|
|
1263
762
|
`Compacting ${oldMessages.length} old messages, keeping ${recentMessages.length} recent (cut at clean boundary)`
|
|
1264
763
|
);
|
|
1265
764
|
try {
|
|
@@ -1289,7 +788,7 @@ Keep each section concise. Omit a section if empty. Preserve specific names, num
|
|
|
1289
788
|
provider,
|
|
1290
789
|
utilityModel
|
|
1291
790
|
});
|
|
1292
|
-
|
|
791
|
+
log2.info(`AI Summary: ${result.tokensUsed} tokens, ${result.chunksProcessed} chunks processed`);
|
|
1293
792
|
const summaryText = `[Auto-compacted ${oldMessages.length} messages]
|
|
1294
793
|
|
|
1295
794
|
${result.summary}`;
|
|
@@ -1303,7 +802,7 @@ ${result.summary}`;
|
|
|
1303
802
|
messages: [summaryMessage, ...recentMessages]
|
|
1304
803
|
};
|
|
1305
804
|
} catch (error) {
|
|
1306
|
-
|
|
805
|
+
log2.error({ err: error }, "AI summarization failed, using fallback");
|
|
1307
806
|
const summaryText = `[Auto-compacted: ${oldMessages.length} earlier messages from this conversation]`;
|
|
1308
807
|
const summaryMessage = {
|
|
1309
808
|
role: "user",
|
|
@@ -1318,7 +817,7 @@ ${result.summary}`;
|
|
|
1318
817
|
}
|
|
1319
818
|
async function compactAndSaveTranscript(sessionId, context, config, apiKey, chatId, provider, utilityModel) {
|
|
1320
819
|
const newSessionId = randomUUID();
|
|
1321
|
-
|
|
820
|
+
log2.info(`Creating compacted transcript: ${sessionId} \u2192 ${newSessionId}`);
|
|
1322
821
|
if (chatId) {
|
|
1323
822
|
await saveSessionMemory({
|
|
1324
823
|
oldSessionId: sessionId,
|
|
@@ -1352,7 +851,7 @@ var CompactionManager = class {
|
|
|
1352
851
|
if (this.config.memoryFlushEnabled) {
|
|
1353
852
|
flushMemoryToDailyLog(context);
|
|
1354
853
|
}
|
|
1355
|
-
|
|
854
|
+
log2.info(`Auto-compacting session ${sessionId}`);
|
|
1356
855
|
const newSessionId = await compactAndSaveTranscript(
|
|
1357
856
|
sessionId,
|
|
1358
857
|
context,
|
|
@@ -1362,7 +861,7 @@ var CompactionManager = class {
|
|
|
1362
861
|
provider,
|
|
1363
862
|
utilityModel
|
|
1364
863
|
);
|
|
1365
|
-
|
|
864
|
+
log2.info(`Compaction complete: ${newSessionId}`);
|
|
1366
865
|
return newSessionId;
|
|
1367
866
|
}
|
|
1368
867
|
updateConfig(config) {
|
|
@@ -1494,7 +993,7 @@ function maskOldToolResults(messages, options) {
|
|
|
1494
993
|
}
|
|
1495
994
|
|
|
1496
995
|
// src/agent/runtime.ts
|
|
1497
|
-
var
|
|
996
|
+
var log3 = createLogger("Agent");
|
|
1498
997
|
var globalTokenUsage = { totalTokens: 0, totalCost: 0 };
|
|
1499
998
|
function getTokenUsage() {
|
|
1500
999
|
return { ...globalTokenUsage };
|
|
@@ -1607,7 +1106,7 @@ var AgentRuntime = class {
|
|
|
1607
1106
|
if (this.userHookEvaluator) {
|
|
1608
1107
|
const hookResult = this.userHookEvaluator.evaluate(userMessage);
|
|
1609
1108
|
if (hookResult.blocked) {
|
|
1610
|
-
|
|
1109
|
+
log3.info("Message blocked by keyword filter");
|
|
1611
1110
|
return { content: hookResult.blockMessage ?? "", toolCalls: [] };
|
|
1612
1111
|
}
|
|
1613
1112
|
if (hookResult.additionalContext) {
|
|
@@ -1633,7 +1132,7 @@ var AgentRuntime = class {
|
|
|
1633
1132
|
};
|
|
1634
1133
|
await this.hookRunner.runModifyingHook("message:receive", msgEvent);
|
|
1635
1134
|
if (msgEvent.block) {
|
|
1636
|
-
|
|
1135
|
+
log3.info(`\u{1F6AB} Message blocked by hook: ${msgEvent.blockReason || "no reason"}`);
|
|
1637
1136
|
return { content: "", toolCalls: [] };
|
|
1638
1137
|
}
|
|
1639
1138
|
effectiveMessage = sanitizeForContext(msgEvent.text);
|
|
@@ -1645,7 +1144,7 @@ var AgentRuntime = class {
|
|
|
1645
1144
|
const now = timestamp ?? Date.now();
|
|
1646
1145
|
const resetPolicy = this.config.agent.session_reset_policy;
|
|
1647
1146
|
if (shouldResetSession(session, resetPolicy)) {
|
|
1648
|
-
|
|
1147
|
+
log3.info(`\u{1F504} Auto-resetting session based on policy`);
|
|
1649
1148
|
if (this.hookRunner) {
|
|
1650
1149
|
await this.hookRunner.runObservingHook("session:end", {
|
|
1651
1150
|
sessionId: session.sessionId,
|
|
@@ -1655,7 +1154,7 @@ var AgentRuntime = class {
|
|
|
1655
1154
|
}
|
|
1656
1155
|
if (transcriptExists(session.sessionId)) {
|
|
1657
1156
|
try {
|
|
1658
|
-
|
|
1157
|
+
log3.info(`\u{1F4BE} Saving memory before daily reset...`);
|
|
1659
1158
|
const oldContext = loadContextFromTranscript(session.sessionId);
|
|
1660
1159
|
await saveSessionMemory({
|
|
1661
1160
|
oldSessionId: session.sessionId,
|
|
@@ -1666,9 +1165,9 @@ var AgentRuntime = class {
|
|
|
1666
1165
|
provider: this.config.agent.provider,
|
|
1667
1166
|
utilityModel: this.config.agent.utility_model
|
|
1668
1167
|
});
|
|
1669
|
-
|
|
1168
|
+
log3.info(`\u2705 Memory saved before reset`);
|
|
1670
1169
|
} catch (error) {
|
|
1671
|
-
|
|
1170
|
+
log3.warn({ err: error }, `\u26A0\uFE0F Failed to save memory before reset`);
|
|
1672
1171
|
}
|
|
1673
1172
|
}
|
|
1674
1173
|
session = resetSessionWithPolicy(chatId, resetPolicy);
|
|
@@ -1676,9 +1175,9 @@ var AgentRuntime = class {
|
|
|
1676
1175
|
let context = loadContextFromTranscript(session.sessionId);
|
|
1677
1176
|
const isNewSession = context.messages.length === 0;
|
|
1678
1177
|
if (!isNewSession) {
|
|
1679
|
-
|
|
1178
|
+
log3.info(`\u{1F4D6} Loading existing session: ${session.sessionId}`);
|
|
1680
1179
|
} else {
|
|
1681
|
-
|
|
1180
|
+
log3.info(`\u{1F195} Starting new session: ${session.sessionId}`);
|
|
1682
1181
|
}
|
|
1683
1182
|
if (this.hookRunner) {
|
|
1684
1183
|
await this.hookRunner.runObservingHook("session:start", {
|
|
@@ -1707,21 +1206,32 @@ var AgentRuntime = class {
|
|
|
1707
1206
|
formattedMessage = `${pendingContext}
|
|
1708
1207
|
|
|
1709
1208
|
${formattedMessage}`;
|
|
1710
|
-
|
|
1209
|
+
log3.debug(`\u{1F4CB} Including ${pendingContext.split("\n").length - 1} pending messages`);
|
|
1711
1210
|
}
|
|
1712
|
-
|
|
1211
|
+
log3.debug(`\u{1F4E8} Formatted message: ${formattedMessage.substring(0, 100)}...`);
|
|
1713
1212
|
const preview = formattedMessage.slice(0, 50).replace(/\n/g, " ");
|
|
1714
1213
|
const who = senderUsername ? `@${senderUsername}` : userName;
|
|
1715
1214
|
const msgType = isGroup ? `Group ${chatId} ${who}` : `DM ${who}`;
|
|
1716
|
-
|
|
1215
|
+
log3.info(`\u{1F4E8} ${msgType}: "${preview}${formattedMessage.length > 50 ? "..." : ""}"`);
|
|
1717
1216
|
let relevantContext = "";
|
|
1718
1217
|
let queryEmbedding;
|
|
1719
1218
|
const isNonTrivial = !isTrivialMessage(effectiveMessage);
|
|
1720
1219
|
if (this.embedder && isNonTrivial) {
|
|
1721
1220
|
try {
|
|
1722
|
-
|
|
1221
|
+
let searchQuery = effectiveMessage;
|
|
1222
|
+
const recentUserMsgs = context.messages.filter((m) => m.role === "user" && typeof m.content === "string").slice(-3).map((m) => {
|
|
1223
|
+
const text = m.content;
|
|
1224
|
+
const bodyMatch = text.match(/\] (.+)/s);
|
|
1225
|
+
return (bodyMatch ? bodyMatch[1] : text).trim();
|
|
1226
|
+
}).filter((t) => t.length > 0);
|
|
1227
|
+
if (recentUserMsgs.length > 0) {
|
|
1228
|
+
searchQuery = recentUserMsgs.join(" ") + " " + effectiveMessage;
|
|
1229
|
+
}
|
|
1230
|
+
queryEmbedding = await this.embedder.embedQuery(
|
|
1231
|
+
searchQuery.slice(0, EMBEDDING_QUERY_MAX_CHARS)
|
|
1232
|
+
);
|
|
1723
1233
|
} catch (error) {
|
|
1724
|
-
|
|
1234
|
+
log3.warn({ err: error }, "Embedding computation failed");
|
|
1725
1235
|
}
|
|
1726
1236
|
}
|
|
1727
1237
|
if (this.contextBuilder && isNonTrivial) {
|
|
@@ -1755,12 +1265,12 @@ ${sanitizedFeed.join("\n")}`
|
|
|
1755
1265
|
}
|
|
1756
1266
|
if (contextParts.length > 0) {
|
|
1757
1267
|
relevantContext = contextParts.join("\n\n");
|
|
1758
|
-
|
|
1268
|
+
log3.debug(
|
|
1759
1269
|
`\u{1F50D} Found ${dbContext.relevantKnowledge.length} knowledge chunks, ${dbContext.relevantFeed.length} feed messages`
|
|
1760
1270
|
);
|
|
1761
1271
|
}
|
|
1762
1272
|
} catch (error) {
|
|
1763
|
-
|
|
1273
|
+
log3.warn({ err: error }, "Context building failed");
|
|
1764
1274
|
}
|
|
1765
1275
|
}
|
|
1766
1276
|
const memoryStats = this.getMemoryStats();
|
|
@@ -1828,7 +1338,7 @@ ${allHookContext}` : "");
|
|
|
1828
1338
|
this.config.agent.utility_model
|
|
1829
1339
|
);
|
|
1830
1340
|
if (preemptiveCompaction) {
|
|
1831
|
-
|
|
1341
|
+
log3.info(`\u{1F5DC}\uFE0F Preemptive compaction triggered, reloading session...`);
|
|
1832
1342
|
session = getSession(chatId);
|
|
1833
1343
|
context = loadContextFromTranscript(session.sessionId);
|
|
1834
1344
|
context.messages.push(userMsg);
|
|
@@ -1850,7 +1360,7 @@ ${allHookContext}` : "");
|
|
|
1850
1360
|
chatId,
|
|
1851
1361
|
isAdmin
|
|
1852
1362
|
);
|
|
1853
|
-
|
|
1363
|
+
log3.info(`\u{1F50D} Tool RAG: ${tools.length}/${this.toolRegistry.count} tools selected`);
|
|
1854
1364
|
} else {
|
|
1855
1365
|
tools = this.toolRegistry?.getForContext(
|
|
1856
1366
|
effectiveIsGroup,
|
|
@@ -1869,9 +1379,10 @@ ${allHookContext}` : "");
|
|
|
1869
1379
|
const totalToolCalls = [];
|
|
1870
1380
|
const accumulatedTexts = [];
|
|
1871
1381
|
const accumulatedUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalCost: 0 };
|
|
1382
|
+
const seenToolSignatures = /* @__PURE__ */ new Set();
|
|
1872
1383
|
while (iteration < maxIterations) {
|
|
1873
1384
|
iteration++;
|
|
1874
|
-
|
|
1385
|
+
log3.debug(`\u{1F504} Agentic iteration ${iteration}/${maxIterations}`);
|
|
1875
1386
|
const iterationStartIndex = context.messages.length;
|
|
1876
1387
|
const maskedMessages = maskOldToolResults(context.messages, {
|
|
1877
1388
|
toolRegistry: this.toolRegistry ?? void 0,
|
|
@@ -1910,35 +1421,35 @@ ${allHookContext}` : "");
|
|
|
1910
1421
|
"Context overflow persists after session reset. Message may be too large for the model's context window."
|
|
1911
1422
|
);
|
|
1912
1423
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1424
|
+
log3.error(`\u{1F6A8} Context overflow detected: ${errorMsg}`);
|
|
1425
|
+
log3.info(`\u{1F4BE} Saving session memory before reset...`);
|
|
1915
1426
|
const summary = extractContextSummary(context, CONTEXT_OVERFLOW_SUMMARY_MESSAGES);
|
|
1916
1427
|
appendToDailyLog(summary);
|
|
1917
|
-
|
|
1428
|
+
log3.info(`\u2705 Memory saved to daily log`);
|
|
1918
1429
|
const archived = archiveTranscript(session.sessionId);
|
|
1919
1430
|
if (!archived) {
|
|
1920
|
-
|
|
1431
|
+
log3.error(
|
|
1921
1432
|
`\u26A0\uFE0F Failed to archive transcript ${session.sessionId}, proceeding with reset anyway`
|
|
1922
1433
|
);
|
|
1923
1434
|
}
|
|
1924
|
-
|
|
1435
|
+
log3.info(`\u{1F504} Resetting session due to context overflow...`);
|
|
1925
1436
|
session = resetSession(chatId);
|
|
1926
1437
|
context = { messages: [userMsg] };
|
|
1927
1438
|
appendToTranscript(session.sessionId, userMsg);
|
|
1928
|
-
|
|
1439
|
+
log3.info(`\u{1F504} Retrying with fresh context...`);
|
|
1929
1440
|
continue;
|
|
1930
1441
|
} else if (errorMsg.toLowerCase().includes("rate") || errorMsg.includes("429")) {
|
|
1931
1442
|
rateLimitRetries++;
|
|
1932
1443
|
if (rateLimitRetries <= RATE_LIMIT_MAX_RETRIES) {
|
|
1933
1444
|
const delay = 1e3 * Math.pow(2, rateLimitRetries - 1);
|
|
1934
|
-
|
|
1445
|
+
log3.warn(
|
|
1935
1446
|
`\u{1F6AB} Rate limited, retrying in ${delay}ms (attempt ${rateLimitRetries}/${RATE_LIMIT_MAX_RETRIES})...`
|
|
1936
1447
|
);
|
|
1937
1448
|
await new Promise((r3) => setTimeout(r3, delay));
|
|
1938
1449
|
iteration--;
|
|
1939
1450
|
continue;
|
|
1940
1451
|
}
|
|
1941
|
-
|
|
1452
|
+
log3.error(`\u{1F6AB} Rate limited after ${RATE_LIMIT_MAX_RETRIES} retries: ${errorMsg}`);
|
|
1942
1453
|
throw new Error(
|
|
1943
1454
|
`API rate limited after ${RATE_LIMIT_MAX_RETRIES} retries. Please try again later.`
|
|
1944
1455
|
);
|
|
@@ -1946,19 +1457,19 @@ ${allHookContext}` : "");
|
|
|
1946
1457
|
serverErrorRetries++;
|
|
1947
1458
|
if (serverErrorRetries <= SERVER_ERROR_MAX_RETRIES) {
|
|
1948
1459
|
const delay = 2e3 * Math.pow(2, serverErrorRetries - 1);
|
|
1949
|
-
|
|
1460
|
+
log3.warn(
|
|
1950
1461
|
`\u{1F504} Server error, retrying in ${delay}ms (attempt ${serverErrorRetries}/${SERVER_ERROR_MAX_RETRIES})...`
|
|
1951
1462
|
);
|
|
1952
1463
|
await new Promise((r3) => setTimeout(r3, delay));
|
|
1953
1464
|
iteration--;
|
|
1954
1465
|
continue;
|
|
1955
1466
|
}
|
|
1956
|
-
|
|
1467
|
+
log3.error(`\u{1F6A8} Server error after ${SERVER_ERROR_MAX_RETRIES} retries: ${errorMsg}`);
|
|
1957
1468
|
throw new Error(
|
|
1958
1469
|
`API server error after ${SERVER_ERROR_MAX_RETRIES} retries. The provider may be experiencing issues.`
|
|
1959
1470
|
);
|
|
1960
1471
|
} else {
|
|
1961
|
-
|
|
1472
|
+
log3.error(`\u{1F6A8} API error: ${errorMsg}`);
|
|
1962
1473
|
throw new Error(`API error: ${errorMsg || "Unknown error"}`);
|
|
1963
1474
|
}
|
|
1964
1475
|
}
|
|
@@ -1975,24 +1486,25 @@ ${allHookContext}` : "");
|
|
|
1975
1486
|
}
|
|
1976
1487
|
const toolCalls = response2.message.content.filter((block) => block.type === "toolCall");
|
|
1977
1488
|
if (toolCalls.length === 0) {
|
|
1978
|
-
|
|
1489
|
+
log3.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 done`);
|
|
1979
1490
|
finalResponse = response2;
|
|
1980
1491
|
break;
|
|
1981
1492
|
}
|
|
1982
1493
|
if (!this.toolRegistry || !toolContext) {
|
|
1983
|
-
|
|
1494
|
+
log3.error("\u26A0\uFE0F Cannot execute tools: registry or context missing");
|
|
1984
1495
|
break;
|
|
1985
1496
|
}
|
|
1986
|
-
|
|
1497
|
+
log3.debug(`\u{1F527} Executing ${toolCalls.length} tool call(s)`);
|
|
1987
1498
|
context.messages.push(response2.message);
|
|
1988
1499
|
const iterationToolNames = [];
|
|
1500
|
+
const fullContext = {
|
|
1501
|
+
...toolContext,
|
|
1502
|
+
chatId,
|
|
1503
|
+
isGroup: effectiveIsGroup
|
|
1504
|
+
};
|
|
1505
|
+
const toolPlans = [];
|
|
1989
1506
|
for (const block of toolCalls) {
|
|
1990
1507
|
if (block.type !== "toolCall") continue;
|
|
1991
|
-
const fullContext = {
|
|
1992
|
-
...toolContext,
|
|
1993
|
-
chatId,
|
|
1994
|
-
isGroup: effectiveIsGroup
|
|
1995
|
-
};
|
|
1996
1508
|
let toolParams = block.arguments ?? {};
|
|
1997
1509
|
let blocked = false;
|
|
1998
1510
|
let blockReason = "";
|
|
@@ -2013,74 +1525,88 @@ ${allHookContext}` : "");
|
|
|
2013
1525
|
toolParams = structuredClone(beforeEvent.params);
|
|
2014
1526
|
}
|
|
2015
1527
|
}
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
toolName: block.name,
|
|
2047
|
-
params: structuredClone(toolParams),
|
|
2048
|
-
error: errMsg,
|
|
2049
|
-
// Note: stack traces are exposed to plugins for debugging — accepted tradeoff
|
|
2050
|
-
stack: errStack,
|
|
2051
|
-
chatId,
|
|
2052
|
-
isGroup: effectiveIsGroup,
|
|
2053
|
-
durationMs: durationMs2
|
|
1528
|
+
toolPlans.push({ block, blocked, blockReason, params: toolParams });
|
|
1529
|
+
}
|
|
1530
|
+
const execResults = new Array(toolPlans.length);
|
|
1531
|
+
{
|
|
1532
|
+
let cursor = 0;
|
|
1533
|
+
const runWorker = async () => {
|
|
1534
|
+
while (cursor < toolPlans.length) {
|
|
1535
|
+
const idx = cursor++;
|
|
1536
|
+
const plan = toolPlans[idx];
|
|
1537
|
+
if (plan.blocked) {
|
|
1538
|
+
execResults[idx] = {
|
|
1539
|
+
result: { success: false, error: plan.blockReason },
|
|
1540
|
+
durationMs: 0
|
|
1541
|
+
};
|
|
1542
|
+
continue;
|
|
1543
|
+
}
|
|
1544
|
+
const startTime = Date.now();
|
|
1545
|
+
try {
|
|
1546
|
+
const result = await this.toolRegistry.execute(
|
|
1547
|
+
{ ...plan.block, arguments: plan.params },
|
|
1548
|
+
fullContext
|
|
1549
|
+
);
|
|
1550
|
+
execResults[idx] = { result, durationMs: Date.now() - startTime };
|
|
1551
|
+
} catch (execErr) {
|
|
1552
|
+
const errMsg = execErr instanceof Error ? execErr.message : String(execErr);
|
|
1553
|
+
const errStack = execErr instanceof Error ? execErr.stack : void 0;
|
|
1554
|
+
execResults[idx] = {
|
|
1555
|
+
result: { success: false, error: errMsg },
|
|
1556
|
+
durationMs: Date.now() - startTime,
|
|
1557
|
+
execError: { message: errMsg, stack: errStack }
|
|
2054
1558
|
};
|
|
2055
|
-
await this.hookRunner.runObservingHook("tool:error", errorEvent);
|
|
2056
1559
|
}
|
|
2057
1560
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
1561
|
+
};
|
|
1562
|
+
const workers = Math.min(TOOL_CONCURRENCY_LIMIT, toolPlans.length);
|
|
1563
|
+
await Promise.all(Array.from({ length: workers }, () => runWorker()));
|
|
1564
|
+
}
|
|
1565
|
+
for (let i = 0; i < toolPlans.length; i++) {
|
|
1566
|
+
const plan = toolPlans[i];
|
|
1567
|
+
const { block } = plan;
|
|
1568
|
+
const exec = execResults[i];
|
|
1569
|
+
if (exec.execError && this.hookRunner) {
|
|
1570
|
+
const errorEvent = {
|
|
1571
|
+
toolName: block.name,
|
|
1572
|
+
params: structuredClone(plan.params),
|
|
1573
|
+
error: exec.execError.message,
|
|
1574
|
+
stack: exec.execError.stack,
|
|
1575
|
+
chatId,
|
|
1576
|
+
isGroup: effectiveIsGroup,
|
|
1577
|
+
durationMs: exec.durationMs
|
|
1578
|
+
};
|
|
1579
|
+
await this.hookRunner.runObservingHook("tool:error", errorEvent);
|
|
2070
1580
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
1581
|
+
if (this.hookRunner) {
|
|
1582
|
+
const afterEvent = {
|
|
1583
|
+
toolName: block.name,
|
|
1584
|
+
params: structuredClone(plan.params),
|
|
1585
|
+
result: {
|
|
1586
|
+
success: exec.result.success,
|
|
1587
|
+
data: exec.result.data,
|
|
1588
|
+
error: exec.result.error
|
|
1589
|
+
},
|
|
1590
|
+
durationMs: exec.durationMs,
|
|
1591
|
+
chatId,
|
|
1592
|
+
isGroup: effectiveIsGroup,
|
|
1593
|
+
...plan.blocked ? { blocked: true, blockReason: plan.blockReason } : {}
|
|
1594
|
+
};
|
|
1595
|
+
await this.hookRunner.runObservingHook("tool:after", afterEvent);
|
|
1596
|
+
}
|
|
1597
|
+
log3.debug(`${block.name}: ${exec.result.success ? "\u2713" : "\u2717"} ${exec.result.error || ""}`);
|
|
1598
|
+
iterationToolNames.push(`${block.name} ${exec.result.success ? "\u2713" : "\u2717"}`);
|
|
2073
1599
|
totalToolCalls.push({
|
|
2074
1600
|
name: block.name,
|
|
2075
1601
|
input: block.arguments
|
|
2076
1602
|
});
|
|
2077
|
-
let resultText = JSON.stringify(result);
|
|
1603
|
+
let resultText = JSON.stringify(exec.result);
|
|
2078
1604
|
if (resultText.length > MAX_TOOL_RESULT_SIZE) {
|
|
2079
|
-
|
|
2080
|
-
const data = result.data;
|
|
1605
|
+
log3.warn(`\u26A0\uFE0F Tool result too large (${resultText.length} chars), truncating...`);
|
|
1606
|
+
const data = exec.result.data;
|
|
2081
1607
|
if (data?.summary || data?.message) {
|
|
2082
1608
|
resultText = JSON.stringify({
|
|
2083
|
-
success: result.success,
|
|
1609
|
+
success: exec.result.success,
|
|
2084
1610
|
data: {
|
|
2085
1611
|
summary: data.summary || data.message,
|
|
2086
1612
|
_truncated: true,
|
|
@@ -2089,11 +1615,27 @@ ${allHookContext}` : "");
|
|
|
2089
1615
|
}
|
|
2090
1616
|
});
|
|
2091
1617
|
} else {
|
|
2092
|
-
|
|
1618
|
+
const summarized = {
|
|
1619
|
+
_truncated: true,
|
|
1620
|
+
_originalSize: resultText.length,
|
|
1621
|
+
_message: "Full data truncated. Use limit parameter for smaller results."
|
|
1622
|
+
};
|
|
1623
|
+
if (data && typeof data === "object") {
|
|
1624
|
+
for (const [key, value] of Object.entries(data)) {
|
|
1625
|
+
if (Array.isArray(value)) {
|
|
1626
|
+
summarized[key] = `[${value.length} items]`;
|
|
1627
|
+
} else if (typeof value === "string" && value.length > 500) {
|
|
1628
|
+
summarized[key] = value.slice(0, 500) + "...[truncated]";
|
|
1629
|
+
} else {
|
|
1630
|
+
summarized[key] = value;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
resultText = JSON.stringify({ success: exec.result.success, data: summarized });
|
|
2093
1635
|
}
|
|
2094
1636
|
}
|
|
2095
1637
|
if (provider === "cocoon") {
|
|
2096
|
-
const { wrapToolResult } = await import("./tool-adapter-
|
|
1638
|
+
const { wrapToolResult } = await import("./tool-adapter-IVX2XQJE.js");
|
|
2097
1639
|
const cocoonResultMsg = {
|
|
2098
1640
|
role: "user",
|
|
2099
1641
|
content: [
|
|
@@ -2117,21 +1659,33 @@ ${allHookContext}` : "");
|
|
|
2117
1659
|
text: resultText
|
|
2118
1660
|
}
|
|
2119
1661
|
],
|
|
2120
|
-
isError: !result.success,
|
|
1662
|
+
isError: !exec.result.success,
|
|
2121
1663
|
timestamp: Date.now()
|
|
2122
1664
|
};
|
|
2123
1665
|
context.messages.push(toolResultMsg);
|
|
2124
1666
|
appendToTranscript(session.sessionId, toolResultMsg);
|
|
2125
1667
|
}
|
|
2126
1668
|
}
|
|
2127
|
-
|
|
1669
|
+
log3.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 ${iterationToolNames.join(", ")}`);
|
|
1670
|
+
const iterSignatures = toolPlans.map(
|
|
1671
|
+
(p2) => `${p2.block.name}:${JSON.stringify(p2.params, Object.keys(p2.params).sort())}`
|
|
1672
|
+
);
|
|
1673
|
+
const allDuplicates = iterSignatures.length > 0 && iterSignatures.every((sig) => seenToolSignatures.has(sig));
|
|
1674
|
+
for (const sig of iterSignatures) seenToolSignatures.add(sig);
|
|
1675
|
+
if (allDuplicates) {
|
|
1676
|
+
log3.warn(
|
|
1677
|
+
`\u{1F501} Loop stall detected: all ${iterSignatures.length} tool call(s) are repeats \u2014 breaking early`
|
|
1678
|
+
);
|
|
1679
|
+
finalResponse = response2;
|
|
1680
|
+
break;
|
|
1681
|
+
}
|
|
2128
1682
|
if (iteration === maxIterations) {
|
|
2129
|
-
|
|
1683
|
+
log3.info(`\u26A0\uFE0F Max iterations reached (${maxIterations})`);
|
|
2130
1684
|
finalResponse = response2;
|
|
2131
1685
|
}
|
|
2132
1686
|
}
|
|
2133
1687
|
if (!finalResponse) {
|
|
2134
|
-
|
|
1688
|
+
log3.error("\u26A0\uFE0F Agentic loop exited early without final response");
|
|
2135
1689
|
return {
|
|
2136
1690
|
content: "Internal error: Agent loop failed to produce a response.",
|
|
2137
1691
|
toolCalls: []
|
|
@@ -2142,14 +1696,6 @@ ${allHookContext}` : "");
|
|
|
2142
1696
|
if (lastMsg?.role !== "assistant") {
|
|
2143
1697
|
context.messages.push(response.message);
|
|
2144
1698
|
}
|
|
2145
|
-
const newSessionId = await this.compactionManager.checkAndCompact(
|
|
2146
|
-
session.sessionId,
|
|
2147
|
-
context,
|
|
2148
|
-
getEffectiveApiKey(this.config.agent.provider, this.config.agent.api_key),
|
|
2149
|
-
chatId,
|
|
2150
|
-
this.config.agent.provider,
|
|
2151
|
-
this.config.agent.utility_model
|
|
2152
|
-
);
|
|
2153
1699
|
const sessionUpdate = {
|
|
2154
1700
|
updatedAt: Date.now(),
|
|
2155
1701
|
messageCount: session.messageCount + 1,
|
|
@@ -2158,9 +1704,6 @@ ${allHookContext}` : "");
|
|
|
2158
1704
|
inputTokens: (session.inputTokens ?? 0) + accumulatedUsage.input + accumulatedUsage.cacheRead + accumulatedUsage.cacheWrite,
|
|
2159
1705
|
outputTokens: (session.outputTokens ?? 0) + accumulatedUsage.output
|
|
2160
1706
|
};
|
|
2161
|
-
if (newSessionId) {
|
|
2162
|
-
sessionUpdate.sessionId = newSessionId;
|
|
2163
|
-
}
|
|
2164
1707
|
updateSession(chatId, sessionUpdate);
|
|
2165
1708
|
if (accumulatedUsage.input > 0 || accumulatedUsage.output > 0) {
|
|
2166
1709
|
const u = accumulatedUsage;
|
|
@@ -2170,20 +1713,20 @@ ${allHookContext}` : "");
|
|
|
2170
1713
|
if (u.cacheRead) cacheParts.push(`${(u.cacheRead / 1e3).toFixed(1)}K cached`);
|
|
2171
1714
|
if (u.cacheWrite) cacheParts.push(`${(u.cacheWrite / 1e3).toFixed(1)}K new`);
|
|
2172
1715
|
const cacheInfo = cacheParts.length > 0 ? ` (${cacheParts.join(", ")})` : "";
|
|
2173
|
-
|
|
1716
|
+
log3.info(`\u{1F4B0} ${inK}K in${cacheInfo}, ${u.output} out | $${u.totalCost.toFixed(3)}`);
|
|
2174
1717
|
globalTokenUsage.totalTokens += u.input + u.output + u.cacheRead + u.cacheWrite;
|
|
2175
1718
|
globalTokenUsage.totalCost += u.totalCost;
|
|
2176
1719
|
}
|
|
2177
1720
|
let content = accumulatedTexts.join("\n").trim() || response.text;
|
|
2178
1721
|
const usedTelegramSendTool = totalToolCalls.some((tc) => TELEGRAM_SEND_TOOLS.has(tc.name));
|
|
2179
1722
|
if (!content && totalToolCalls.length > 0 && !usedTelegramSendTool) {
|
|
2180
|
-
|
|
1723
|
+
log3.warn("\u26A0\uFE0F Empty response after tool calls - generating fallback");
|
|
2181
1724
|
content = "I executed the requested action but couldn't generate a response. Please try again.";
|
|
2182
1725
|
} else if (!content && usedTelegramSendTool) {
|
|
2183
|
-
|
|
1726
|
+
log3.info("\u2705 Response sent via Telegram tool - no additional text needed");
|
|
2184
1727
|
content = "";
|
|
2185
1728
|
} else if (!content && accumulatedUsage.input === 0 && accumulatedUsage.output === 0) {
|
|
2186
|
-
|
|
1729
|
+
log3.warn("\u26A0\uFE0F Empty response with zero tokens - possible API issue");
|
|
2187
1730
|
content = "I couldn't process your request. Please try again.";
|
|
2188
1731
|
}
|
|
2189
1732
|
let responseMetadata = {};
|
|
@@ -2200,7 +1743,7 @@ ${allHookContext}` : "");
|
|
|
2200
1743
|
};
|
|
2201
1744
|
await this.hookRunner.runModifyingHook("response:before", responseBeforeEvent);
|
|
2202
1745
|
if (responseBeforeEvent.block) {
|
|
2203
|
-
|
|
1746
|
+
log3.info(
|
|
2204
1747
|
`\u{1F6AB} Response blocked by hook: ${responseBeforeEvent.blockReason || "no reason"}`
|
|
2205
1748
|
);
|
|
2206
1749
|
content = "";
|
|
@@ -2227,7 +1770,7 @@ ${allHookContext}` : "");
|
|
|
2227
1770
|
toolCalls: totalToolCalls
|
|
2228
1771
|
};
|
|
2229
1772
|
} catch (error) {
|
|
2230
|
-
|
|
1773
|
+
log3.error({ err: error }, "Agent error");
|
|
2231
1774
|
throw error;
|
|
2232
1775
|
}
|
|
2233
1776
|
}
|
|
@@ -2240,7 +1783,7 @@ ${allHookContext}` : "");
|
|
|
2240
1783
|
).run(chatId);
|
|
2241
1784
|
db.prepare(`DELETE FROM tg_messages WHERE chat_id = ?`).run(chatId);
|
|
2242
1785
|
resetSession(chatId);
|
|
2243
|
-
|
|
1786
|
+
log3.info(`\u{1F5D1}\uFE0F Cleared history for chat ${chatId}`);
|
|
2244
1787
|
}
|
|
2245
1788
|
getConfig() {
|
|
2246
1789
|
return this.config;
|
|
@@ -2261,7 +1804,7 @@ ${allHookContext}` : "");
|
|
|
2261
1804
|
}
|
|
2262
1805
|
configureCompaction(config) {
|
|
2263
1806
|
this.compactionManager.updateConfig(config);
|
|
2264
|
-
|
|
1807
|
+
log3.info({ config: this.compactionManager.getConfig() }, `\u{1F5DC}\uFE0F Compaction config updated`);
|
|
2265
1808
|
}
|
|
2266
1809
|
getCompactionConfig() {
|
|
2267
1810
|
return this.compactionManager.getConfig();
|
|
@@ -2349,6 +1892,17 @@ function prefixButtons(rows, pluginName) {
|
|
|
2349
1892
|
|
|
2350
1893
|
// src/bot/services/html-parser.ts
|
|
2351
1894
|
import { Api as Api2 } from "telegram";
|
|
1895
|
+
|
|
1896
|
+
// src/utils/gramjs-bigint.ts
|
|
1897
|
+
import { randomBytes } from "crypto";
|
|
1898
|
+
function toLong(value) {
|
|
1899
|
+
return typeof value === "bigint" ? value : BigInt(value);
|
|
1900
|
+
}
|
|
1901
|
+
function randomLong() {
|
|
1902
|
+
return randomBytes(8).readBigUInt64BE();
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
// src/bot/services/html-parser.ts
|
|
2352
1906
|
function parseHtml(html) {
|
|
2353
1907
|
const entities = [];
|
|
2354
1908
|
let text = "";
|
|
@@ -2399,8 +1953,7 @@ function parseHtml(html) {
|
|
|
2399
1953
|
new Api2.MessageEntityCustomEmoji({
|
|
2400
1954
|
offset: open.offset,
|
|
2401
1955
|
length,
|
|
2402
|
-
|
|
2403
|
-
documentId: BigInt(open.emojiId)
|
|
1956
|
+
documentId: toLong(open.emojiId)
|
|
2404
1957
|
})
|
|
2405
1958
|
);
|
|
2406
1959
|
}
|
|
@@ -2461,7 +2014,7 @@ function unescapeHtml(text) {
|
|
|
2461
2014
|
}
|
|
2462
2015
|
|
|
2463
2016
|
// src/bot/inline-router.ts
|
|
2464
|
-
var
|
|
2017
|
+
var log4 = createLogger("InlineRouter");
|
|
2465
2018
|
var INLINE_TIMEOUT_MS = 5e3;
|
|
2466
2019
|
var CALLBACK_TIMEOUT_MS = 15e3;
|
|
2467
2020
|
function compileGlob(pattern) {
|
|
@@ -2482,11 +2035,11 @@ var InlineRouter = class {
|
|
|
2482
2035
|
}
|
|
2483
2036
|
registerPlugin(name, handlers) {
|
|
2484
2037
|
this.plugins.set(name, handlers);
|
|
2485
|
-
|
|
2038
|
+
log4.info(`Registered plugin "${name}" for inline routing`);
|
|
2486
2039
|
}
|
|
2487
2040
|
unregisterPlugin(name) {
|
|
2488
2041
|
this.plugins.delete(name);
|
|
2489
|
-
|
|
2042
|
+
log4.info(`Unregistered plugin "${name}" from inline routing`);
|
|
2490
2043
|
}
|
|
2491
2044
|
hasPlugin(name) {
|
|
2492
2045
|
return this.plugins.has(name);
|
|
@@ -2558,7 +2111,7 @@ var InlineRouter = class {
|
|
|
2558
2111
|
is_personal: true
|
|
2559
2112
|
});
|
|
2560
2113
|
} catch (error) {
|
|
2561
|
-
|
|
2114
|
+
log4.error({ err: error }, `Plugin "${pluginName}" inline query handler failed`);
|
|
2562
2115
|
try {
|
|
2563
2116
|
await ctx.answerInlineQuery([], { cache_time: 0, is_personal: true });
|
|
2564
2117
|
} catch {
|
|
@@ -2618,7 +2171,7 @@ var InlineRouter = class {
|
|
|
2618
2171
|
} catch (error) {
|
|
2619
2172
|
const errMsg = error?.errorMessage;
|
|
2620
2173
|
if (errMsg === "MESSAGE_NOT_MODIFIED") return;
|
|
2621
|
-
|
|
2174
|
+
log4.debug(`GramJS edit failed, falling back to Grammy: ${errMsg || error}`);
|
|
2622
2175
|
}
|
|
2623
2176
|
}
|
|
2624
2177
|
const replyMarkup = styledButtons ? toGrammyKeyboard(styledButtons) : void 0;
|
|
@@ -2638,7 +2191,7 @@ var InlineRouter = class {
|
|
|
2638
2191
|
await ctx.answerCallbackQuery();
|
|
2639
2192
|
}
|
|
2640
2193
|
} catch (error) {
|
|
2641
|
-
|
|
2194
|
+
log4.error({ err: error }, `Plugin "${pluginName}" callback handler failed`);
|
|
2642
2195
|
if (!answered) {
|
|
2643
2196
|
try {
|
|
2644
2197
|
await ctx.answerCallbackQuery({ text: "Error processing action" });
|
|
@@ -2661,7 +2214,7 @@ var InlineRouter = class {
|
|
|
2661
2214
|
};
|
|
2662
2215
|
await plugin.onChosenResult(crCtx);
|
|
2663
2216
|
} catch (error) {
|
|
2664
|
-
|
|
2217
|
+
log4.error({ err: error }, `Plugin "${pluginName}" chosen result handler failed`);
|
|
2665
2218
|
}
|
|
2666
2219
|
}
|
|
2667
2220
|
/**
|
|
@@ -2739,8 +2292,8 @@ function withTimeout(promise, ms, message) {
|
|
|
2739
2292
|
}
|
|
2740
2293
|
|
|
2741
2294
|
// src/agent/tools/plugin-loader.ts
|
|
2742
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
2743
|
-
import { join as
|
|
2295
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync4, existsSync as existsSync5, statSync } from "fs";
|
|
2296
|
+
import { join as join3 } from "path";
|
|
2744
2297
|
import { pathToFileURL } from "url";
|
|
2745
2298
|
import { execFile } from "child_process";
|
|
2746
2299
|
|
|
@@ -2771,7 +2324,7 @@ import { promisify } from "util";
|
|
|
2771
2324
|
|
|
2772
2325
|
// src/agent/tools/plugin-validator.ts
|
|
2773
2326
|
import { z } from "zod";
|
|
2774
|
-
var
|
|
2327
|
+
var log5 = createLogger("PluginValidator");
|
|
2775
2328
|
var ManifestSchema = z.object({
|
|
2776
2329
|
name: z.string().min(1).max(64).regex(
|
|
2777
2330
|
/^[a-z0-9][a-z0-9-]*$/,
|
|
@@ -2814,20 +2367,20 @@ function validateToolDefs(defs, pluginName) {
|
|
|
2814
2367
|
const valid = [];
|
|
2815
2368
|
for (const def of defs) {
|
|
2816
2369
|
if (!def || typeof def !== "object") {
|
|
2817
|
-
|
|
2370
|
+
log5.warn(`[${pluginName}] tool is not an object, skipping`);
|
|
2818
2371
|
continue;
|
|
2819
2372
|
}
|
|
2820
2373
|
const t = def;
|
|
2821
2374
|
if (!t.name || typeof t.name !== "string") {
|
|
2822
|
-
|
|
2375
|
+
log5.warn(`[${pluginName}] tool missing 'name', skipping`);
|
|
2823
2376
|
continue;
|
|
2824
2377
|
}
|
|
2825
2378
|
if (!t.description || typeof t.description !== "string") {
|
|
2826
|
-
|
|
2379
|
+
log5.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
|
|
2827
2380
|
continue;
|
|
2828
2381
|
}
|
|
2829
2382
|
if (!t.execute || typeof t.execute !== "function") {
|
|
2830
|
-
|
|
2383
|
+
log5.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
|
|
2831
2384
|
continue;
|
|
2832
2385
|
}
|
|
2833
2386
|
valid.push(t);
|
|
@@ -2864,8 +2417,19 @@ import { Address, SendMode } from "@ton/core";
|
|
|
2864
2417
|
|
|
2865
2418
|
// src/ton/tx-lock.ts
|
|
2866
2419
|
var pending = Promise.resolve();
|
|
2420
|
+
var TX_LOCK_TIMEOUT_MS = 6e4;
|
|
2867
2421
|
function withTxLock(fn) {
|
|
2868
|
-
const
|
|
2422
|
+
const guarded = () => {
|
|
2423
|
+
let timerId;
|
|
2424
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
2425
|
+
timerId = setTimeout(
|
|
2426
|
+
() => reject(new Error("TON tx-lock timeout (60s)")),
|
|
2427
|
+
TX_LOCK_TIMEOUT_MS
|
|
2428
|
+
);
|
|
2429
|
+
});
|
|
2430
|
+
return Promise.race([fn(), timeoutPromise]).finally(() => clearTimeout(timerId));
|
|
2431
|
+
};
|
|
2432
|
+
const execute = pending.then(guarded, guarded);
|
|
2869
2433
|
pending = execute.then(
|
|
2870
2434
|
() => {
|
|
2871
2435
|
},
|
|
@@ -2876,25 +2440,25 @@ function withTxLock(fn) {
|
|
|
2876
2440
|
}
|
|
2877
2441
|
|
|
2878
2442
|
// src/ton/transfer.ts
|
|
2879
|
-
var
|
|
2443
|
+
var log6 = createLogger("TON");
|
|
2880
2444
|
async function sendTon(params) {
|
|
2881
2445
|
return withTxLock(async () => {
|
|
2882
2446
|
try {
|
|
2883
2447
|
const { toAddress: toAddress2, amount, comment = "", bounce = false } = params;
|
|
2884
2448
|
if (!Number.isFinite(amount) || amount <= 0) {
|
|
2885
|
-
|
|
2449
|
+
log6.error({ amount }, "Invalid transfer amount");
|
|
2886
2450
|
return null;
|
|
2887
2451
|
}
|
|
2888
2452
|
let recipientAddress;
|
|
2889
2453
|
try {
|
|
2890
2454
|
recipientAddress = Address.parse(toAddress2);
|
|
2891
2455
|
} catch (e) {
|
|
2892
|
-
|
|
2456
|
+
log6.error({ err: e }, `Invalid recipient address: ${toAddress2}`);
|
|
2893
2457
|
return null;
|
|
2894
2458
|
}
|
|
2895
2459
|
const keyPair = await getKeyPair();
|
|
2896
2460
|
if (!keyPair) {
|
|
2897
|
-
|
|
2461
|
+
log6.error("Wallet not initialized");
|
|
2898
2462
|
return null;
|
|
2899
2463
|
}
|
|
2900
2464
|
const wallet = WalletContractV5R1.create({
|
|
@@ -2918,7 +2482,7 @@ async function sendTon(params) {
|
|
|
2918
2482
|
]
|
|
2919
2483
|
});
|
|
2920
2484
|
const pseudoHash = `${seqno}_${Date.now()}_${amount.toFixed(2)}`;
|
|
2921
|
-
|
|
2485
|
+
log6.info(`Sent ${amount} TON to ${toAddress2.slice(0, 8)}... - seqno: ${seqno}`);
|
|
2922
2486
|
return pseudoHash;
|
|
2923
2487
|
} catch (error) {
|
|
2924
2488
|
const err = error;
|
|
@@ -2926,14 +2490,14 @@ async function sendTon(params) {
|
|
|
2926
2490
|
if (status === 429 || status !== void 0 && status >= 500) {
|
|
2927
2491
|
invalidateTonClientCache();
|
|
2928
2492
|
}
|
|
2929
|
-
|
|
2493
|
+
log6.error({ err: error }, "Error sending TON");
|
|
2930
2494
|
throw error;
|
|
2931
2495
|
}
|
|
2932
2496
|
});
|
|
2933
2497
|
}
|
|
2934
2498
|
|
|
2935
2499
|
// src/utils/retry.ts
|
|
2936
|
-
var
|
|
2500
|
+
var log7 = createLogger("Utils");
|
|
2937
2501
|
var DEFAULT_OPTIONS = {
|
|
2938
2502
|
maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
|
|
2939
2503
|
baseDelayMs: RETRY_DEFAULT_BASE_DELAY_MS,
|
|
@@ -2957,7 +2521,7 @@ async function withRetry(fn, options = {}) {
|
|
|
2957
2521
|
return result;
|
|
2958
2522
|
} catch (error) {
|
|
2959
2523
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2960
|
-
|
|
2524
|
+
log7.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
|
|
2961
2525
|
if (attempt < opts.maxAttempts) {
|
|
2962
2526
|
const delay = Math.min(opts.baseDelayMs * Math.pow(2, attempt - 1), opts.maxDelayMs);
|
|
2963
2527
|
await sleep(delay);
|
|
@@ -6435,7 +5999,7 @@ var DEDUST_GAS = {
|
|
|
6435
5999
|
var NATIVE_TON_ADDRESS = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
|
|
6436
6000
|
|
|
6437
6001
|
// src/agent/tools/dedust/asset-cache.ts
|
|
6438
|
-
var
|
|
6002
|
+
var log8 = createLogger("Tools");
|
|
6439
6003
|
var ASSET_LIST_URL = "https://assets.dedust.io/list.json";
|
|
6440
6004
|
var CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
6441
6005
|
var cachedAssets = [];
|
|
@@ -6454,7 +6018,7 @@ async function getAssetList() {
|
|
|
6454
6018
|
return cachedAssets;
|
|
6455
6019
|
} catch (error) {
|
|
6456
6020
|
if (cachedAssets.length > 0) {
|
|
6457
|
-
|
|
6021
|
+
log8.warn({ err: error }, "Asset list fetch failed, using stale cache");
|
|
6458
6022
|
return cachedAssets;
|
|
6459
6023
|
}
|
|
6460
6024
|
throw error;
|
|
@@ -6507,7 +6071,7 @@ var stonApiClient = new StonApiClient();
|
|
|
6507
6071
|
function isTon(asset) {
|
|
6508
6072
|
return asset.toLowerCase() === "ton";
|
|
6509
6073
|
}
|
|
6510
|
-
async function getStonfiQuote(fromAsset, toAsset, amount, slippage,
|
|
6074
|
+
async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log12) {
|
|
6511
6075
|
try {
|
|
6512
6076
|
const isTonInput = isTon(fromAsset);
|
|
6513
6077
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6538,11 +6102,11 @@ async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log15) {
|
|
|
6538
6102
|
fee: feeAmount.toFixed(6)
|
|
6539
6103
|
};
|
|
6540
6104
|
} catch (err) {
|
|
6541
|
-
|
|
6105
|
+
log12.debug("dex.quoteSTONfi() failed:", err);
|
|
6542
6106
|
return null;
|
|
6543
6107
|
}
|
|
6544
6108
|
}
|
|
6545
|
-
async function getDedustQuote(fromAsset, toAsset, amount, slippage,
|
|
6109
|
+
async function getDedustQuote(fromAsset, toAsset, amount, slippage, log12) {
|
|
6546
6110
|
try {
|
|
6547
6111
|
const isTonInput = isTon(fromAsset);
|
|
6548
6112
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6576,7 +6140,7 @@ async function getDedustQuote(fromAsset, toAsset, amount, slippage, log15) {
|
|
|
6576
6140
|
poolType
|
|
6577
6141
|
};
|
|
6578
6142
|
} catch (err) {
|
|
6579
|
-
|
|
6143
|
+
log12.debug("dex.quoteDeDust() failed:", err);
|
|
6580
6144
|
return null;
|
|
6581
6145
|
}
|
|
6582
6146
|
}
|
|
@@ -6748,14 +6312,14 @@ function validateDexParams(amount, slippage) {
|
|
|
6748
6312
|
throw new PluginSDKError("Slippage must be between 0 and 1", "OPERATION_FAILED");
|
|
6749
6313
|
}
|
|
6750
6314
|
}
|
|
6751
|
-
function createDexSDK(
|
|
6315
|
+
function createDexSDK(log12) {
|
|
6752
6316
|
return {
|
|
6753
6317
|
async quote(params) {
|
|
6754
6318
|
validateDexParams(params.amount, params.slippage);
|
|
6755
6319
|
const slippage = params.slippage ?? 0.01;
|
|
6756
6320
|
const [stonfi, dedust] = await Promise.all([
|
|
6757
|
-
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6758
|
-
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6321
|
+
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage, log12),
|
|
6322
|
+
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage, log12)
|
|
6759
6323
|
]);
|
|
6760
6324
|
if (!stonfi && !dedust) {
|
|
6761
6325
|
throw new PluginSDKError("No DEX has liquidity for this pair", "OPERATION_FAILED");
|
|
@@ -6789,7 +6353,7 @@ function createDexSDK(log15) {
|
|
|
6789
6353
|
params.toAsset,
|
|
6790
6354
|
params.amount,
|
|
6791
6355
|
params.slippage ?? 0.01,
|
|
6792
|
-
|
|
6356
|
+
log12
|
|
6793
6357
|
);
|
|
6794
6358
|
},
|
|
6795
6359
|
async quoteDeDust(params) {
|
|
@@ -6798,25 +6362,25 @@ function createDexSDK(log15) {
|
|
|
6798
6362
|
params.toAsset,
|
|
6799
6363
|
params.amount,
|
|
6800
6364
|
params.slippage ?? 0.01,
|
|
6801
|
-
|
|
6365
|
+
log12
|
|
6802
6366
|
);
|
|
6803
6367
|
},
|
|
6804
6368
|
async swap(params) {
|
|
6805
6369
|
validateDexParams(params.amount, params.slippage);
|
|
6806
6370
|
if (params.dex === "stonfi") {
|
|
6807
|
-
return executeSTONfiSwap(params,
|
|
6371
|
+
return executeSTONfiSwap(params, log12);
|
|
6808
6372
|
}
|
|
6809
6373
|
if (params.dex === "dedust") {
|
|
6810
|
-
return executeDedustSwap(params,
|
|
6374
|
+
return executeDedustSwap(params, log12);
|
|
6811
6375
|
}
|
|
6812
6376
|
const quoteResult = await this.quote(params);
|
|
6813
|
-
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params,
|
|
6377
|
+
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log12) : executeDedustSwap(params, log12);
|
|
6814
6378
|
},
|
|
6815
6379
|
async swapSTONfi(params) {
|
|
6816
|
-
return executeSTONfiSwap(params,
|
|
6380
|
+
return executeSTONfiSwap(params, log12);
|
|
6817
6381
|
},
|
|
6818
6382
|
async swapDeDust(params) {
|
|
6819
|
-
return executeDedustSwap(params,
|
|
6383
|
+
return executeDedustSwap(params, log12);
|
|
6820
6384
|
}
|
|
6821
6385
|
};
|
|
6822
6386
|
}
|
|
@@ -6853,7 +6417,7 @@ function normalizeDomain(domain) {
|
|
|
6853
6417
|
if (!d.endsWith(".ton")) d += ".ton";
|
|
6854
6418
|
return d;
|
|
6855
6419
|
}
|
|
6856
|
-
function createDnsSDK(
|
|
6420
|
+
function createDnsSDK(log12) {
|
|
6857
6421
|
return {
|
|
6858
6422
|
async check(domain) {
|
|
6859
6423
|
const normalized = normalizeDomain(domain);
|
|
@@ -6876,7 +6440,7 @@ function createDnsSDK(log15) {
|
|
|
6876
6440
|
};
|
|
6877
6441
|
} catch (err) {
|
|
6878
6442
|
if (err instanceof PluginSDKError) throw err;
|
|
6879
|
-
|
|
6443
|
+
log12.debug("dns.check() failed:", err);
|
|
6880
6444
|
throw new PluginSDKError(
|
|
6881
6445
|
`Failed to check domain: ${err instanceof Error ? err.message : String(err)}`,
|
|
6882
6446
|
"OPERATION_FAILED"
|
|
@@ -6889,7 +6453,7 @@ function createDnsSDK(log15) {
|
|
|
6889
6453
|
const response = await tonapiFetch(`/dns/${encodeURIComponent(normalized)}`);
|
|
6890
6454
|
if (response.status === 404) return null;
|
|
6891
6455
|
if (!response.ok) {
|
|
6892
|
-
|
|
6456
|
+
log12.debug(`dns.resolve() TonAPI error: ${response.status}`);
|
|
6893
6457
|
return null;
|
|
6894
6458
|
}
|
|
6895
6459
|
const data = await response.json();
|
|
@@ -6901,7 +6465,7 @@ function createDnsSDK(log15) {
|
|
|
6901
6465
|
expirationDate: data.expiring_at || void 0
|
|
6902
6466
|
};
|
|
6903
6467
|
} catch (err) {
|
|
6904
|
-
|
|
6468
|
+
log12.debug("dns.resolve() failed:", err);
|
|
6905
6469
|
return null;
|
|
6906
6470
|
}
|
|
6907
6471
|
},
|
|
@@ -6911,7 +6475,7 @@ function createDnsSDK(log15) {
|
|
|
6911
6475
|
`/dns/auctions?tld=ton&limit=${Math.min(limit ?? 20, 100)}`
|
|
6912
6476
|
);
|
|
6913
6477
|
if (!response.ok) {
|
|
6914
|
-
|
|
6478
|
+
log12.debug(`dns.getAuctions() TonAPI error: ${response.status}`);
|
|
6915
6479
|
return [];
|
|
6916
6480
|
}
|
|
6917
6481
|
const data = await response.json();
|
|
@@ -6924,7 +6488,7 @@ function createDnsSDK(log15) {
|
|
|
6924
6488
|
bids: a.bids || 0
|
|
6925
6489
|
}));
|
|
6926
6490
|
} catch (err) {
|
|
6927
|
-
|
|
6491
|
+
log12.debug("dns.getAuctions() failed:", err);
|
|
6928
6492
|
return [];
|
|
6929
6493
|
}
|
|
6930
6494
|
},
|
|
@@ -7238,25 +6802,25 @@ function findJettonBalance(balances, jettonAddress) {
|
|
|
7238
6802
|
}
|
|
7239
6803
|
});
|
|
7240
6804
|
}
|
|
7241
|
-
function cleanupOldTransactions(db, retentionDays,
|
|
6805
|
+
function cleanupOldTransactions(db, retentionDays, log12) {
|
|
7242
6806
|
if (Math.random() > CLEANUP_PROBABILITY) return;
|
|
7243
6807
|
try {
|
|
7244
6808
|
const cutoff = Math.floor(Date.now() / 1e3) - retentionDays * 24 * 60 * 60;
|
|
7245
6809
|
const result = db.prepare("DELETE FROM used_transactions WHERE used_at < ?").run(cutoff);
|
|
7246
6810
|
if (result.changes > 0) {
|
|
7247
|
-
|
|
6811
|
+
log12.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
|
|
7248
6812
|
}
|
|
7249
6813
|
} catch (err) {
|
|
7250
|
-
|
|
6814
|
+
log12.error("Transaction cleanup failed:", err);
|
|
7251
6815
|
}
|
|
7252
6816
|
}
|
|
7253
|
-
function createTonSDK(
|
|
6817
|
+
function createTonSDK(log12, db) {
|
|
7254
6818
|
return {
|
|
7255
6819
|
getAddress() {
|
|
7256
6820
|
try {
|
|
7257
6821
|
return getWalletAddress();
|
|
7258
6822
|
} catch (err) {
|
|
7259
|
-
|
|
6823
|
+
log12.error("ton.getAddress() failed:", err);
|
|
7260
6824
|
return null;
|
|
7261
6825
|
}
|
|
7262
6826
|
},
|
|
@@ -7266,7 +6830,7 @@ function createTonSDK(log15, db) {
|
|
|
7266
6830
|
if (!addr) return null;
|
|
7267
6831
|
return await getWalletBalance(addr);
|
|
7268
6832
|
} catch (err) {
|
|
7269
|
-
|
|
6833
|
+
log12.error("ton.getBalance() failed:", err);
|
|
7270
6834
|
return null;
|
|
7271
6835
|
}
|
|
7272
6836
|
},
|
|
@@ -7274,7 +6838,7 @@ function createTonSDK(log15, db) {
|
|
|
7274
6838
|
try {
|
|
7275
6839
|
return await getTonPrice();
|
|
7276
6840
|
} catch (err) {
|
|
7277
|
-
|
|
6841
|
+
log12.error("ton.getPrice() failed:", err);
|
|
7278
6842
|
return null;
|
|
7279
6843
|
}
|
|
7280
6844
|
},
|
|
@@ -7325,7 +6889,7 @@ function createTonSDK(log15, db) {
|
|
|
7325
6889
|
);
|
|
7326
6890
|
return formatTransactions(transactions);
|
|
7327
6891
|
} catch (err) {
|
|
7328
|
-
|
|
6892
|
+
log12.error("ton.getTransactions() failed:", err);
|
|
7329
6893
|
return [];
|
|
7330
6894
|
}
|
|
7331
6895
|
},
|
|
@@ -7348,7 +6912,7 @@ function createTonSDK(log15, db) {
|
|
|
7348
6912
|
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
7349
6913
|
}
|
|
7350
6914
|
const maxAgeMinutes = params.maxAgeMinutes ?? DEFAULT_MAX_AGE_MINUTES;
|
|
7351
|
-
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS,
|
|
6915
|
+
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log12);
|
|
7352
6916
|
try {
|
|
7353
6917
|
const txs = await this.getTransactions(address4, 20);
|
|
7354
6918
|
for (const tx of txs) {
|
|
@@ -7382,7 +6946,7 @@ function createTonSDK(log15, db) {
|
|
|
7382
6946
|
};
|
|
7383
6947
|
} catch (err) {
|
|
7384
6948
|
if (err instanceof PluginSDKError) throw err;
|
|
7385
|
-
|
|
6949
|
+
log12.error("ton.verifyPayment() failed:", err);
|
|
7386
6950
|
return {
|
|
7387
6951
|
verified: false,
|
|
7388
6952
|
error: `Verification failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -7396,7 +6960,7 @@ function createTonSDK(log15, db) {
|
|
|
7396
6960
|
if (!addr) return [];
|
|
7397
6961
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(addr)}/jettons`);
|
|
7398
6962
|
if (!response.ok) {
|
|
7399
|
-
|
|
6963
|
+
log12.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
|
|
7400
6964
|
return [];
|
|
7401
6965
|
}
|
|
7402
6966
|
const data = await response.json();
|
|
@@ -7420,7 +6984,7 @@ function createTonSDK(log15, db) {
|
|
|
7420
6984
|
}
|
|
7421
6985
|
return balances;
|
|
7422
6986
|
} catch (err) {
|
|
7423
|
-
|
|
6987
|
+
log12.error("ton.getJettonBalances() failed:", err);
|
|
7424
6988
|
return [];
|
|
7425
6989
|
}
|
|
7426
6990
|
},
|
|
@@ -7429,7 +6993,7 @@ function createTonSDK(log15, db) {
|
|
|
7429
6993
|
const response = await tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`);
|
|
7430
6994
|
if (response.status === 404) return null;
|
|
7431
6995
|
if (!response.ok) {
|
|
7432
|
-
|
|
6996
|
+
log12.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
|
|
7433
6997
|
return null;
|
|
7434
6998
|
}
|
|
7435
6999
|
const data = await response.json();
|
|
@@ -7447,7 +7011,7 @@ function createTonSDK(log15, db) {
|
|
|
7447
7011
|
image: data.preview || metadata.image || void 0
|
|
7448
7012
|
};
|
|
7449
7013
|
} catch (err) {
|
|
7450
|
-
|
|
7014
|
+
log12.error("ton.getJettonInfo() failed:", err);
|
|
7451
7015
|
return null;
|
|
7452
7016
|
}
|
|
7453
7017
|
},
|
|
@@ -7534,12 +7098,13 @@ function createTonSDK(log15, db) {
|
|
|
7534
7098
|
return seq;
|
|
7535
7099
|
} catch (err) {
|
|
7536
7100
|
lastErr = err;
|
|
7537
|
-
const
|
|
7538
|
-
const
|
|
7101
|
+
const httpErr = isHttpError(err) ? err : void 0;
|
|
7102
|
+
const status = httpErr?.status || httpErr?.response?.status;
|
|
7103
|
+
const respData = httpErr?.response?.data;
|
|
7539
7104
|
if (status === 429 || status && status >= 500) {
|
|
7540
7105
|
invalidateTonClientCache();
|
|
7541
7106
|
if (attempt < MAX_SEND_ATTEMPTS) {
|
|
7542
|
-
|
|
7107
|
+
log12.warn(
|
|
7543
7108
|
`sendJetton attempt ${attempt} failed (${status}): ${JSON.stringify(respData ?? err.message)}, retrying...`
|
|
7544
7109
|
);
|
|
7545
7110
|
await new Promise((r3) => setTimeout(r3, 1e3 * attempt));
|
|
@@ -7553,7 +7118,8 @@ function createTonSDK(log15, db) {
|
|
|
7553
7118
|
});
|
|
7554
7119
|
return { success: true, seqno };
|
|
7555
7120
|
} catch (err) {
|
|
7556
|
-
const
|
|
7121
|
+
const outerHttpErr = isHttpError(err) ? err : void 0;
|
|
7122
|
+
const status = outerHttpErr?.status || outerHttpErr?.response?.status;
|
|
7557
7123
|
if (status === 429 || status && status >= 500) {
|
|
7558
7124
|
invalidateTonClientCache();
|
|
7559
7125
|
}
|
|
@@ -7568,14 +7134,14 @@ function createTonSDK(log15, db) {
|
|
|
7568
7134
|
try {
|
|
7569
7135
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(ownerAddress)}/jettons`);
|
|
7570
7136
|
if (!response.ok) {
|
|
7571
|
-
|
|
7137
|
+
log12.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
|
|
7572
7138
|
return null;
|
|
7573
7139
|
}
|
|
7574
7140
|
const data = await response.json();
|
|
7575
7141
|
const match = findJettonBalance(data.balances ?? [], jettonAddress);
|
|
7576
7142
|
return match ? match.wallet_address.address : null;
|
|
7577
7143
|
} catch (err) {
|
|
7578
|
-
|
|
7144
|
+
log12.error("ton.getJettonWalletAddress() failed:", err);
|
|
7579
7145
|
return null;
|
|
7580
7146
|
}
|
|
7581
7147
|
},
|
|
@@ -7752,7 +7318,7 @@ function createTonSDK(log15, db) {
|
|
|
7752
7318
|
const wallet = loadWallet();
|
|
7753
7319
|
return wallet?.publicKey ?? null;
|
|
7754
7320
|
} catch (err) {
|
|
7755
|
-
|
|
7321
|
+
log12.error("ton.getPublicKey() failed:", err);
|
|
7756
7322
|
return null;
|
|
7757
7323
|
}
|
|
7758
7324
|
},
|
|
@@ -7768,14 +7334,14 @@ function createTonSDK(log15, db) {
|
|
|
7768
7334
|
`/accounts/${encodeURIComponent(addr)}/nfts?limit=100&indirect_ownership=true`
|
|
7769
7335
|
);
|
|
7770
7336
|
if (!response.ok) {
|
|
7771
|
-
|
|
7337
|
+
log12.error(`ton.getNftItems() TonAPI error: ${response.status}`);
|
|
7772
7338
|
return [];
|
|
7773
7339
|
}
|
|
7774
7340
|
const data = await response.json();
|
|
7775
7341
|
if (!Array.isArray(data.nft_items)) return [];
|
|
7776
7342
|
return data.nft_items.filter((item) => item.trust !== "blacklist").map((item) => mapNftItem(item));
|
|
7777
7343
|
} catch (err) {
|
|
7778
|
-
|
|
7344
|
+
log12.error("ton.getNftItems() failed:", err);
|
|
7779
7345
|
return [];
|
|
7780
7346
|
}
|
|
7781
7347
|
},
|
|
@@ -7784,13 +7350,13 @@ function createTonSDK(log15, db) {
|
|
|
7784
7350
|
const response = await tonapiFetch(`/nfts/${encodeURIComponent(nftAddress)}`);
|
|
7785
7351
|
if (response.status === 404) return null;
|
|
7786
7352
|
if (!response.ok) {
|
|
7787
|
-
|
|
7353
|
+
log12.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
|
|
7788
7354
|
return null;
|
|
7789
7355
|
}
|
|
7790
7356
|
const item = await response.json();
|
|
7791
7357
|
return mapNftItem(item);
|
|
7792
7358
|
} catch (err) {
|
|
7793
|
-
|
|
7359
|
+
log12.error("ton.getNftInfo() failed:", err);
|
|
7794
7360
|
return null;
|
|
7795
7361
|
}
|
|
7796
7362
|
},
|
|
@@ -7823,7 +7389,7 @@ function createTonSDK(log15, db) {
|
|
|
7823
7389
|
`/rates?tokens=${encodeURIComponent(jettonAddress)}¤cies=usd,ton`
|
|
7824
7390
|
);
|
|
7825
7391
|
if (!response.ok) {
|
|
7826
|
-
|
|
7392
|
+
log12.debug(`ton.getJettonPrice() TonAPI error: ${response.status}`);
|
|
7827
7393
|
return null;
|
|
7828
7394
|
}
|
|
7829
7395
|
const data = await response.json();
|
|
@@ -7837,7 +7403,7 @@ function createTonSDK(log15, db) {
|
|
|
7837
7403
|
change30d: rateData.diff_30d?.USD ?? null
|
|
7838
7404
|
};
|
|
7839
7405
|
} catch (err) {
|
|
7840
|
-
|
|
7406
|
+
log12.debug("ton.getJettonPrice() failed:", err);
|
|
7841
7407
|
return null;
|
|
7842
7408
|
}
|
|
7843
7409
|
},
|
|
@@ -7851,7 +7417,7 @@ function createTonSDK(log15, db) {
|
|
|
7851
7417
|
tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`)
|
|
7852
7418
|
]);
|
|
7853
7419
|
if (!holdersResponse.ok) {
|
|
7854
|
-
|
|
7420
|
+
log12.debug(`ton.getJettonHolders() TonAPI error: ${holdersResponse.status}`);
|
|
7855
7421
|
return [];
|
|
7856
7422
|
}
|
|
7857
7423
|
const data = await holdersResponse.json();
|
|
@@ -7871,7 +7437,7 @@ function createTonSDK(log15, db) {
|
|
|
7871
7437
|
};
|
|
7872
7438
|
});
|
|
7873
7439
|
} catch (err) {
|
|
7874
|
-
|
|
7440
|
+
log12.debug("ton.getJettonHolders() failed:", err);
|
|
7875
7441
|
return [];
|
|
7876
7442
|
}
|
|
7877
7443
|
},
|
|
@@ -7943,13 +7509,13 @@ function createTonSDK(log15, db) {
|
|
|
7943
7509
|
holders: holdersCount
|
|
7944
7510
|
};
|
|
7945
7511
|
} catch (err) {
|
|
7946
|
-
|
|
7512
|
+
log12.debug("ton.getJettonHistory() failed:", err);
|
|
7947
7513
|
return null;
|
|
7948
7514
|
}
|
|
7949
7515
|
},
|
|
7950
7516
|
// ─── Sub-namespaces ───────────────────────────────────────────
|
|
7951
|
-
dex: Object.freeze(createDexSDK(
|
|
7952
|
-
dns: Object.freeze(createDnsSDK(
|
|
7517
|
+
dex: Object.freeze(createDexSDK(log12)),
|
|
7518
|
+
dns: Object.freeze(createDnsSDK(log12))
|
|
7953
7519
|
};
|
|
7954
7520
|
}
|
|
7955
7521
|
function mapNftItem(item) {
|
|
@@ -7973,15 +7539,6 @@ function mapNftItem(item) {
|
|
|
7973
7539
|
// src/sdk/telegram.ts
|
|
7974
7540
|
import { Api as Api3 } from "telegram";
|
|
7975
7541
|
|
|
7976
|
-
// src/utils/gramjs-bigint.ts
|
|
7977
|
-
import { randomBytes } from "crypto";
|
|
7978
|
-
function toLong(value) {
|
|
7979
|
-
return typeof value === "bigint" ? value : BigInt(value);
|
|
7980
|
-
}
|
|
7981
|
-
function randomLong() {
|
|
7982
|
-
return randomBytes(8).readBigUInt64BE();
|
|
7983
|
-
}
|
|
7984
|
-
|
|
7985
7542
|
// src/sdk/telegram-utils.ts
|
|
7986
7543
|
function requireBridge(bridge) {
|
|
7987
7544
|
if (!bridge.isAvailable()) {
|
|
@@ -7998,7 +7555,6 @@ function toSimpleMessage(msg) {
|
|
|
7998
7555
|
return {
|
|
7999
7556
|
id: msg.id,
|
|
8000
7557
|
text: msg.message ?? "",
|
|
8001
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- GramJS fromId is a union of untyped peer types
|
|
8002
7558
|
senderId: Number(msg.fromId?.userId ?? msg.fromId?.channelId ?? 0),
|
|
8003
7559
|
timestamp: new Date(msg.date * 1e3)
|
|
8004
7560
|
};
|
|
@@ -8012,7 +7568,7 @@ async function getApi() {
|
|
|
8012
7568
|
}
|
|
8013
7569
|
|
|
8014
7570
|
// src/sdk/telegram-messages.ts
|
|
8015
|
-
function createTelegramMessagesSDK(bridge,
|
|
7571
|
+
function createTelegramMessagesSDK(bridge, log12) {
|
|
8016
7572
|
function requireBridge2() {
|
|
8017
7573
|
requireBridge(bridge);
|
|
8018
7574
|
}
|
|
@@ -8119,7 +7675,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8119
7675
|
return (resultData.messages ?? []).map(toSimpleMessage);
|
|
8120
7676
|
} catch (err) {
|
|
8121
7677
|
if (err instanceof PluginSDKError) throw err;
|
|
8122
|
-
|
|
7678
|
+
log12.error("telegram.searchMessages() failed:", err);
|
|
8123
7679
|
return [];
|
|
8124
7680
|
}
|
|
8125
7681
|
},
|
|
@@ -8156,8 +7712,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8156
7712
|
limit,
|
|
8157
7713
|
maxId: 0,
|
|
8158
7714
|
minId: 0,
|
|
8159
|
-
|
|
8160
|
-
hash: 0n
|
|
7715
|
+
hash: toLong(0n)
|
|
8161
7716
|
})
|
|
8162
7717
|
);
|
|
8163
7718
|
const messages = [];
|
|
@@ -8320,10 +7875,12 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8320
7875
|
if (!messages || messages.length === 0 || !messages[0].media) {
|
|
8321
7876
|
return null;
|
|
8322
7877
|
}
|
|
8323
|
-
const
|
|
8324
|
-
|
|
7878
|
+
const media = messages[0].media;
|
|
7879
|
+
const doc = media && "document" in media ? media.document : void 0;
|
|
7880
|
+
const docSize = doc && "size" in doc ? doc.size : void 0;
|
|
7881
|
+
if (docSize && Number(docSize) > MAX_DOWNLOAD_SIZE) {
|
|
8325
7882
|
throw new PluginSDKError(
|
|
8326
|
-
`File too large (${Math.round(Number(
|
|
7883
|
+
`File too large (${Math.round(Number(docSize) / 1024 / 1024)}MB). Max: 50MB`,
|
|
8327
7884
|
"OPERATION_FAILED"
|
|
8328
7885
|
);
|
|
8329
7886
|
}
|
|
@@ -8347,8 +7904,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8347
7904
|
const result = await gramJsClient.invoke(
|
|
8348
7905
|
new Api4.messages.GetScheduledHistory({
|
|
8349
7906
|
peer,
|
|
8350
|
-
|
|
8351
|
-
hash: 0n
|
|
7907
|
+
hash: toLong(0n)
|
|
8352
7908
|
})
|
|
8353
7909
|
);
|
|
8354
7910
|
const messages = [];
|
|
@@ -8362,7 +7918,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8362
7918
|
return messages;
|
|
8363
7919
|
} catch (err) {
|
|
8364
7920
|
if (err instanceof PluginSDKError) throw err;
|
|
8365
|
-
|
|
7921
|
+
log12.error("telegram.getScheduledMessages() failed:", err);
|
|
8366
7922
|
return [];
|
|
8367
7923
|
}
|
|
8368
7924
|
},
|
|
@@ -8423,7 +7979,7 @@ function createTelegramMessagesSDK(bridge, log15) {
|
|
|
8423
7979
|
}
|
|
8424
7980
|
|
|
8425
7981
|
// src/sdk/telegram-social.ts
|
|
8426
|
-
function createTelegramSocialSDK(bridge,
|
|
7982
|
+
function createTelegramSocialSDK(bridge, log12) {
|
|
8427
7983
|
function requireBridge2() {
|
|
8428
7984
|
requireBridge(bridge);
|
|
8429
7985
|
}
|
|
@@ -8498,7 +8054,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8498
8054
|
return null;
|
|
8499
8055
|
} catch (err) {
|
|
8500
8056
|
if (err instanceof PluginSDKError) throw err;
|
|
8501
|
-
|
|
8057
|
+
log12.error("telegram.getChatInfo() failed:", err);
|
|
8502
8058
|
return null;
|
|
8503
8059
|
}
|
|
8504
8060
|
},
|
|
@@ -8612,7 +8168,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8612
8168
|
});
|
|
8613
8169
|
} catch (err) {
|
|
8614
8170
|
if (err instanceof PluginSDKError) throw err;
|
|
8615
|
-
|
|
8171
|
+
log12.error("telegram.getParticipants() failed:", err);
|
|
8616
8172
|
return [];
|
|
8617
8173
|
}
|
|
8618
8174
|
},
|
|
@@ -8829,7 +8385,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8829
8385
|
const user = await client.getInputEntity(userId.toString());
|
|
8830
8386
|
const invoiceData = {
|
|
8831
8387
|
peer: user,
|
|
8832
|
-
giftId:
|
|
8388
|
+
giftId: toLong(giftId),
|
|
8833
8389
|
hideName: opts?.anonymous ?? false,
|
|
8834
8390
|
message: opts?.message ? new Api4.TextWithEntities({ text: opts.message, entities: [] }) : void 0
|
|
8835
8391
|
};
|
|
@@ -8913,7 +8469,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8913
8469
|
const Api4 = await getApi();
|
|
8914
8470
|
const result = await client.invoke(
|
|
8915
8471
|
new Api4.payments.GetResaleStarGifts({
|
|
8916
|
-
giftId:
|
|
8472
|
+
giftId: toLong(giftId),
|
|
8917
8473
|
offset: "",
|
|
8918
8474
|
limit: limit ?? 50
|
|
8919
8475
|
})
|
|
@@ -8974,7 +8530,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8974
8530
|
}));
|
|
8975
8531
|
} catch (err) {
|
|
8976
8532
|
if (err instanceof PluginSDKError) throw err;
|
|
8977
|
-
|
|
8533
|
+
log12.error("telegram.getDialogs() failed:", err);
|
|
8978
8534
|
return [];
|
|
8979
8535
|
}
|
|
8980
8536
|
},
|
|
@@ -8988,7 +8544,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
8988
8544
|
return messages.map(toSimpleMessage);
|
|
8989
8545
|
} catch (err) {
|
|
8990
8546
|
if (err instanceof PluginSDKError) throw err;
|
|
8991
|
-
|
|
8547
|
+
log12.error("telegram.getHistory() failed:", err);
|
|
8992
8548
|
return [];
|
|
8993
8549
|
}
|
|
8994
8550
|
},
|
|
@@ -9019,7 +8575,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9019
8575
|
}));
|
|
9020
8576
|
} catch (err) {
|
|
9021
8577
|
if (err instanceof PluginSDKError) throw err;
|
|
9022
|
-
|
|
8578
|
+
log12.error("telegram.getStarsTransactions() failed:", err);
|
|
9023
8579
|
return [];
|
|
9024
8580
|
}
|
|
9025
8581
|
},
|
|
@@ -9072,7 +8628,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9072
8628
|
new Api4.payments.UpdateStarGiftPrice({
|
|
9073
8629
|
stargift: new Api4.InputSavedStarGiftUser({ msgId }),
|
|
9074
8630
|
resellAmount: new Api4.StarsAmount({
|
|
9075
|
-
amount:
|
|
8631
|
+
amount: toLong(price),
|
|
9076
8632
|
nanos: 0
|
|
9077
8633
|
})
|
|
9078
8634
|
})
|
|
@@ -9124,7 +8680,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9124
8680
|
};
|
|
9125
8681
|
} catch (err) {
|
|
9126
8682
|
if (err instanceof PluginSDKError) throw err;
|
|
9127
|
-
|
|
8683
|
+
log12.error("telegram.getCollectibleInfo() failed:", err);
|
|
9128
8684
|
return null;
|
|
9129
8685
|
}
|
|
9130
8686
|
},
|
|
@@ -9162,7 +8718,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9162
8718
|
} catch (err) {
|
|
9163
8719
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
9164
8720
|
if (err instanceof PluginSDKError) throw err;
|
|
9165
|
-
|
|
8721
|
+
log12.error("telegram.getUniqueGift() failed:", err);
|
|
9166
8722
|
return null;
|
|
9167
8723
|
}
|
|
9168
8724
|
},
|
|
@@ -9188,7 +8744,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9188
8744
|
} catch (err) {
|
|
9189
8745
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
9190
8746
|
if (err instanceof PluginSDKError) throw err;
|
|
9191
|
-
|
|
8747
|
+
log12.error("telegram.getUniqueGiftValue() failed:", err);
|
|
9192
8748
|
return null;
|
|
9193
8749
|
}
|
|
9194
8750
|
},
|
|
@@ -9203,7 +8759,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9203
8759
|
new Api4.payments.SendStarGiftOffer({
|
|
9204
8760
|
peer,
|
|
9205
8761
|
slug: giftSlug,
|
|
9206
|
-
price: new Api4.StarsAmount({ amount:
|
|
8762
|
+
price: new Api4.StarsAmount({ amount: toLong(price), nanos: 0 }),
|
|
9207
8763
|
duration,
|
|
9208
8764
|
randomId: randomLong()
|
|
9209
8765
|
})
|
|
@@ -9223,13 +8779,13 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9223
8779
|
const client = getClient2();
|
|
9224
8780
|
const { Api: Api4, helpers } = await import("telegram");
|
|
9225
8781
|
const { CustomFile } = await import("telegram/client/uploads.js");
|
|
9226
|
-
const { readFileSync:
|
|
8782
|
+
const { readFileSync: readFileSync7, statSync: statSync2 } = await import("fs");
|
|
9227
8783
|
const { basename: basename2 } = await import("path");
|
|
9228
8784
|
const { resolve: resolve2, normalize: normalize2 } = await import("path");
|
|
9229
|
-
const { homedir:
|
|
8785
|
+
const { homedir: homedir2 } = await import("os");
|
|
9230
8786
|
const { realpathSync } = await import("fs");
|
|
9231
8787
|
const filePath = realpathSync(resolve2(normalize2(mediaPath)));
|
|
9232
|
-
const home =
|
|
8788
|
+
const home = homedir2();
|
|
9233
8789
|
const teletonWorkspace = `${home}/.teleton/workspace/`;
|
|
9234
8790
|
const allowedPrefixes = [
|
|
9235
8791
|
"/tmp/",
|
|
@@ -9248,7 +8804,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9248
8804
|
}
|
|
9249
8805
|
const fileName = basename2(filePath);
|
|
9250
8806
|
const fileSize = statSync2(filePath).size;
|
|
9251
|
-
const fileBuffer =
|
|
8807
|
+
const fileBuffer = readFileSync7(filePath);
|
|
9252
8808
|
const isVideo = filePath.toLowerCase().match(/\.(mp4|mov|avi|webm|mkv|m4v)$/);
|
|
9253
8809
|
const customFile = new CustomFile(fileName, fileSize, filePath, fileBuffer);
|
|
9254
8810
|
const uploadedFile = await client.uploadFile({
|
|
@@ -9299,7 +8855,7 @@ function createTelegramSocialSDK(bridge, log15) {
|
|
|
9299
8855
|
}
|
|
9300
8856
|
|
|
9301
8857
|
// src/sdk/telegram.ts
|
|
9302
|
-
function createTelegramSDK(bridge,
|
|
8858
|
+
function createTelegramSDK(bridge, log12) {
|
|
9303
8859
|
function requireBridge2() {
|
|
9304
8860
|
requireBridge(bridge);
|
|
9305
8861
|
}
|
|
@@ -9403,7 +8959,7 @@ function createTelegramSDK(bridge, log15) {
|
|
|
9403
8959
|
timestamp: m.timestamp
|
|
9404
8960
|
}));
|
|
9405
8961
|
} catch (err) {
|
|
9406
|
-
|
|
8962
|
+
log12.error("telegram.getMessages() failed:", err);
|
|
9407
8963
|
return [];
|
|
9408
8964
|
}
|
|
9409
8965
|
},
|
|
@@ -9425,7 +8981,7 @@ function createTelegramSDK(bridge, log15) {
|
|
|
9425
8981
|
return bridge.isAvailable();
|
|
9426
8982
|
},
|
|
9427
8983
|
getRawClient() {
|
|
9428
|
-
|
|
8984
|
+
log12.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
|
|
9429
8985
|
if (!bridge.isAvailable()) return null;
|
|
9430
8986
|
try {
|
|
9431
8987
|
return bridge.getClient().getClient();
|
|
@@ -9434,23 +8990,23 @@ function createTelegramSDK(bridge, log15) {
|
|
|
9434
8990
|
}
|
|
9435
8991
|
},
|
|
9436
8992
|
// Spread extended methods from sub-modules
|
|
9437
|
-
...createTelegramMessagesSDK(bridge,
|
|
9438
|
-
...createTelegramSocialSDK(bridge,
|
|
8993
|
+
...createTelegramMessagesSDK(bridge, log12),
|
|
8994
|
+
...createTelegramSocialSDK(bridge, log12)
|
|
9439
8995
|
};
|
|
9440
8996
|
}
|
|
9441
8997
|
|
|
9442
8998
|
// src/sdk/secrets.ts
|
|
9443
|
-
import { readFileSync as
|
|
9444
|
-
import { join as
|
|
9445
|
-
var SECRETS_DIR =
|
|
8999
|
+
import { readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
9000
|
+
import { join as join2 } from "path";
|
|
9001
|
+
var SECRETS_DIR = join2(TELETON_ROOT, "plugins", "data");
|
|
9446
9002
|
function getSecretsPath(pluginName) {
|
|
9447
|
-
return
|
|
9003
|
+
return join2(SECRETS_DIR, `${pluginName}.secrets.json`);
|
|
9448
9004
|
}
|
|
9449
9005
|
function readSecretsFile(pluginName) {
|
|
9450
9006
|
const filePath = getSecretsPath(pluginName);
|
|
9451
9007
|
try {
|
|
9452
|
-
if (!
|
|
9453
|
-
const raw =
|
|
9008
|
+
if (!existsSync4(filePath)) return {};
|
|
9009
|
+
const raw = readFileSync3(filePath, "utf-8");
|
|
9454
9010
|
const parsed = JSON.parse(raw);
|
|
9455
9011
|
if (typeof parsed !== "object" || parsed === null) return {};
|
|
9456
9012
|
return parsed;
|
|
@@ -9459,40 +9015,40 @@ function readSecretsFile(pluginName) {
|
|
|
9459
9015
|
}
|
|
9460
9016
|
}
|
|
9461
9017
|
function writePluginSecret(pluginName, key, value) {
|
|
9462
|
-
|
|
9018
|
+
mkdirSync2(SECRETS_DIR, { recursive: true, mode: 448 });
|
|
9463
9019
|
const filePath = getSecretsPath(pluginName);
|
|
9464
9020
|
const existing = readSecretsFile(pluginName);
|
|
9465
9021
|
existing[key] = value;
|
|
9466
|
-
|
|
9022
|
+
writeFileSync(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
9467
9023
|
}
|
|
9468
9024
|
function deletePluginSecret(pluginName, key) {
|
|
9469
9025
|
const existing = readSecretsFile(pluginName);
|
|
9470
9026
|
if (!(key in existing)) return false;
|
|
9471
9027
|
delete existing[key];
|
|
9472
9028
|
const filePath = getSecretsPath(pluginName);
|
|
9473
|
-
|
|
9029
|
+
writeFileSync(filePath, JSON.stringify(existing, null, 2), { mode: 384 });
|
|
9474
9030
|
return true;
|
|
9475
9031
|
}
|
|
9476
9032
|
function listPluginSecretKeys(pluginName) {
|
|
9477
9033
|
return Object.keys(readSecretsFile(pluginName));
|
|
9478
9034
|
}
|
|
9479
|
-
function createSecretsSDK(pluginName, pluginConfig,
|
|
9035
|
+
function createSecretsSDK(pluginName, pluginConfig, log12) {
|
|
9480
9036
|
const envPrefix = pluginName.replace(/-/g, "_").toUpperCase();
|
|
9481
9037
|
function get(key) {
|
|
9482
9038
|
const envKey = `${envPrefix}_${key.toUpperCase()}`;
|
|
9483
9039
|
const envValue = process.env[envKey];
|
|
9484
9040
|
if (envValue) {
|
|
9485
|
-
|
|
9041
|
+
log12.debug(`Secret "${key}" resolved from env var ${envKey}`);
|
|
9486
9042
|
return envValue;
|
|
9487
9043
|
}
|
|
9488
9044
|
const stored = readSecretsFile(pluginName);
|
|
9489
9045
|
if (key in stored && stored[key]) {
|
|
9490
|
-
|
|
9046
|
+
log12.debug(`Secret "${key}" resolved from secrets store`);
|
|
9491
9047
|
return stored[key];
|
|
9492
9048
|
}
|
|
9493
9049
|
const configValue = pluginConfig[key];
|
|
9494
9050
|
if (configValue !== void 0 && configValue !== null) {
|
|
9495
|
-
|
|
9051
|
+
log12.debug(`Secret "${key}" resolved from pluginConfig`);
|
|
9496
9052
|
return String(configValue);
|
|
9497
9053
|
}
|
|
9498
9054
|
return void 0;
|
|
@@ -9594,7 +9150,7 @@ function createStorageSDK(db) {
|
|
|
9594
9150
|
}
|
|
9595
9151
|
|
|
9596
9152
|
// src/sdk/bot.ts
|
|
9597
|
-
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter,
|
|
9153
|
+
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter, log12) {
|
|
9598
9154
|
if (!router || !manifest || !manifest.inline && !manifest.callbacks) {
|
|
9599
9155
|
return null;
|
|
9600
9156
|
}
|
|
@@ -9617,7 +9173,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9617
9173
|
},
|
|
9618
9174
|
onInlineQuery(handler) {
|
|
9619
9175
|
if (handlers.onInlineQuery) {
|
|
9620
|
-
|
|
9176
|
+
log12.warn("onInlineQuery called again \u2014 overwriting previous handler");
|
|
9621
9177
|
}
|
|
9622
9178
|
handlers.onInlineQuery = async (ctx) => {
|
|
9623
9179
|
if (rateLimiter) {
|
|
@@ -9663,7 +9219,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9663
9219
|
return;
|
|
9664
9220
|
} catch (error) {
|
|
9665
9221
|
if (error?.errorMessage === "MESSAGE_NOT_MODIFIED") return;
|
|
9666
|
-
|
|
9222
|
+
log12.warn(`GramJS edit failed, falling back to Grammy: ${error?.errorMessage || error}`);
|
|
9667
9223
|
}
|
|
9668
9224
|
}
|
|
9669
9225
|
if (grammyBot) {
|
|
@@ -9676,7 +9232,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9676
9232
|
});
|
|
9677
9233
|
} catch (error) {
|
|
9678
9234
|
if (error?.description?.includes("message is not modified")) return;
|
|
9679
|
-
|
|
9235
|
+
log12.error(`Failed to edit inline message: ${error?.description || error}`);
|
|
9680
9236
|
}
|
|
9681
9237
|
}
|
|
9682
9238
|
},
|
|
@@ -9735,13 +9291,13 @@ function createSafeDb(db) {
|
|
|
9735
9291
|
});
|
|
9736
9292
|
}
|
|
9737
9293
|
function createPluginSDK(deps, opts) {
|
|
9738
|
-
const
|
|
9294
|
+
const log12 = createLogger2(opts.pluginName);
|
|
9739
9295
|
const safeDb = opts.db ? createSafeDb(opts.db) : null;
|
|
9740
|
-
const ton = Object.freeze(createTonSDK(
|
|
9741
|
-
const telegram = Object.freeze(createTelegramSDK(deps.bridge,
|
|
9742
|
-
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig,
|
|
9296
|
+
const ton = Object.freeze(createTonSDK(log12, safeDb));
|
|
9297
|
+
const telegram = Object.freeze(createTelegramSDK(deps.bridge, log12));
|
|
9298
|
+
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log12));
|
|
9743
9299
|
const storage = safeDb ? Object.freeze(createStorageSDK(safeDb)) : null;
|
|
9744
|
-
const frozenLog = Object.freeze(
|
|
9300
|
+
const frozenLog = Object.freeze(log12);
|
|
9745
9301
|
const frozenConfig = Object.freeze(JSON.parse(JSON.stringify(opts.sanitizedConfig ?? {})));
|
|
9746
9302
|
const frozenPluginConfig = Object.freeze(JSON.parse(JSON.stringify(opts.pluginConfig ?? {})));
|
|
9747
9303
|
let cachedBot;
|
|
@@ -9772,20 +9328,20 @@ function createPluginSDK(deps, opts) {
|
|
|
9772
9328
|
},
|
|
9773
9329
|
on(hookName, handler, onOpts) {
|
|
9774
9330
|
if (!opts.hookRegistry) {
|
|
9775
|
-
|
|
9331
|
+
log12.warn(`Hook registration unavailable \u2014 sdk.on() ignored`);
|
|
9776
9332
|
return;
|
|
9777
9333
|
}
|
|
9778
9334
|
if (opts.declaredHooks) {
|
|
9779
9335
|
const declared = opts.declaredHooks.some((h2) => h2.name === hookName);
|
|
9780
9336
|
if (!declared) {
|
|
9781
|
-
|
|
9337
|
+
log12.warn(`Hook "${hookName}" not declared in manifest \u2014 registration rejected`);
|
|
9782
9338
|
return;
|
|
9783
9339
|
}
|
|
9784
9340
|
}
|
|
9785
9341
|
const rawPriority = Number(onOpts?.priority) || 0;
|
|
9786
9342
|
const clampedPriority = Math.max(-1e3, Math.min(1e3, rawPriority));
|
|
9787
9343
|
if (rawPriority !== clampedPriority) {
|
|
9788
|
-
|
|
9344
|
+
log12.debug(`Hook "${hookName}" priority ${rawPriority} clamped to ${clampedPriority}`);
|
|
9789
9345
|
}
|
|
9790
9346
|
const registered = opts.hookRegistry.register({
|
|
9791
9347
|
pluginId: opts.pluginName,
|
|
@@ -9795,7 +9351,7 @@ function createPluginSDK(deps, opts) {
|
|
|
9795
9351
|
globalPriority: opts.globalPriority ?? 0
|
|
9796
9352
|
});
|
|
9797
9353
|
if (!registered) {
|
|
9798
|
-
|
|
9354
|
+
log12.warn(
|
|
9799
9355
|
`Hook registration limit reached for plugin "${opts.pluginName}" \u2014 "${hookName}" rejected`
|
|
9800
9356
|
);
|
|
9801
9357
|
}
|
|
@@ -9914,24 +9470,24 @@ var HookRegistry = class {
|
|
|
9914
9470
|
|
|
9915
9471
|
// src/agent/tools/plugin-loader.ts
|
|
9916
9472
|
var execFileAsync = promisify(execFile);
|
|
9917
|
-
var
|
|
9918
|
-
var PLUGIN_DATA_DIR =
|
|
9473
|
+
var log9 = createLogger("PluginLoader");
|
|
9474
|
+
var PLUGIN_DATA_DIR = join3(TELETON_ROOT, "plugins", "data");
|
|
9919
9475
|
function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookRegistry, pluginPriorities) {
|
|
9920
9476
|
let manifest = null;
|
|
9921
9477
|
if (raw.manifest) {
|
|
9922
9478
|
try {
|
|
9923
9479
|
manifest = validateManifest(raw.manifest);
|
|
9924
9480
|
} catch (err) {
|
|
9925
|
-
|
|
9481
|
+
log9.warn(
|
|
9926
9482
|
`[${entryName}] invalid manifest, ignoring: ${err instanceof Error ? err.message : err}`
|
|
9927
9483
|
);
|
|
9928
9484
|
}
|
|
9929
9485
|
}
|
|
9930
9486
|
if (!manifest) {
|
|
9931
|
-
const manifestPath =
|
|
9487
|
+
const manifestPath = join3(WORKSPACE_PATHS.PLUGINS_DIR, entryName, "manifest.json");
|
|
9932
9488
|
try {
|
|
9933
|
-
if (
|
|
9934
|
-
const diskManifest = JSON.parse(
|
|
9489
|
+
if (existsSync5(manifestPath)) {
|
|
9490
|
+
const diskManifest = JSON.parse(readFileSync4(manifestPath, "utf-8"));
|
|
9935
9491
|
if (diskManifest && typeof diskManifest.version === "string") {
|
|
9936
9492
|
manifest = {
|
|
9937
9493
|
name: entryName,
|
|
@@ -10006,7 +9562,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookReg
|
|
|
10006
9562
|
},
|
|
10007
9563
|
migrate() {
|
|
10008
9564
|
try {
|
|
10009
|
-
const dbPath =
|
|
9565
|
+
const dbPath = join3(PLUGIN_DATA_DIR, `${pluginName}.db`);
|
|
10010
9566
|
pluginDb = openModuleDb(dbPath);
|
|
10011
9567
|
if (hasMigrate) {
|
|
10012
9568
|
raw.migrate?.(pluginDb);
|
|
@@ -10111,36 +9667,36 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookReg
|
|
|
10111
9667
|
return module;
|
|
10112
9668
|
}
|
|
10113
9669
|
async function ensurePluginDeps(pluginDir, pluginEntry) {
|
|
10114
|
-
const pkgJson =
|
|
10115
|
-
const lockfile =
|
|
10116
|
-
const nodeModules =
|
|
10117
|
-
if (!
|
|
10118
|
-
if (!
|
|
10119
|
-
|
|
9670
|
+
const pkgJson = join3(pluginDir, "package.json");
|
|
9671
|
+
const lockfile = join3(pluginDir, "package-lock.json");
|
|
9672
|
+
const nodeModules = join3(pluginDir, "node_modules");
|
|
9673
|
+
if (!existsSync5(pkgJson)) return;
|
|
9674
|
+
if (!existsSync5(lockfile)) {
|
|
9675
|
+
log9.warn(
|
|
10120
9676
|
`[${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
|
|
10121
9677
|
);
|
|
10122
9678
|
return;
|
|
10123
9679
|
}
|
|
10124
|
-
if (
|
|
10125
|
-
const marker =
|
|
10126
|
-
if (
|
|
9680
|
+
if (existsSync5(nodeModules)) {
|
|
9681
|
+
const marker = join3(nodeModules, ".package-lock.json");
|
|
9682
|
+
if (existsSync5(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
|
|
10127
9683
|
}
|
|
10128
|
-
|
|
9684
|
+
log9.info(`[${pluginEntry}] Installing dependencies...`);
|
|
10129
9685
|
try {
|
|
10130
9686
|
await execFileAsync("npm", ["ci", "--ignore-scripts", "--no-audit", "--no-fund"], {
|
|
10131
9687
|
cwd: pluginDir,
|
|
10132
9688
|
timeout: 6e4,
|
|
10133
9689
|
env: { ...process.env, NODE_ENV: "production" }
|
|
10134
9690
|
});
|
|
10135
|
-
|
|
9691
|
+
log9.info(`[${pluginEntry}] Dependencies installed`);
|
|
10136
9692
|
} catch (err) {
|
|
10137
|
-
|
|
9693
|
+
log9.error(`[${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
|
|
10138
9694
|
}
|
|
10139
9695
|
}
|
|
10140
9696
|
async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
10141
9697
|
const hookRegistry = new HookRegistry();
|
|
10142
9698
|
const pluginsDir = WORKSPACE_PATHS.PLUGINS_DIR;
|
|
10143
|
-
if (!
|
|
9699
|
+
if (!existsSync5(pluginsDir)) {
|
|
10144
9700
|
return { modules: [], hookRegistry };
|
|
10145
9701
|
}
|
|
10146
9702
|
let pluginPriorities = /* @__PURE__ */ new Map();
|
|
@@ -10156,15 +9712,15 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10156
9712
|
const pluginPaths = [];
|
|
10157
9713
|
for (const entry of entries) {
|
|
10158
9714
|
if (entry === "data") continue;
|
|
10159
|
-
const entryPath =
|
|
9715
|
+
const entryPath = join3(pluginsDir, entry);
|
|
10160
9716
|
let modulePath = null;
|
|
10161
9717
|
try {
|
|
10162
9718
|
const stat = statSync(entryPath);
|
|
10163
9719
|
if (stat.isFile() && entry.endsWith(".js")) {
|
|
10164
9720
|
modulePath = entryPath;
|
|
10165
9721
|
} else if (stat.isDirectory()) {
|
|
10166
|
-
const indexPath =
|
|
10167
|
-
if (
|
|
9722
|
+
const indexPath = join3(entryPath, "index.js");
|
|
9723
|
+
if (existsSync5(indexPath)) {
|
|
10168
9724
|
modulePath = indexPath;
|
|
10169
9725
|
}
|
|
10170
9726
|
}
|
|
@@ -10176,7 +9732,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10176
9732
|
}
|
|
10177
9733
|
}
|
|
10178
9734
|
await Promise.allSettled(
|
|
10179
|
-
pluginPaths.filter(({ path }) => path.endsWith("index.js")).map(({ entry }) => ensurePluginDeps(
|
|
9735
|
+
pluginPaths.filter(({ path }) => path.endsWith("index.js")).map(({ entry }) => ensurePluginDeps(join3(pluginsDir, entry), entry))
|
|
10180
9736
|
);
|
|
10181
9737
|
const loadResults = await Promise.allSettled(
|
|
10182
9738
|
pluginPaths.map(async ({ entry, path }) => {
|
|
@@ -10187,7 +9743,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10187
9743
|
);
|
|
10188
9744
|
for (const result of loadResults) {
|
|
10189
9745
|
if (result.status === "rejected") {
|
|
10190
|
-
|
|
9746
|
+
log9.error(
|
|
10191
9747
|
`Plugin failed to load: ${result.reason instanceof Error ? result.reason.message : result.reason}`
|
|
10192
9748
|
);
|
|
10193
9749
|
continue;
|
|
@@ -10195,7 +9751,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10195
9751
|
const { entry, mod } = result.value;
|
|
10196
9752
|
try {
|
|
10197
9753
|
if (!mod.tools || typeof mod.tools !== "function" && !Array.isArray(mod.tools)) {
|
|
10198
|
-
|
|
9754
|
+
log9.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
|
|
10199
9755
|
continue;
|
|
10200
9756
|
}
|
|
10201
9757
|
const adapted = adaptPlugin(
|
|
@@ -10208,21 +9764,21 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
10208
9764
|
pluginPriorities
|
|
10209
9765
|
);
|
|
10210
9766
|
if (loadedNames.has(adapted.name)) {
|
|
10211
|
-
|
|
9767
|
+
log9.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
|
|
10212
9768
|
continue;
|
|
10213
9769
|
}
|
|
10214
9770
|
loadedNames.add(adapted.name);
|
|
10215
9771
|
modules.push(adapted);
|
|
10216
9772
|
} catch (err) {
|
|
10217
|
-
|
|
9773
|
+
log9.error(`Plugin "${entry}" failed to adapt: ${err instanceof Error ? err.message : err}`);
|
|
10218
9774
|
}
|
|
10219
9775
|
}
|
|
10220
9776
|
return { modules, hookRegistry };
|
|
10221
9777
|
}
|
|
10222
9778
|
|
|
10223
9779
|
// src/config/configurable-keys.ts
|
|
10224
|
-
import { readFileSync as
|
|
10225
|
-
import { parse
|
|
9780
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6 } from "fs";
|
|
9781
|
+
import { parse, stringify } from "yaml";
|
|
10226
9782
|
var noValidation = () => void 0;
|
|
10227
9783
|
var identity = (v) => v;
|
|
10228
9784
|
var nonEmpty = (v) => v.length > 0 ? void 0 : "Must not be empty";
|
|
@@ -10841,11 +10397,11 @@ function deleteNestedValue(obj, path) {
|
|
|
10841
10397
|
}
|
|
10842
10398
|
function readRawConfig(configPath) {
|
|
10843
10399
|
const fullPath = expandPath(configPath);
|
|
10844
|
-
if (!
|
|
10400
|
+
if (!existsSync6(fullPath)) {
|
|
10845
10401
|
throw new Error(`Config file not found: ${fullPath}
|
|
10846
10402
|
Run 'teleton setup' to create one.`);
|
|
10847
10403
|
}
|
|
10848
|
-
const raw =
|
|
10404
|
+
const raw = parse(readFileSync5(fullPath, "utf-8"));
|
|
10849
10405
|
if (!raw || typeof raw !== "object") {
|
|
10850
10406
|
throw new Error(`Invalid config file: ${fullPath}`);
|
|
10851
10407
|
}
|
|
@@ -10861,26 +10417,26 @@ function writeRawConfig(raw, configPath) {
|
|
|
10861
10417
|
raw.meta = raw.meta ?? {};
|
|
10862
10418
|
raw.meta.last_modified_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10863
10419
|
const fullPath = expandPath(configPath);
|
|
10864
|
-
|
|
10420
|
+
writeFileSync2(fullPath, stringify(raw), { encoding: "utf-8", mode: 384 });
|
|
10865
10421
|
}
|
|
10866
10422
|
|
|
10867
10423
|
// src/ton-proxy/manager.ts
|
|
10868
10424
|
import { spawn, execSync } from "child_process";
|
|
10869
10425
|
import {
|
|
10870
|
-
existsSync as
|
|
10426
|
+
existsSync as existsSync7,
|
|
10871
10427
|
chmodSync,
|
|
10872
10428
|
createWriteStream,
|
|
10873
|
-
readFileSync as
|
|
10874
|
-
writeFileSync as
|
|
10429
|
+
readFileSync as readFileSync6,
|
|
10430
|
+
writeFileSync as writeFileSync3,
|
|
10875
10431
|
unlinkSync
|
|
10876
10432
|
} from "fs";
|
|
10877
|
-
import { mkdir
|
|
10878
|
-
import { join as
|
|
10433
|
+
import { mkdir } from "fs/promises";
|
|
10434
|
+
import { join as join4 } from "path";
|
|
10879
10435
|
import { pipeline } from "stream/promises";
|
|
10880
|
-
var
|
|
10436
|
+
var log10 = createLogger("TonProxy");
|
|
10881
10437
|
var GITHUB_REPO = "xssnick/Tonutils-Proxy";
|
|
10882
|
-
var BINARY_DIR =
|
|
10883
|
-
var PID_FILE =
|
|
10438
|
+
var BINARY_DIR = join4(TELETON_ROOT, "bin");
|
|
10439
|
+
var PID_FILE = join4(TELETON_ROOT, "ton-proxy.pid");
|
|
10884
10440
|
var HEALTH_CHECK_INTERVAL_MS = 3e4;
|
|
10885
10441
|
var HEALTH_CHECK_TIMEOUT_MS = 5e3;
|
|
10886
10442
|
var KILL_GRACE_MS = 5e3;
|
|
@@ -10896,11 +10452,11 @@ var TonProxyManager = class {
|
|
|
10896
10452
|
/** Resolve the binary path — user-specified or auto-detected */
|
|
10897
10453
|
getBinaryPath() {
|
|
10898
10454
|
if (this.config.binary_path) return this.config.binary_path;
|
|
10899
|
-
return
|
|
10455
|
+
return join4(BINARY_DIR, getBinaryName());
|
|
10900
10456
|
}
|
|
10901
10457
|
/** Check if the binary exists on disk */
|
|
10902
10458
|
isInstalled() {
|
|
10903
|
-
return
|
|
10459
|
+
return existsSync7(this.getBinaryPath());
|
|
10904
10460
|
}
|
|
10905
10461
|
/** Whether the proxy process is currently running */
|
|
10906
10462
|
isRunning() {
|
|
@@ -10912,8 +10468,8 @@ var TonProxyManager = class {
|
|
|
10912
10468
|
*/
|
|
10913
10469
|
async install() {
|
|
10914
10470
|
const binaryName = getBinaryName();
|
|
10915
|
-
|
|
10916
|
-
await
|
|
10471
|
+
log10.info(`Downloading TON Proxy binary (${binaryName})...`);
|
|
10472
|
+
await mkdir(BINARY_DIR, { recursive: true });
|
|
10917
10473
|
const releaseUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
10918
10474
|
const releaseRes = await fetch(releaseUrl, {
|
|
10919
10475
|
headers: { Accept: "application/vnd.github.v3+json" }
|
|
@@ -10924,7 +10480,7 @@ var TonProxyManager = class {
|
|
|
10924
10480
|
const release = await releaseRes.json();
|
|
10925
10481
|
const tag = release.tag_name;
|
|
10926
10482
|
const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${binaryName}`;
|
|
10927
|
-
|
|
10483
|
+
log10.info(`Downloading ${downloadUrl}`);
|
|
10928
10484
|
const res = await fetch(downloadUrl);
|
|
10929
10485
|
if (!res.ok || !res.body) {
|
|
10930
10486
|
throw new Error(`Download failed: ${res.status} ${res.statusText}`);
|
|
@@ -10933,17 +10489,17 @@ var TonProxyManager = class {
|
|
|
10933
10489
|
const fileStream = createWriteStream(dest);
|
|
10934
10490
|
await pipeline(res.body, fileStream);
|
|
10935
10491
|
chmodSync(dest, 493);
|
|
10936
|
-
|
|
10492
|
+
log10.info(`TON Proxy installed: ${dest} (${tag})`);
|
|
10937
10493
|
}
|
|
10938
10494
|
/** Kill any orphan proxy process from a previous session */
|
|
10939
10495
|
killOrphan() {
|
|
10940
|
-
if (
|
|
10496
|
+
if (existsSync7(PID_FILE)) {
|
|
10941
10497
|
try {
|
|
10942
|
-
const pid = parseInt(
|
|
10498
|
+
const pid = parseInt(readFileSync6(PID_FILE, "utf-8").trim(), 10);
|
|
10943
10499
|
if (pid && !isNaN(pid)) {
|
|
10944
10500
|
try {
|
|
10945
10501
|
process.kill(pid, 0);
|
|
10946
|
-
|
|
10502
|
+
log10.warn(`Killing orphan TON Proxy (PID ${pid}) from previous session`);
|
|
10947
10503
|
process.kill(pid, "SIGTERM");
|
|
10948
10504
|
} catch {
|
|
10949
10505
|
}
|
|
@@ -10960,7 +10516,7 @@ var TonProxyManager = class {
|
|
|
10960
10516
|
const pidMatch = out.match(/pid=(\d+)/);
|
|
10961
10517
|
if (pidMatch) {
|
|
10962
10518
|
const pid = parseInt(pidMatch[1], 10);
|
|
10963
|
-
|
|
10519
|
+
log10.warn(`Port ${this.config.port} occupied by PID ${pid}, killing it`);
|
|
10964
10520
|
try {
|
|
10965
10521
|
process.kill(pid, "SIGTERM");
|
|
10966
10522
|
} catch {
|
|
@@ -10973,22 +10529,22 @@ var TonProxyManager = class {
|
|
|
10973
10529
|
/** Write PID to file for orphan detection */
|
|
10974
10530
|
writePidFile(pid) {
|
|
10975
10531
|
try {
|
|
10976
|
-
|
|
10532
|
+
writeFileSync3(PID_FILE, String(pid), { mode: 384 });
|
|
10977
10533
|
} catch {
|
|
10978
|
-
|
|
10534
|
+
log10.warn("Failed to write TON Proxy PID file");
|
|
10979
10535
|
}
|
|
10980
10536
|
}
|
|
10981
10537
|
/** Remove PID file */
|
|
10982
10538
|
removePidFile() {
|
|
10983
10539
|
try {
|
|
10984
|
-
if (
|
|
10540
|
+
if (existsSync7(PID_FILE)) unlinkSync(PID_FILE);
|
|
10985
10541
|
} catch {
|
|
10986
10542
|
}
|
|
10987
10543
|
}
|
|
10988
10544
|
/** Start the proxy process */
|
|
10989
10545
|
async start() {
|
|
10990
10546
|
if (this.isRunning()) {
|
|
10991
|
-
|
|
10547
|
+
log10.warn("TON Proxy is already running");
|
|
10992
10548
|
return;
|
|
10993
10549
|
}
|
|
10994
10550
|
this.restartCount = 0;
|
|
@@ -10999,7 +10555,7 @@ var TonProxyManager = class {
|
|
|
10999
10555
|
}
|
|
11000
10556
|
const binaryPath = this.getBinaryPath();
|
|
11001
10557
|
const port = String(this.config.port);
|
|
11002
|
-
|
|
10558
|
+
log10.info(`Starting TON Proxy on 127.0.0.1:${port}`);
|
|
11003
10559
|
this.process = spawn(binaryPath, ["-addr", `127.0.0.1:${port}`], {
|
|
11004
10560
|
cwd: BINARY_DIR,
|
|
11005
10561
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -11007,24 +10563,24 @@ var TonProxyManager = class {
|
|
|
11007
10563
|
});
|
|
11008
10564
|
this.process.stdout?.on("data", (chunk) => {
|
|
11009
10565
|
const line = chunk.toString().trim();
|
|
11010
|
-
if (line)
|
|
10566
|
+
if (line) log10.debug(`[proxy] ${line}`);
|
|
11011
10567
|
});
|
|
11012
10568
|
this.process.stderr?.on("data", (chunk) => {
|
|
11013
10569
|
const line = chunk.toString().trim();
|
|
11014
|
-
if (line)
|
|
10570
|
+
if (line) log10.warn(`[proxy:err] ${line}`);
|
|
11015
10571
|
});
|
|
11016
10572
|
this.process.on("exit", (code, signal) => {
|
|
11017
|
-
|
|
10573
|
+
log10.info(`TON Proxy exited (code=${code}, signal=${signal})`);
|
|
11018
10574
|
this.process = null;
|
|
11019
10575
|
this.removePidFile();
|
|
11020
10576
|
if (code !== 0 && code !== null && this.restartCount < this.maxRestarts) {
|
|
11021
10577
|
this.restartCount++;
|
|
11022
|
-
|
|
11023
|
-
this.start().catch((err) =>
|
|
10578
|
+
log10.warn(`Auto-restarting TON Proxy (attempt ${this.restartCount}/${this.maxRestarts})`);
|
|
10579
|
+
this.start().catch((err) => log10.error({ err }, "Failed to auto-restart TON Proxy"));
|
|
11024
10580
|
}
|
|
11025
10581
|
});
|
|
11026
10582
|
this.process.on("error", (err) => {
|
|
11027
|
-
|
|
10583
|
+
log10.error({ err }, "TON Proxy process error");
|
|
11028
10584
|
this.process = null;
|
|
11029
10585
|
});
|
|
11030
10586
|
this.startHealthCheck();
|
|
@@ -11042,14 +10598,14 @@ var TonProxyManager = class {
|
|
|
11042
10598
|
});
|
|
11043
10599
|
});
|
|
11044
10600
|
if (this.process?.pid) this.writePidFile(this.process.pid);
|
|
11045
|
-
|
|
10601
|
+
log10.info(`TON Proxy running on 127.0.0.1:${port} (PID ${this.process?.pid})`);
|
|
11046
10602
|
}
|
|
11047
10603
|
/** Stop the proxy process gracefully */
|
|
11048
10604
|
async stop() {
|
|
11049
10605
|
this.stopHealthCheck();
|
|
11050
10606
|
if (!this.process) return;
|
|
11051
10607
|
this.maxRestarts = 0;
|
|
11052
|
-
|
|
10608
|
+
log10.info("Stopping TON Proxy...");
|
|
11053
10609
|
return new Promise((resolve2) => {
|
|
11054
10610
|
if (!this.process) {
|
|
11055
10611
|
resolve2();
|
|
@@ -11057,7 +10613,7 @@ var TonProxyManager = class {
|
|
|
11057
10613
|
}
|
|
11058
10614
|
const forceKill = setTimeout(() => {
|
|
11059
10615
|
if (this.process) {
|
|
11060
|
-
|
|
10616
|
+
log10.warn("TON Proxy did not exit gracefully, sending SIGKILL");
|
|
11061
10617
|
this.process.kill("SIGKILL");
|
|
11062
10618
|
}
|
|
11063
10619
|
}, KILL_GRACE_MS);
|
|
@@ -11076,10 +10632,10 @@ var TonProxyManager = class {
|
|
|
11076
10632
|
await this.stop();
|
|
11077
10633
|
}
|
|
11078
10634
|
const binaryPath = this.getBinaryPath();
|
|
11079
|
-
if (
|
|
10635
|
+
if (existsSync7(binaryPath)) {
|
|
11080
10636
|
const { unlink } = await import("fs/promises");
|
|
11081
10637
|
await unlink(binaryPath);
|
|
11082
|
-
|
|
10638
|
+
log10.info(`TON Proxy binary removed: ${binaryPath}`);
|
|
11083
10639
|
}
|
|
11084
10640
|
}
|
|
11085
10641
|
/** Get proxy status for WebUI / tools */
|
|
@@ -11113,7 +10669,7 @@ var TonProxyManager = class {
|
|
|
11113
10669
|
}).catch(() => null);
|
|
11114
10670
|
clearTimeout(timeout);
|
|
11115
10671
|
if (!res) {
|
|
11116
|
-
|
|
10672
|
+
log10.warn("TON Proxy health check failed (no response)");
|
|
11117
10673
|
}
|
|
11118
10674
|
} catch {
|
|
11119
10675
|
}
|
|
@@ -11170,7 +10726,7 @@ var tonProxyStatusExecutor = async () => {
|
|
|
11170
10726
|
};
|
|
11171
10727
|
|
|
11172
10728
|
// src/ton-proxy/module.ts
|
|
11173
|
-
var
|
|
10729
|
+
var log11 = createLogger("TonProxyModule");
|
|
11174
10730
|
var manager = null;
|
|
11175
10731
|
function getTonProxyManager() {
|
|
11176
10732
|
return manager;
|
|
@@ -11197,9 +10753,9 @@ var tonProxyModule = {
|
|
|
11197
10753
|
setProxyManager(manager);
|
|
11198
10754
|
try {
|
|
11199
10755
|
await manager.start();
|
|
11200
|
-
|
|
10756
|
+
log11.info(`TON Proxy started on port ${proxyConfig.port}`);
|
|
11201
10757
|
} catch (err) {
|
|
11202
|
-
|
|
10758
|
+
log11.error({ err }, "Failed to start TON Proxy");
|
|
11203
10759
|
manager = null;
|
|
11204
10760
|
}
|
|
11205
10761
|
},
|
|
@@ -11251,9 +10807,6 @@ function setTriggersConfig(db, triggers) {
|
|
|
11251
10807
|
}
|
|
11252
10808
|
|
|
11253
10809
|
export {
|
|
11254
|
-
loadConfig,
|
|
11255
|
-
configExists,
|
|
11256
|
-
getDefaultConfigPath,
|
|
11257
10810
|
WorkspaceSecurityError,
|
|
11258
10811
|
validatePath,
|
|
11259
10812
|
validateReadPath,
|