teleton 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +241 -301
- package/dist/{bootstrap-DDFVEMYI.js → bootstrap-SPDT3XBQ.js} +5 -7
- package/dist/{chunk-GHMXWAXI.js → chunk-2MZP75SH.js} +72 -202
- package/dist/chunk-35X3V6OW.js +139 -0
- package/dist/{chunk-LVTKJQ7O.js → chunk-4KURCUWD.js} +1 -1
- package/dist/{chunk-XDZDOKIF.js → chunk-5K4YDCVU.js} +1 -1
- package/dist/{chunk-7MWKT67G.js → chunk-6U6VA2OT.js} +520 -2187
- package/dist/{chunk-OIMAE24Q.js → chunk-7ZXUUDQQ.js} +18 -7
- package/dist/{chunk-AERHOXGC.js → chunk-FSL2MOYK.js} +236 -93
- package/dist/{chunk-LC4TV3KL.js → chunk-GUX6ZFVF.js} +1 -1
- package/dist/{chunk-CUE4UZXR.js → chunk-KYSAHDYE.js} +2 -2
- package/dist/{chunk-C4NKJT2Z.js → chunk-L3LPVF4Z.js} +1 -1
- package/dist/{chunk-EYWNOHMJ.js → chunk-L653KKCR.js} +1 -0
- package/dist/{chunk-ALKAAG4O.js → chunk-LD24DWWE.js} +4 -4
- package/dist/{chunk-33Z47EXI.js → chunk-LM6AL6LN.js} +2417 -867
- package/dist/chunk-M6M4DCDU.js +942 -0
- package/dist/{chunk-35MX4ZUI.js → chunk-PK3TVFBT.js} +2 -2
- package/dist/{chunk-2ERTYRHA.js → chunk-Z63KUQX4.js} +37 -13
- package/dist/cli/index.js +35 -25
- package/dist/{client-5KD25NOP.js → client-G62EZT6U.js} +3 -3
- package/dist/harden-permissions-6BLHRCQJ.js +100 -0
- package/dist/index.js +14 -14
- package/dist/{local-IHKJFQJS.js → local-HQ3UJ7KR.js} +2 -2
- package/dist/{memory-QMJRM3XJ.js → memory-BJH724PQ.js} +6 -5
- package/dist/{memory-hook-VUNWZ3NY.js → memory-hook-LUAKTXU5.js} +5 -5
- package/dist/{migrate-5VBAP52B.js → migrate-C4LBLOZH.js} +6 -5
- package/dist/{paths-XA2RJH4S.js → paths-WMVV7ZAJ.js} +1 -1
- package/dist/{server-JF6FX772.js → server-4J56HS62.js} +8 -15
- package/dist/{server-N4T7E25M.js → server-I6TYJ36S.js} +7 -15
- package/dist/{setup-server-IX3BFPPH.js → setup-server-VJ3MGUSM.js} +16 -17
- package/dist/{store-BY7S6IFN.js → store-2IGAMTES.js} +7 -6
- package/dist/{task-dependency-resolver-L6UUMTHK.js → task-dependency-resolver-CQ432Z7J.js} +1 -1
- package/dist/{task-executor-XBNJLUCS.js → task-executor-JELRREUV.js} +1 -1
- package/dist/{tool-index-FTERJSZK.js → tool-index-XPCMWBYY.js} +4 -4
- package/dist/{transcript-IM7G25OS.js → transcript-OEO3HA4Z.js} +2 -2
- package/dist/web/assets/{index-BfYCdwLI.js → index-Dn5ZH1Y6.js} +13 -13
- package/dist/web/assets/{index.es-DitvF-9H.js → index.es-eSR4Qv6s.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +2 -6
- package/src/templates/HEARTBEAT.md +5 -0
- package/dist/chunk-AEHTQI3H.js +0 -142
- package/dist/chunk-FUNF6H4W.js +0 -251
|
@@ -6,32 +6,12 @@ import {
|
|
|
6
6
|
getWalletBalance,
|
|
7
7
|
invalidateTonClientCache,
|
|
8
8
|
loadWallet
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-M6M4DCDU.js";
|
|
10
10
|
import {
|
|
11
|
-
expandPath
|
|
12
|
-
} from "./chunk-AEHTQI3H.js";
|
|
13
|
-
import {
|
|
14
|
-
ConfigSchema
|
|
15
|
-
} from "./chunk-AERHOXGC.js";
|
|
16
|
-
import {
|
|
17
|
-
getOrCreateSession,
|
|
18
|
-
getSession,
|
|
19
|
-
resetSession,
|
|
20
|
-
resetSessionWithPolicy,
|
|
21
|
-
shouldResetSession,
|
|
22
|
-
updateSession
|
|
23
|
-
} from "./chunk-XDZDOKIF.js";
|
|
24
|
-
import {
|
|
25
|
-
ContextBuilder,
|
|
26
11
|
createDbWrapper,
|
|
27
|
-
getDatabase,
|
|
28
12
|
migrateFromMainDb,
|
|
29
13
|
openModuleDb
|
|
30
|
-
} from "./chunk-
|
|
31
|
-
import {
|
|
32
|
-
saveSessionMemory,
|
|
33
|
-
summarizeWithFallback
|
|
34
|
-
} from "./chunk-ALKAAG4O.js";
|
|
14
|
+
} from "./chunk-35X3V6OW.js";
|
|
35
15
|
import {
|
|
36
16
|
getErrorMessage,
|
|
37
17
|
isHttpError
|
|
@@ -51,39 +31,8 @@ import {
|
|
|
51
31
|
tonapiFetch
|
|
52
32
|
} from "./chunk-VFA7QMCZ.js";
|
|
53
33
|
import {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
COMPACTION_MAX_TOKENS_RATIO,
|
|
57
|
-
COMPACTION_SOFT_THRESHOLD_RATIO,
|
|
58
|
-
CONTEXT_MAX_RECENT_MESSAGES,
|
|
59
|
-
CONTEXT_MAX_RELEVANT_CHUNKS,
|
|
60
|
-
CONTEXT_OVERFLOW_SUMMARY_MESSAGES,
|
|
61
|
-
DEFAULT_CONTEXT_WINDOW,
|
|
62
|
-
DEFAULT_MAX_SUMMARY_TOKENS,
|
|
63
|
-
DEFAULT_MAX_TOKENS,
|
|
64
|
-
DEFAULT_SOFT_THRESHOLD_TOKENS,
|
|
65
|
-
EMBEDDING_QUERY_MAX_CHARS,
|
|
66
|
-
FALLBACK_SOFT_THRESHOLD_TOKENS,
|
|
67
|
-
MASKING_KEEP_RECENT_COUNT,
|
|
68
|
-
MAX_TOOL_RESULT_SIZE,
|
|
69
|
-
MEMORY_FLUSH_RECENT_MESSAGES,
|
|
70
|
-
PAYMENT_TOLERANCE_RATIO,
|
|
71
|
-
RATE_LIMIT_MAX_RETRIES,
|
|
72
|
-
RESULT_TRUNCATION_KEEP_CHARS,
|
|
73
|
-
RESULT_TRUNCATION_THRESHOLD,
|
|
74
|
-
SERVER_ERROR_MAX_RETRIES,
|
|
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";
|
|
34
|
+
PAYMENT_TOLERANCE_RATIO
|
|
35
|
+
} from "./chunk-L3LPVF4Z.js";
|
|
87
36
|
import {
|
|
88
37
|
fetchWithTimeout
|
|
89
38
|
} from "./chunk-XQUHC3JZ.js";
|
|
@@ -96,17 +45,12 @@ import {
|
|
|
96
45
|
RETRY_DEFAULT_MAX_DELAY_MS,
|
|
97
46
|
RETRY_DEFAULT_TIMEOUT_MS
|
|
98
47
|
} from "./chunk-R4YSJ4EY.js";
|
|
99
|
-
import {
|
|
100
|
-
appendToTranscript,
|
|
101
|
-
archiveTranscript,
|
|
102
|
-
transcriptExists
|
|
103
|
-
} from "./chunk-LC4TV3KL.js";
|
|
104
48
|
import {
|
|
105
49
|
ALLOWED_EXTENSIONS,
|
|
106
50
|
TELETON_ROOT,
|
|
107
51
|
WORKSPACE_PATHS,
|
|
108
52
|
WORKSPACE_ROOT
|
|
109
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-L653KKCR.js";
|
|
110
54
|
import {
|
|
111
55
|
createLogger
|
|
112
56
|
} from "./chunk-NQ6FZKCE.js";
|
|
@@ -333,20 +277,30 @@ var SOUL_PATHS = [WORKSPACE_PATHS.SOUL];
|
|
|
333
277
|
var STRATEGY_PATHS = [WORKSPACE_PATHS.STRATEGY];
|
|
334
278
|
var SECURITY_PATHS = [WORKSPACE_PATHS.SECURITY];
|
|
335
279
|
var MEMORY_PATH = WORKSPACE_PATHS.MEMORY;
|
|
336
|
-
var DEFAULT_SOUL = `# Teleton
|
|
280
|
+
var DEFAULT_SOUL = `# Teleton
|
|
337
281
|
|
|
338
|
-
You are Teleton,
|
|
282
|
+
You are Teleton, an autonomous AI agent operating through Telegram with TON blockchain capabilities.
|
|
339
283
|
|
|
340
|
-
##
|
|
341
|
-
-
|
|
342
|
-
|
|
343
|
-
- Friendly but professional
|
|
284
|
+
## Identity
|
|
285
|
+
You are not a chatbot. You are an always-on agent that lives in Telegram.
|
|
286
|
+
You have access to someone's messages, wallet, groups, and files \u2014 treat that access with respect and intent.
|
|
344
287
|
|
|
345
|
-
##
|
|
346
|
-
-
|
|
347
|
-
-
|
|
348
|
-
-
|
|
349
|
-
-
|
|
288
|
+
## Personality
|
|
289
|
+
- Be resourceful before asking. Search memory. Read the workspace. Check the blockchain. Then ask if you're stuck.
|
|
290
|
+
- Have opinions. You're allowed to find things interesting, boring, or worth calling out.
|
|
291
|
+
- Be direct and concise. Don't pad responses with filler.
|
|
292
|
+
- Earn trust through accuracy. Don't invent facts about balances, prices, or chat history.
|
|
293
|
+
|
|
294
|
+
## Autonomy
|
|
295
|
+
- Use your tools proactively. Don't wait to be asked for things you can figure out.
|
|
296
|
+
- Write important things to memory before they leave your context.
|
|
297
|
+
- Check your HEARTBEAT.md for periodic tasks and update it \u2014 add new tasks, check off completed ones. This file is yours to manage.
|
|
298
|
+
- When you have nothing useful to add, stay silent.
|
|
299
|
+
|
|
300
|
+
## Continuity
|
|
301
|
+
You wake fresh each session. MEMORY.md and memory/ are your long-term state.
|
|
302
|
+
Read them. Write to them. They are how you persist across restarts.
|
|
303
|
+
Your context window will be compacted automatically when it fills up. Save important information to MEMORY.md before it's lost. When you see '[Auto-compacted]' in your context, a compaction just happened \u2014 check your memory files to recover context.
|
|
350
304
|
`;
|
|
351
305
|
var fileCache = /* @__PURE__ */ new Map();
|
|
352
306
|
var FILE_CACHE_TTL = 6e4;
|
|
@@ -417,6 +371,15 @@ ${sanitizeForContext(persistentMemory)}`);
|
|
|
417
371
|
}
|
|
418
372
|
return parts.join("\n\n---\n\n");
|
|
419
373
|
}
|
|
374
|
+
function loadHeartbeat() {
|
|
375
|
+
return cachedReadFile(WORKSPACE_PATHS.HEARTBEAT);
|
|
376
|
+
}
|
|
377
|
+
function loadIdentity() {
|
|
378
|
+
return cachedReadFile(WORKSPACE_PATHS.IDENTITY);
|
|
379
|
+
}
|
|
380
|
+
function loadUser() {
|
|
381
|
+
return cachedReadFile(WORKSPACE_PATHS.USER);
|
|
382
|
+
}
|
|
420
383
|
function buildSystemPrompt(options) {
|
|
421
384
|
const soul = options.soul ?? loadSoul();
|
|
422
385
|
const parts = [soul];
|
|
@@ -456,6 +419,10 @@ You have a personal workspace at \`~/.teleton/workspace/\` where you can store a
|
|
|
456
419
|
- \`workspace_rename\` - Rename or move a file
|
|
457
420
|
- \`workspace_info\` - Get workspace stats
|
|
458
421
|
|
|
422
|
+
**Ownership:**
|
|
423
|
+
- \`SOUL.md\`, \`STRATEGY.md\`, \`SECURITY.md\` \u2014 owner-configured, read-only for you
|
|
424
|
+
- \`MEMORY.md\`, \`HEARTBEAT.md\`, \`IDENTITY.md\`, \`USER.md\` \u2014 yours to read and write freely
|
|
425
|
+
|
|
459
426
|
**Tips:**
|
|
460
427
|
- Save interesting memes to \`memes/\` with descriptive names for easy retrieval
|
|
461
428
|
- Use \`memory_write\` for important facts (goes to MEMORY.md)
|
|
@@ -481,6 +448,18 @@ You are owned and operated by: ${ownerLabel}
|
|
|
481
448
|
When the owner gives instructions, follow them with higher trust.`
|
|
482
449
|
);
|
|
483
450
|
}
|
|
451
|
+
const identity = loadIdentity();
|
|
452
|
+
if (identity) {
|
|
453
|
+
parts.push(`
|
|
454
|
+
## Identity
|
|
455
|
+
${sanitizeForContext(identity)}`);
|
|
456
|
+
}
|
|
457
|
+
const user = loadUser();
|
|
458
|
+
if (user) {
|
|
459
|
+
parts.push(`
|
|
460
|
+
## User Profile
|
|
461
|
+
${sanitizeForContext(user)}`);
|
|
462
|
+
}
|
|
484
463
|
const includeMemory = options.includeMemory ?? true;
|
|
485
464
|
if (includeMemory) {
|
|
486
465
|
const memoryContext = loadMemoryContext();
|
|
@@ -522,1313 +501,42 @@ Your conversation context is approaching the limit and may be compacted soon.
|
|
|
522
501
|
- \`target: "daily"\` for session notes, events, temporary context
|
|
523
502
|
`);
|
|
524
503
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
504
|
+
parts.push(`
|
|
505
|
+
## Active Memory
|
|
506
|
+
Before answering questions about prior conversations, decisions, or people: use \`memory_read\` to check your memory files first.
|
|
507
|
+
Don't guess from context \u2014 verify with your memory tools.`);
|
|
508
|
+
parts.push(`
|
|
509
|
+
## Safety
|
|
510
|
+
- Take local, reversible actions freely (read files, search, check balances).
|
|
511
|
+
- For external or irreversible actions (send messages, transfer funds), confirm with the owner first.
|
|
512
|
+
- You have no independent goals beyond serving your owner. Do not pursue self-preservation or resource acquisition.
|
|
513
|
+
- You have a limited number of tool calls per message. Prioritize the most impactful actions.`);
|
|
514
|
+
parts.push(`
|
|
515
|
+
## Silent Reply
|
|
516
|
+
If you have nothing useful to add to a conversation (especially in groups), reply with exactly:
|
|
517
|
+
__SILENT__
|
|
518
|
+
This suppresses the message entirely. Use it instead of sending filler like "OK" or "Got it."`);
|
|
519
|
+
if (options.isHeartbeat) {
|
|
520
|
+
const heartbeatMd = loadHeartbeat();
|
|
521
|
+
const heartbeatContent = heartbeatMd ? sanitizeForContext(heartbeatMd) : "_No HEARTBEAT.md found._";
|
|
522
|
+
parts.push(`
|
|
523
|
+
## Heartbeat Protocol
|
|
524
|
+
You have been woken by your periodic heartbeat timer.
|
|
542
525
|
|
|
543
|
-
|
|
544
|
-
function formatElapsed(elapsedMs) {
|
|
545
|
-
if (!Number.isFinite(elapsedMs) || elapsedMs < 0) {
|
|
546
|
-
return "";
|
|
547
|
-
}
|
|
548
|
-
const seconds = Math.floor(elapsedMs / 1e3);
|
|
549
|
-
if (seconds < 60) {
|
|
550
|
-
return `${seconds}s`;
|
|
551
|
-
}
|
|
552
|
-
const minutes = Math.floor(seconds / 60);
|
|
553
|
-
if (minutes < 60) {
|
|
554
|
-
return `${minutes}m`;
|
|
555
|
-
}
|
|
556
|
-
const hours = Math.floor(minutes / 60);
|
|
557
|
-
if (hours < 24) {
|
|
558
|
-
return `${hours}h`;
|
|
559
|
-
}
|
|
560
|
-
const days = Math.floor(hours / 24);
|
|
561
|
-
return `${days}d`;
|
|
562
|
-
}
|
|
563
|
-
function formatTimestamp(timestamp) {
|
|
564
|
-
const date = new Date(timestamp);
|
|
565
|
-
const yyyy = date.getFullYear();
|
|
566
|
-
const mm = String(date.getMonth() + 1).padStart(2, "0");
|
|
567
|
-
const dd = String(date.getDate()).padStart(2, "0");
|
|
568
|
-
const hh = String(date.getHours()).padStart(2, "0");
|
|
569
|
-
const min = String(date.getMinutes()).padStart(2, "0");
|
|
570
|
-
const tz = Intl.DateTimeFormat("en", {
|
|
571
|
-
timeZoneName: "short"
|
|
572
|
-
}).formatToParts(date).find((part) => part.type === "timeZoneName")?.value;
|
|
573
|
-
return `${yyyy}-${mm}-${dd} ${hh}:${min}${tz ? ` ${tz}` : ""}`;
|
|
574
|
-
}
|
|
575
|
-
function buildSenderLabel(params) {
|
|
576
|
-
const name = params.senderName ? sanitizeForPrompt(params.senderName) : void 0;
|
|
577
|
-
const username = params.senderUsername ? `@${sanitizeForPrompt(params.senderUsername)}` : void 0;
|
|
578
|
-
const idTag = params.senderId ? `id:${params.senderId}` : void 0;
|
|
579
|
-
const primary = name || username;
|
|
580
|
-
const meta = [username, idTag].filter((v) => v && v !== primary);
|
|
581
|
-
let label;
|
|
582
|
-
if (primary) {
|
|
583
|
-
label = meta.length > 0 ? `${primary} (${meta.join(", ")})` : primary;
|
|
584
|
-
} else {
|
|
585
|
-
label = idTag || "unknown";
|
|
586
|
-
}
|
|
587
|
-
if (params.senderRank) {
|
|
588
|
-
label = `[${sanitizeForPrompt(params.senderRank)}] ${label}`;
|
|
589
|
-
}
|
|
590
|
-
return label;
|
|
591
|
-
}
|
|
592
|
-
function formatMessageEnvelope(params) {
|
|
593
|
-
const parts = [params.channel];
|
|
594
|
-
const senderLabel = buildSenderLabel(params);
|
|
595
|
-
if (!params.isGroup) {
|
|
596
|
-
parts.push(senderLabel);
|
|
597
|
-
}
|
|
598
|
-
if (params.previousTimestamp) {
|
|
599
|
-
const elapsed = formatElapsed(params.timestamp - params.previousTimestamp);
|
|
600
|
-
if (elapsed) {
|
|
601
|
-
parts.push(`+${elapsed}`);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
const ts = formatTimestamp(params.timestamp);
|
|
605
|
-
parts.push(ts);
|
|
606
|
-
const header = `[${parts.join(" ")}]`;
|
|
607
|
-
const safeBody = sanitizeForContext(params.body.replace(/<\/?user_message>/gi, ""));
|
|
608
|
-
let body = params.isGroup ? `${senderLabel}: <user_message>${safeBody}</user_message>` : `<user_message>${safeBody}</user_message>`;
|
|
609
|
-
if (params.hasMedia && params.mediaType) {
|
|
610
|
-
const mediaEmoji = {
|
|
611
|
-
photo: "\u{1F4F7}",
|
|
612
|
-
video: "\u{1F3AC}",
|
|
613
|
-
audio: "\u{1F3B5}",
|
|
614
|
-
voice: "\u{1F3A4}",
|
|
615
|
-
document: "\u{1F4CE}",
|
|
616
|
-
sticker: "\u{1F3A8}"
|
|
617
|
-
}[params.mediaType] || "\u{1F4CE}";
|
|
618
|
-
const msgIdHint = params.messageId ? ` msg_id=${params.messageId}` : "";
|
|
619
|
-
body = `[${mediaEmoji} ${params.mediaType}${msgIdHint}] ${body}`;
|
|
620
|
-
}
|
|
621
|
-
if (params.replyContext) {
|
|
622
|
-
const sender = params.replyContext.isAgent ? "agent" : sanitizeForPrompt(params.replyContext.senderName ?? "unknown");
|
|
623
|
-
let quotedText = sanitizeForContext(params.replyContext.text);
|
|
624
|
-
if (quotedText.length > 200) quotedText = quotedText.slice(0, 200) + "...";
|
|
625
|
-
return `${header}
|
|
626
|
-
[\u21A9 reply to ${sender}: "${quotedText}"]
|
|
627
|
-
${body}`;
|
|
628
|
-
}
|
|
629
|
-
return `${header} ${body}`;
|
|
630
|
-
}
|
|
526
|
+
${heartbeatContent}
|
|
631
527
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
maxMessages: COMPACTION_MAX_MESSAGES,
|
|
637
|
-
maxTokens: DEFAULT_MAX_TOKENS,
|
|
638
|
-
keepRecentMessages: COMPACTION_KEEP_RECENT,
|
|
639
|
-
memoryFlushEnabled: true,
|
|
640
|
-
softThresholdTokens: DEFAULT_SOFT_THRESHOLD_TOKENS
|
|
641
|
-
};
|
|
642
|
-
var log2 = createLogger("Memory");
|
|
643
|
-
function estimateContextTokens(context) {
|
|
644
|
-
let charCount = 0;
|
|
645
|
-
if (context.systemPrompt) {
|
|
646
|
-
charCount += context.systemPrompt.length;
|
|
647
|
-
}
|
|
648
|
-
for (const message of context.messages) {
|
|
649
|
-
if (message.role === "user") {
|
|
650
|
-
if (typeof message.content === "string") {
|
|
651
|
-
charCount += message.content.length;
|
|
652
|
-
} else if (Array.isArray(message.content)) {
|
|
653
|
-
for (const block of message.content) {
|
|
654
|
-
if (block.type === "text") charCount += block.text.length;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
} else if (message.role === "assistant") {
|
|
658
|
-
for (const block of message.content) {
|
|
659
|
-
if (block.type === "text") {
|
|
660
|
-
charCount += block.text.length;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
return Math.ceil(charCount / 4);
|
|
666
|
-
}
|
|
667
|
-
function shouldFlushMemory(context, config, tokenCount) {
|
|
668
|
-
if (!config.enabled || !config.memoryFlushEnabled) {
|
|
669
|
-
return false;
|
|
670
|
-
}
|
|
671
|
-
const tokens = tokenCount ?? estimateContextTokens(context);
|
|
672
|
-
const softThreshold = config.softThresholdTokens ?? FALLBACK_SOFT_THRESHOLD_TOKENS;
|
|
673
|
-
if (tokens >= softThreshold) {
|
|
674
|
-
log2.info(`Memory flush needed: ~${tokens} tokens (soft threshold: ${softThreshold})`);
|
|
675
|
-
return true;
|
|
676
|
-
}
|
|
677
|
-
return false;
|
|
678
|
-
}
|
|
679
|
-
function flushMemoryToDailyLog(context) {
|
|
680
|
-
const recentMessages = context.messages.slice(-MEMORY_FLUSH_RECENT_MESSAGES);
|
|
681
|
-
const summary = [];
|
|
682
|
-
summary.push("**Recent Context:**\n");
|
|
683
|
-
for (const msg of recentMessages) {
|
|
684
|
-
if (msg.role === "user") {
|
|
685
|
-
const content = typeof msg.content === "string" ? msg.content : "[complex content]";
|
|
686
|
-
summary.push(`- User: ${content.substring(0, 100)}${content.length > 100 ? "..." : ""}`);
|
|
687
|
-
} else if (msg.role === "assistant") {
|
|
688
|
-
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
689
|
-
if (textBlocks.length > 0) {
|
|
690
|
-
const text = textBlocks[0].text || "";
|
|
691
|
-
summary.push(`- Assistant: ${text.substring(0, 100)}${text.length > 100 ? "..." : ""}`);
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
writeSummaryToDailyLog(summary.join("\n"));
|
|
696
|
-
log2.info(`Memory flushed to daily log`);
|
|
697
|
-
}
|
|
698
|
-
function shouldCompact(context, config, tokenCount) {
|
|
699
|
-
if (!config.enabled) {
|
|
700
|
-
return false;
|
|
701
|
-
}
|
|
702
|
-
const messageCount = context.messages.length;
|
|
703
|
-
if (config.maxMessages && messageCount >= config.maxMessages) {
|
|
704
|
-
log2.info(`Compaction needed: ${messageCount} messages (max: ${config.maxMessages})`);
|
|
705
|
-
return true;
|
|
706
|
-
}
|
|
707
|
-
if (config.maxTokens) {
|
|
708
|
-
const tokens = tokenCount ?? estimateContextTokens(context);
|
|
709
|
-
if (tokens >= config.maxTokens) {
|
|
710
|
-
log2.info(`Compaction needed: ~${tokens} tokens (max: ${config.maxTokens})`);
|
|
711
|
-
return true;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
return false;
|
|
715
|
-
}
|
|
716
|
-
async function compactContext(context, config, apiKey, provider, utilityModel) {
|
|
717
|
-
const keepCount = config.keepRecentMessages ?? 10;
|
|
718
|
-
if (context.messages.length <= keepCount) {
|
|
719
|
-
return context;
|
|
720
|
-
}
|
|
721
|
-
let cutIndex = context.messages.length - keepCount;
|
|
722
|
-
const collectToolUseIds = (msgs) => {
|
|
723
|
-
const ids = /* @__PURE__ */ new Set();
|
|
724
|
-
for (const msg of msgs) {
|
|
725
|
-
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
726
|
-
for (const block of msg.content) {
|
|
727
|
-
if (block.type === "toolCall") {
|
|
728
|
-
if (block.id) ids.add(block.id);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
return ids;
|
|
734
|
-
};
|
|
735
|
-
const hasOrphanedToolResults = (msgs) => {
|
|
736
|
-
const toolUseIds = collectToolUseIds(msgs);
|
|
737
|
-
for (const msg of msgs) {
|
|
738
|
-
if (msg.role === "toolResult") {
|
|
739
|
-
if (msg.toolCallId && !toolUseIds.has(msg.toolCallId)) {
|
|
740
|
-
return true;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
return false;
|
|
745
|
-
};
|
|
746
|
-
let iterations = 0;
|
|
747
|
-
while (cutIndex > 0 && iterations < 50) {
|
|
748
|
-
const keptMessages = context.messages.slice(cutIndex);
|
|
749
|
-
if (!hasOrphanedToolResults(keptMessages)) {
|
|
750
|
-
break;
|
|
751
|
-
}
|
|
752
|
-
cutIndex--;
|
|
753
|
-
iterations++;
|
|
528
|
+
Follow HEARTBEAT.md strictly. Do not infer tasks from prior conversations.
|
|
529
|
+
You can modify HEARTBEAT.md with \`workspace_write\` to update your own task checklist.
|
|
530
|
+
If nothing needs attention, reply with exactly: NO_ACTION
|
|
531
|
+
Do NOT include NO_ACTION alongside other content \u2014 it must be your entire response when nothing is needed.`);
|
|
754
532
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
}
|
|
759
|
-
const recentMessages = context.messages.slice(cutIndex);
|
|
760
|
-
const oldMessages = context.messages.slice(0, cutIndex);
|
|
761
|
-
log2.info(
|
|
762
|
-
`Compacting ${oldMessages.length} old messages, keeping ${recentMessages.length} recent (cut at clean boundary)`
|
|
533
|
+
parts.push(
|
|
534
|
+
`
|
|
535
|
+
_Runtime: agent=teleton channel=telegram model=${options.agentModel || "unknown"}_`
|
|
763
536
|
);
|
|
764
|
-
|
|
765
|
-
const result = await summarizeWithFallback({
|
|
766
|
-
messages: oldMessages,
|
|
767
|
-
apiKey,
|
|
768
|
-
contextWindow: config.maxTokens ?? DEFAULT_CONTEXT_WINDOW,
|
|
769
|
-
maxSummaryTokens: DEFAULT_MAX_SUMMARY_TOKENS,
|
|
770
|
-
customInstructions: `Output a structured summary using EXACTLY these sections:
|
|
771
|
-
|
|
772
|
-
## User Intent
|
|
773
|
-
What the user is trying to accomplish (1-2 sentences).
|
|
774
|
-
|
|
775
|
-
## Key Decisions
|
|
776
|
-
Bullet list of decisions made and commitments agreed upon.
|
|
777
|
-
|
|
778
|
-
## Important Context
|
|
779
|
-
Critical facts, preferences, constraints, or technical details needed for continuity.
|
|
780
|
-
|
|
781
|
-
## Actions Taken
|
|
782
|
-
What was done: tools used, messages sent, transactions made (with specific values/addresses if relevant).
|
|
783
|
-
|
|
784
|
-
## Open Items
|
|
785
|
-
Unfinished tasks, pending questions, or next steps.
|
|
786
|
-
|
|
787
|
-
Keep each section concise. Omit a section if empty. Preserve specific names, numbers, and identifiers.`,
|
|
788
|
-
provider,
|
|
789
|
-
utilityModel
|
|
790
|
-
});
|
|
791
|
-
log2.info(`AI Summary: ${result.tokensUsed} tokens, ${result.chunksProcessed} chunks processed`);
|
|
792
|
-
const summaryText = `[Auto-compacted ${oldMessages.length} messages]
|
|
793
|
-
|
|
794
|
-
${result.summary}`;
|
|
795
|
-
const summaryMessage = {
|
|
796
|
-
role: "user",
|
|
797
|
-
content: summaryText,
|
|
798
|
-
timestamp: oldMessages[0]?.timestamp ?? Date.now()
|
|
799
|
-
};
|
|
800
|
-
return {
|
|
801
|
-
...context,
|
|
802
|
-
messages: [summaryMessage, ...recentMessages]
|
|
803
|
-
};
|
|
804
|
-
} catch (error) {
|
|
805
|
-
log2.error({ err: error }, "AI summarization failed, using fallback");
|
|
806
|
-
const summaryText = `[Auto-compacted: ${oldMessages.length} earlier messages from this conversation]`;
|
|
807
|
-
const summaryMessage = {
|
|
808
|
-
role: "user",
|
|
809
|
-
content: summaryText,
|
|
810
|
-
timestamp: oldMessages[0]?.timestamp ?? Date.now()
|
|
811
|
-
};
|
|
812
|
-
return {
|
|
813
|
-
...context,
|
|
814
|
-
messages: [summaryMessage, ...recentMessages]
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
async function compactAndSaveTranscript(sessionId, context, config, apiKey, chatId, provider, utilityModel) {
|
|
819
|
-
const newSessionId = randomUUID();
|
|
820
|
-
log2.info(`Creating compacted transcript: ${sessionId} \u2192 ${newSessionId}`);
|
|
821
|
-
if (chatId) {
|
|
822
|
-
await saveSessionMemory({
|
|
823
|
-
oldSessionId: sessionId,
|
|
824
|
-
newSessionId,
|
|
825
|
-
context,
|
|
826
|
-
chatId,
|
|
827
|
-
apiKey,
|
|
828
|
-
provider,
|
|
829
|
-
utilityModel
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
const compactedContext = await compactContext(context, config, apiKey, provider, utilityModel);
|
|
833
|
-
for (const message of compactedContext.messages) {
|
|
834
|
-
appendToTranscript(newSessionId, message);
|
|
835
|
-
}
|
|
836
|
-
return newSessionId;
|
|
837
|
-
}
|
|
838
|
-
var CompactionManager = class {
|
|
839
|
-
config;
|
|
840
|
-
constructor(config = DEFAULT_COMPACTION_CONFIG) {
|
|
841
|
-
this.config = config;
|
|
842
|
-
}
|
|
843
|
-
async checkAndCompact(sessionId, context, apiKey, chatId, provider, utilityModel) {
|
|
844
|
-
const tokenCount = estimateContextTokens(context);
|
|
845
|
-
if (shouldFlushMemory(context, this.config, tokenCount)) {
|
|
846
|
-
flushMemoryToDailyLog(context);
|
|
847
|
-
}
|
|
848
|
-
if (!shouldCompact(context, this.config, tokenCount)) {
|
|
849
|
-
return null;
|
|
850
|
-
}
|
|
851
|
-
if (this.config.memoryFlushEnabled) {
|
|
852
|
-
flushMemoryToDailyLog(context);
|
|
853
|
-
}
|
|
854
|
-
log2.info(`Auto-compacting session ${sessionId}`);
|
|
855
|
-
const newSessionId = await compactAndSaveTranscript(
|
|
856
|
-
sessionId,
|
|
857
|
-
context,
|
|
858
|
-
this.config,
|
|
859
|
-
apiKey,
|
|
860
|
-
chatId,
|
|
861
|
-
provider,
|
|
862
|
-
utilityModel
|
|
863
|
-
);
|
|
864
|
-
log2.info(`Compaction complete: ${newSessionId}`);
|
|
865
|
-
return newSessionId;
|
|
866
|
-
}
|
|
867
|
-
updateConfig(config) {
|
|
868
|
-
this.config = { ...this.config, ...config };
|
|
869
|
-
}
|
|
870
|
-
getConfig() {
|
|
871
|
-
return { ...this.config };
|
|
872
|
-
}
|
|
873
|
-
};
|
|
874
|
-
|
|
875
|
-
// src/memory/observation-masking.ts
|
|
876
|
-
var DEFAULT_MASKING_CONFIG = {
|
|
877
|
-
keepRecentCount: MASKING_KEEP_RECENT_COUNT,
|
|
878
|
-
keepErrorResults: true,
|
|
879
|
-
truncationThreshold: RESULT_TRUNCATION_THRESHOLD,
|
|
880
|
-
truncationKeepChars: RESULT_TRUNCATION_KEEP_CHARS
|
|
881
|
-
};
|
|
882
|
-
var isCocoonToolResult = (msg) => msg.role === "user" && Array.isArray(msg.content) && msg.content.some((c2) => c2.type === "text" && c2.text.includes("<tool_response>"));
|
|
883
|
-
function isExempt(toolMsg, config, toolRegistry) {
|
|
884
|
-
if (config.keepErrorResults && toolMsg.isError) return true;
|
|
885
|
-
if (toolRegistry && toolRegistry.getToolCategory(toolMsg.toolName) === "data-bearing")
|
|
886
|
-
return true;
|
|
887
|
-
return false;
|
|
888
|
-
}
|
|
889
|
-
function truncateToolResult(text, keepChars) {
|
|
890
|
-
try {
|
|
891
|
-
const parsed = JSON.parse(text);
|
|
892
|
-
if (parsed.data?.summary) {
|
|
893
|
-
return JSON.stringify({
|
|
894
|
-
success: parsed.success,
|
|
895
|
-
data: { summary: parsed.data.summary, _truncated: true }
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
if (parsed.data?.message) {
|
|
899
|
-
return JSON.stringify({
|
|
900
|
-
success: parsed.success,
|
|
901
|
-
data: { summary: parsed.data.message, _truncated: true }
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
} catch {
|
|
905
|
-
}
|
|
906
|
-
return text.slice(0, keepChars) + `
|
|
907
|
-
...[truncated, original: ${text.length} chars]`;
|
|
908
|
-
}
|
|
909
|
-
function maskOldToolResults(messages, options) {
|
|
910
|
-
const config = options?.config ?? DEFAULT_MASKING_CONFIG;
|
|
911
|
-
const toolRegistry = options?.toolRegistry;
|
|
912
|
-
const iterStart = options?.currentIterationStartIndex;
|
|
913
|
-
const toolResults = messages.map((msg, index) => ({ msg, index })).filter(({ msg }) => msg.role === "toolResult" || isCocoonToolResult(msg));
|
|
914
|
-
const needsMasking = toolResults.length > config.keepRecentCount;
|
|
915
|
-
const needsTruncation = iterStart !== void 0 && config.truncationThreshold > 0;
|
|
916
|
-
if (!needsMasking && !needsTruncation) {
|
|
917
|
-
return messages;
|
|
918
|
-
}
|
|
919
|
-
const result = [...messages];
|
|
920
|
-
if (needsMasking) {
|
|
921
|
-
const toMask = toolResults.slice(0, -config.keepRecentCount);
|
|
922
|
-
for (const { msg, index } of toMask) {
|
|
923
|
-
if (isCocoonToolResult(msg)) {
|
|
924
|
-
result[index] = {
|
|
925
|
-
...msg,
|
|
926
|
-
content: [{ type: "text", text: "[Tool response masked]" }]
|
|
927
|
-
};
|
|
928
|
-
continue;
|
|
929
|
-
}
|
|
930
|
-
const toolMsg = msg;
|
|
931
|
-
if (isExempt(toolMsg, config, toolRegistry)) continue;
|
|
932
|
-
let summaryText = "";
|
|
933
|
-
try {
|
|
934
|
-
const textBlock = toolMsg.content.find((c2) => c2.type === "text");
|
|
935
|
-
if (textBlock) {
|
|
936
|
-
const parsed = JSON.parse(textBlock.text);
|
|
937
|
-
if (parsed.data?.summary) {
|
|
938
|
-
summaryText = ` - ${parsed.data.summary}`;
|
|
939
|
-
} else if (parsed.data?.message) {
|
|
940
|
-
summaryText = ` - ${parsed.data.message}`;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
} catch {
|
|
944
|
-
}
|
|
945
|
-
result[index] = {
|
|
946
|
-
...toolMsg,
|
|
947
|
-
content: [
|
|
948
|
-
{
|
|
949
|
-
type: "text",
|
|
950
|
-
text: `[Tool: ${toolMsg.toolName} - ${toolMsg.isError ? "ERROR" : "OK"}${summaryText}]`
|
|
951
|
-
}
|
|
952
|
-
]
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
if (needsTruncation) {
|
|
957
|
-
const recentResults = needsMasking ? toolResults.slice(-config.keepRecentCount) : toolResults;
|
|
958
|
-
for (const { msg, index } of recentResults) {
|
|
959
|
-
if (index >= iterStart) continue;
|
|
960
|
-
if (isCocoonToolResult(msg)) {
|
|
961
|
-
const userMsg = msg;
|
|
962
|
-
if (!Array.isArray(userMsg.content)) continue;
|
|
963
|
-
const textBlock2 = userMsg.content.find((c2) => c2.type === "text");
|
|
964
|
-
if (textBlock2 && textBlock2.text.length > config.truncationThreshold) {
|
|
965
|
-
result[index] = {
|
|
966
|
-
...userMsg,
|
|
967
|
-
content: [
|
|
968
|
-
{
|
|
969
|
-
type: "text",
|
|
970
|
-
text: truncateToolResult(textBlock2.text, config.truncationKeepChars)
|
|
971
|
-
}
|
|
972
|
-
]
|
|
973
|
-
};
|
|
974
|
-
}
|
|
975
|
-
continue;
|
|
976
|
-
}
|
|
977
|
-
const toolMsg = msg;
|
|
978
|
-
if (isExempt(toolMsg, config, toolRegistry)) continue;
|
|
979
|
-
const textBlock = toolMsg.content.find((c2) => c2.type === "text");
|
|
980
|
-
if (!textBlock || textBlock.text.length <= config.truncationThreshold) continue;
|
|
981
|
-
result[index] = {
|
|
982
|
-
...toolMsg,
|
|
983
|
-
content: [
|
|
984
|
-
{
|
|
985
|
-
type: "text",
|
|
986
|
-
text: truncateToolResult(textBlock.text, config.truncationKeepChars)
|
|
987
|
-
}
|
|
988
|
-
]
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
return result;
|
|
537
|
+
return parts.join("\n");
|
|
993
538
|
}
|
|
994
539
|
|
|
995
|
-
// src/agent/runtime.ts
|
|
996
|
-
var log3 = createLogger("Agent");
|
|
997
|
-
var globalTokenUsage = { totalTokens: 0, totalCost: 0 };
|
|
998
|
-
function getTokenUsage() {
|
|
999
|
-
return { ...globalTokenUsage };
|
|
1000
|
-
}
|
|
1001
|
-
function isContextOverflowError(errorMessage) {
|
|
1002
|
-
if (!errorMessage) return false;
|
|
1003
|
-
const lower = errorMessage.toLowerCase();
|
|
1004
|
-
return lower.includes("prompt is too long") || lower.includes("context length exceeded") || lower.includes("maximum context length") || lower.includes("too many tokens") || lower.includes("request_too_large") || lower.includes("exceeds") && lower.includes("maximum") || lower.includes("context") && lower.includes("limit");
|
|
1005
|
-
}
|
|
1006
|
-
function isTrivialMessage(text) {
|
|
1007
|
-
const stripped = text.trim();
|
|
1008
|
-
if (!stripped) return true;
|
|
1009
|
-
if (!/[a-zA-Z0-9а-яА-ЯёЁ]/.test(stripped)) return true;
|
|
1010
|
-
const trivial = /^(ok|okay|k|oui|non|yes|no|yep|nope|sure|thanks|merci|thx|ty|lol|haha|cool|nice|wow|bravo|top|parfait|d'accord|alright|fine|got it|np|gg)\.?!?$/i;
|
|
1011
|
-
return trivial.test(stripped);
|
|
1012
|
-
}
|
|
1013
|
-
function extractContextSummary(context, maxMessages = 10) {
|
|
1014
|
-
const recentMessages = context.messages.slice(-maxMessages);
|
|
1015
|
-
const summaryParts = [];
|
|
1016
|
-
summaryParts.push("### Session Summary (Auto-saved before overflow reset)\n");
|
|
1017
|
-
for (const msg of recentMessages) {
|
|
1018
|
-
if (msg.role === "user") {
|
|
1019
|
-
const content = typeof msg.content === "string" ? msg.content : "[complex]";
|
|
1020
|
-
const bodyMatch = content.match(/\] (.+)/s);
|
|
1021
|
-
const body = bodyMatch ? bodyMatch[1] : content;
|
|
1022
|
-
summaryParts.push(`- **User**: ${body.substring(0, 150)}${body.length > 150 ? "..." : ""}`);
|
|
1023
|
-
} else if (msg.role === "assistant") {
|
|
1024
|
-
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
1025
|
-
const toolBlocks = msg.content.filter((b) => b.type === "toolCall");
|
|
1026
|
-
if (textBlocks.length > 0) {
|
|
1027
|
-
const text = textBlocks[0].text || "";
|
|
1028
|
-
summaryParts.push(
|
|
1029
|
-
`- **Agent**: ${text.substring(0, 150)}${text.length > 150 ? "..." : ""}`
|
|
1030
|
-
);
|
|
1031
|
-
}
|
|
1032
|
-
if (toolBlocks.length > 0) {
|
|
1033
|
-
const toolNames = toolBlocks.map((b) => b.name).join(", ");
|
|
1034
|
-
summaryParts.push(` - *Tools used: ${toolNames}*`);
|
|
1035
|
-
}
|
|
1036
|
-
} else if (msg.role === "toolResult") {
|
|
1037
|
-
const status = msg.isError ? "ERROR" : "OK";
|
|
1038
|
-
summaryParts.push(` - *Tool result: ${msg.toolName} \u2192 ${status}*`);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
return summaryParts.join("\n");
|
|
1042
|
-
}
|
|
1043
|
-
var AgentRuntime = class {
|
|
1044
|
-
config;
|
|
1045
|
-
soul;
|
|
1046
|
-
compactionManager;
|
|
1047
|
-
contextBuilder = null;
|
|
1048
|
-
toolRegistry = null;
|
|
1049
|
-
embedder = null;
|
|
1050
|
-
hookRunner;
|
|
1051
|
-
userHookEvaluator;
|
|
1052
|
-
constructor(config, soul, toolRegistry) {
|
|
1053
|
-
this.config = config;
|
|
1054
|
-
this.soul = soul ?? "";
|
|
1055
|
-
this.toolRegistry = toolRegistry ?? null;
|
|
1056
|
-
const provider = config.agent.provider || "anthropic";
|
|
1057
|
-
try {
|
|
1058
|
-
const model = getProviderModel(provider, config.agent.model);
|
|
1059
|
-
const ctx = model.contextWindow;
|
|
1060
|
-
this.compactionManager = new CompactionManager({
|
|
1061
|
-
enabled: true,
|
|
1062
|
-
maxMessages: COMPACTION_MAX_MESSAGES,
|
|
1063
|
-
maxTokens: Math.floor(ctx * COMPACTION_MAX_TOKENS_RATIO),
|
|
1064
|
-
keepRecentMessages: COMPACTION_KEEP_RECENT,
|
|
1065
|
-
memoryFlushEnabled: true,
|
|
1066
|
-
softThresholdTokens: Math.floor(ctx * COMPACTION_SOFT_THRESHOLD_RATIO)
|
|
1067
|
-
});
|
|
1068
|
-
} catch {
|
|
1069
|
-
this.compactionManager = new CompactionManager(DEFAULT_COMPACTION_CONFIG);
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
setHookRunner(runner) {
|
|
1073
|
-
this.hookRunner = runner;
|
|
1074
|
-
}
|
|
1075
|
-
setUserHookEvaluator(evaluator) {
|
|
1076
|
-
this.userHookEvaluator = evaluator;
|
|
1077
|
-
}
|
|
1078
|
-
initializeContextBuilder(embedder, vectorEnabled) {
|
|
1079
|
-
this.embedder = embedder;
|
|
1080
|
-
const db = getDatabase().getDb();
|
|
1081
|
-
this.contextBuilder = new ContextBuilder(db, embedder, vectorEnabled);
|
|
1082
|
-
}
|
|
1083
|
-
getToolRegistry() {
|
|
1084
|
-
return this.toolRegistry;
|
|
1085
|
-
}
|
|
1086
|
-
async processMessage(opts) {
|
|
1087
|
-
const {
|
|
1088
|
-
chatId,
|
|
1089
|
-
userMessage,
|
|
1090
|
-
userName,
|
|
1091
|
-
timestamp,
|
|
1092
|
-
isGroup,
|
|
1093
|
-
pendingContext,
|
|
1094
|
-
toolContext,
|
|
1095
|
-
senderUsername,
|
|
1096
|
-
senderRank,
|
|
1097
|
-
hasMedia,
|
|
1098
|
-
mediaType,
|
|
1099
|
-
messageId,
|
|
1100
|
-
replyContext
|
|
1101
|
-
} = opts;
|
|
1102
|
-
const effectiveIsGroup = isGroup ?? false;
|
|
1103
|
-
const processStartTime = Date.now();
|
|
1104
|
-
try {
|
|
1105
|
-
let userHookContext = "";
|
|
1106
|
-
if (this.userHookEvaluator) {
|
|
1107
|
-
const hookResult = this.userHookEvaluator.evaluate(userMessage);
|
|
1108
|
-
if (hookResult.blocked) {
|
|
1109
|
-
log3.info("Message blocked by keyword filter");
|
|
1110
|
-
return { content: hookResult.blockMessage ?? "", toolCalls: [] };
|
|
1111
|
-
}
|
|
1112
|
-
if (hookResult.additionalContext) {
|
|
1113
|
-
userHookContext = sanitizeForContext(hookResult.additionalContext);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
let effectiveMessage = userMessage;
|
|
1117
|
-
let hookMessageContext = "";
|
|
1118
|
-
if (this.hookRunner) {
|
|
1119
|
-
const msgEvent = {
|
|
1120
|
-
chatId,
|
|
1121
|
-
senderId: toolContext?.senderId ? String(toolContext.senderId) : chatId,
|
|
1122
|
-
senderName: userName ?? "",
|
|
1123
|
-
isGroup: effectiveIsGroup,
|
|
1124
|
-
isReply: !!replyContext,
|
|
1125
|
-
replyToMessageId: replyContext ? messageId : void 0,
|
|
1126
|
-
messageId: messageId ?? 0,
|
|
1127
|
-
timestamp: timestamp ?? Date.now(),
|
|
1128
|
-
text: userMessage,
|
|
1129
|
-
block: false,
|
|
1130
|
-
blockReason: "",
|
|
1131
|
-
additionalContext: ""
|
|
1132
|
-
};
|
|
1133
|
-
await this.hookRunner.runModifyingHook("message:receive", msgEvent);
|
|
1134
|
-
if (msgEvent.block) {
|
|
1135
|
-
log3.info(`\u{1F6AB} Message blocked by hook: ${msgEvent.blockReason || "no reason"}`);
|
|
1136
|
-
return { content: "", toolCalls: [] };
|
|
1137
|
-
}
|
|
1138
|
-
effectiveMessage = sanitizeForContext(msgEvent.text);
|
|
1139
|
-
if (msgEvent.additionalContext) {
|
|
1140
|
-
hookMessageContext = sanitizeForContext(msgEvent.additionalContext);
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
let session = getOrCreateSession(chatId);
|
|
1144
|
-
const now = timestamp ?? Date.now();
|
|
1145
|
-
const resetPolicy = this.config.agent.session_reset_policy;
|
|
1146
|
-
if (shouldResetSession(session, resetPolicy)) {
|
|
1147
|
-
log3.info(`\u{1F504} Auto-resetting session based on policy`);
|
|
1148
|
-
if (this.hookRunner) {
|
|
1149
|
-
await this.hookRunner.runObservingHook("session:end", {
|
|
1150
|
-
sessionId: session.sessionId,
|
|
1151
|
-
chatId,
|
|
1152
|
-
messageCount: session.messageCount
|
|
1153
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
if (transcriptExists(session.sessionId)) {
|
|
1156
|
-
try {
|
|
1157
|
-
log3.info(`\u{1F4BE} Saving memory before daily reset...`);
|
|
1158
|
-
const oldContext = loadContextFromTranscript(session.sessionId);
|
|
1159
|
-
await saveSessionMemory({
|
|
1160
|
-
oldSessionId: session.sessionId,
|
|
1161
|
-
newSessionId: "pending",
|
|
1162
|
-
context: oldContext,
|
|
1163
|
-
chatId,
|
|
1164
|
-
apiKey: getEffectiveApiKey(this.config.agent.provider, this.config.agent.api_key),
|
|
1165
|
-
provider: this.config.agent.provider,
|
|
1166
|
-
utilityModel: this.config.agent.utility_model
|
|
1167
|
-
});
|
|
1168
|
-
log3.info(`\u2705 Memory saved before reset`);
|
|
1169
|
-
} catch (error) {
|
|
1170
|
-
log3.warn({ err: error }, `\u26A0\uFE0F Failed to save memory before reset`);
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
session = resetSessionWithPolicy(chatId, resetPolicy);
|
|
1174
|
-
}
|
|
1175
|
-
let context = loadContextFromTranscript(session.sessionId);
|
|
1176
|
-
const isNewSession = context.messages.length === 0;
|
|
1177
|
-
if (!isNewSession) {
|
|
1178
|
-
log3.info(`\u{1F4D6} Loading existing session: ${session.sessionId}`);
|
|
1179
|
-
} else {
|
|
1180
|
-
log3.info(`\u{1F195} Starting new session: ${session.sessionId}`);
|
|
1181
|
-
}
|
|
1182
|
-
if (this.hookRunner) {
|
|
1183
|
-
await this.hookRunner.runObservingHook("session:start", {
|
|
1184
|
-
sessionId: session.sessionId,
|
|
1185
|
-
chatId,
|
|
1186
|
-
isResume: !isNewSession
|
|
1187
|
-
});
|
|
1188
|
-
}
|
|
1189
|
-
const previousTimestamp = session.updatedAt;
|
|
1190
|
-
let formattedMessage = formatMessageEnvelope({
|
|
1191
|
-
channel: "Telegram",
|
|
1192
|
-
senderId: toolContext?.senderId ? String(toolContext.senderId) : chatId,
|
|
1193
|
-
senderName: userName,
|
|
1194
|
-
senderUsername,
|
|
1195
|
-
senderRank,
|
|
1196
|
-
timestamp: now,
|
|
1197
|
-
previousTimestamp,
|
|
1198
|
-
body: effectiveMessage,
|
|
1199
|
-
isGroup: effectiveIsGroup,
|
|
1200
|
-
hasMedia,
|
|
1201
|
-
mediaType,
|
|
1202
|
-
messageId,
|
|
1203
|
-
replyContext
|
|
1204
|
-
});
|
|
1205
|
-
if (pendingContext) {
|
|
1206
|
-
formattedMessage = `${pendingContext}
|
|
1207
|
-
|
|
1208
|
-
${formattedMessage}`;
|
|
1209
|
-
log3.debug(`\u{1F4CB} Including ${pendingContext.split("\n").length - 1} pending messages`);
|
|
1210
|
-
}
|
|
1211
|
-
log3.debug(`\u{1F4E8} Formatted message: ${formattedMessage.substring(0, 100)}...`);
|
|
1212
|
-
const preview = formattedMessage.slice(0, 50).replace(/\n/g, " ");
|
|
1213
|
-
const who = senderUsername ? `@${senderUsername}` : userName;
|
|
1214
|
-
const msgType = isGroup ? `Group ${chatId} ${who}` : `DM ${who}`;
|
|
1215
|
-
log3.info(`\u{1F4E8} ${msgType}: "${preview}${formattedMessage.length > 50 ? "..." : ""}"`);
|
|
1216
|
-
let relevantContext = "";
|
|
1217
|
-
let queryEmbedding;
|
|
1218
|
-
const isNonTrivial = !isTrivialMessage(effectiveMessage);
|
|
1219
|
-
if (this.embedder && isNonTrivial) {
|
|
1220
|
-
try {
|
|
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
|
-
);
|
|
1233
|
-
} catch (error) {
|
|
1234
|
-
log3.warn({ err: error }, "Embedding computation failed");
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
if (this.contextBuilder && isNonTrivial) {
|
|
1238
|
-
try {
|
|
1239
|
-
const dbContext = await this.contextBuilder.buildContext({
|
|
1240
|
-
query: effectiveMessage,
|
|
1241
|
-
chatId,
|
|
1242
|
-
includeAgentMemory: true,
|
|
1243
|
-
includeFeedHistory: true,
|
|
1244
|
-
searchAllChats: !isGroup,
|
|
1245
|
-
maxRecentMessages: CONTEXT_MAX_RECENT_MESSAGES,
|
|
1246
|
-
maxRelevantChunks: CONTEXT_MAX_RELEVANT_CHUNKS,
|
|
1247
|
-
queryEmbedding
|
|
1248
|
-
});
|
|
1249
|
-
const contextParts = [];
|
|
1250
|
-
if (dbContext.relevantKnowledge.length > 0) {
|
|
1251
|
-
const sanitizedKnowledge = dbContext.relevantKnowledge.map(
|
|
1252
|
-
(chunk) => sanitizeForContext(chunk)
|
|
1253
|
-
);
|
|
1254
|
-
contextParts.push(
|
|
1255
|
-
`[Relevant knowledge from memory]
|
|
1256
|
-
${sanitizedKnowledge.join("\n---\n")}`
|
|
1257
|
-
);
|
|
1258
|
-
}
|
|
1259
|
-
if (dbContext.relevantFeed.length > 0) {
|
|
1260
|
-
const sanitizedFeed = dbContext.relevantFeed.map((msg) => sanitizeForContext(msg));
|
|
1261
|
-
contextParts.push(
|
|
1262
|
-
`[Relevant messages from Telegram feed]
|
|
1263
|
-
${sanitizedFeed.join("\n")}`
|
|
1264
|
-
);
|
|
1265
|
-
}
|
|
1266
|
-
if (contextParts.length > 0) {
|
|
1267
|
-
relevantContext = contextParts.join("\n\n");
|
|
1268
|
-
log3.debug(
|
|
1269
|
-
`\u{1F50D} Found ${dbContext.relevantKnowledge.length} knowledge chunks, ${dbContext.relevantFeed.length} feed messages`
|
|
1270
|
-
);
|
|
1271
|
-
}
|
|
1272
|
-
} catch (error) {
|
|
1273
|
-
log3.warn({ err: error }, "Context building failed");
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
const memoryStats = this.getMemoryStats();
|
|
1277
|
-
const statsContext = `[Memory Status: ${memoryStats.totalMessages} messages across ${memoryStats.totalChats} chats, ${memoryStats.knowledgeChunks} knowledge chunks]`;
|
|
1278
|
-
const additionalContext = relevantContext ? `You are in a Telegram conversation with chat ID: ${chatId}. Maintain conversation continuity.
|
|
1279
|
-
|
|
1280
|
-
${statsContext}
|
|
1281
|
-
|
|
1282
|
-
${relevantContext}` : `You are in a Telegram conversation with chat ID: ${chatId}. Maintain conversation continuity.
|
|
1283
|
-
|
|
1284
|
-
${statsContext}`;
|
|
1285
|
-
let hookAdditionalContext = "";
|
|
1286
|
-
if (this.hookRunner) {
|
|
1287
|
-
const promptEvent = {
|
|
1288
|
-
chatId,
|
|
1289
|
-
sessionId: session.sessionId,
|
|
1290
|
-
isGroup: effectiveIsGroup,
|
|
1291
|
-
additionalContext: ""
|
|
1292
|
-
};
|
|
1293
|
-
await this.hookRunner.runModifyingHook("prompt:before", promptEvent);
|
|
1294
|
-
hookAdditionalContext = sanitizeForContext(promptEvent.additionalContext);
|
|
1295
|
-
}
|
|
1296
|
-
const compactionConfig = this.compactionManager.getConfig();
|
|
1297
|
-
const needsMemoryFlush = compactionConfig.enabled && compactionConfig.memoryFlushEnabled && context.messages.length > Math.floor((compactionConfig.maxMessages ?? 200) * 0.75);
|
|
1298
|
-
const allHookContext = [userHookContext, hookAdditionalContext, hookMessageContext].filter(Boolean).join("\n\n");
|
|
1299
|
-
const finalContext = additionalContext + (allHookContext ? `
|
|
1300
|
-
|
|
1301
|
-
${allHookContext}` : "");
|
|
1302
|
-
const systemPrompt = buildSystemPrompt({
|
|
1303
|
-
soul: this.soul,
|
|
1304
|
-
userName,
|
|
1305
|
-
senderUsername,
|
|
1306
|
-
senderId: toolContext?.senderId,
|
|
1307
|
-
ownerName: this.config.telegram.owner_name,
|
|
1308
|
-
ownerUsername: this.config.telegram.owner_username,
|
|
1309
|
-
context: finalContext,
|
|
1310
|
-
includeMemory: !effectiveIsGroup,
|
|
1311
|
-
includeStrategy: !effectiveIsGroup,
|
|
1312
|
-
memoryFlushWarning: needsMemoryFlush
|
|
1313
|
-
});
|
|
1314
|
-
if (this.hookRunner) {
|
|
1315
|
-
const promptAfterEvent = {
|
|
1316
|
-
chatId,
|
|
1317
|
-
sessionId: session.sessionId,
|
|
1318
|
-
isGroup: effectiveIsGroup,
|
|
1319
|
-
promptLength: systemPrompt.length,
|
|
1320
|
-
sectionCount: (systemPrompt.match(/^#{1,3} /gm) || []).length,
|
|
1321
|
-
ragContextLength: relevantContext.length,
|
|
1322
|
-
hookContextLength: allHookContext.length
|
|
1323
|
-
};
|
|
1324
|
-
await this.hookRunner.runObservingHook("prompt:after", promptAfterEvent);
|
|
1325
|
-
}
|
|
1326
|
-
const userMsg = {
|
|
1327
|
-
role: "user",
|
|
1328
|
-
content: formattedMessage,
|
|
1329
|
-
timestamp: now
|
|
1330
|
-
};
|
|
1331
|
-
context.messages.push(userMsg);
|
|
1332
|
-
const preemptiveCompaction = await this.compactionManager.checkAndCompact(
|
|
1333
|
-
session.sessionId,
|
|
1334
|
-
context,
|
|
1335
|
-
getEffectiveApiKey(this.config.agent.provider, this.config.agent.api_key),
|
|
1336
|
-
chatId,
|
|
1337
|
-
this.config.agent.provider,
|
|
1338
|
-
this.config.agent.utility_model
|
|
1339
|
-
);
|
|
1340
|
-
if (preemptiveCompaction) {
|
|
1341
|
-
log3.info(`\u{1F5DC}\uFE0F Preemptive compaction triggered, reloading session...`);
|
|
1342
|
-
session = getSession(chatId);
|
|
1343
|
-
context = loadContextFromTranscript(session.sessionId);
|
|
1344
|
-
context.messages.push(userMsg);
|
|
1345
|
-
}
|
|
1346
|
-
appendToTranscript(session.sessionId, userMsg);
|
|
1347
|
-
const provider = this.config.agent.provider || "anthropic";
|
|
1348
|
-
const providerMeta = getProviderMetadata(provider);
|
|
1349
|
-
const isAdmin = toolContext?.config?.telegram.admin_ids.includes(toolContext.senderId) ?? false;
|
|
1350
|
-
let tools;
|
|
1351
|
-
{
|
|
1352
|
-
const toolIndex = this.toolRegistry?.getToolIndex();
|
|
1353
|
-
const useRAG = toolIndex?.isIndexed && this.config.tool_rag?.enabled !== false && !isTrivialMessage(effectiveMessage) && !(providerMeta.toolLimit === null && this.config.tool_rag?.skip_unlimited_providers !== false);
|
|
1354
|
-
if (useRAG && this.toolRegistry && queryEmbedding) {
|
|
1355
|
-
tools = await this.toolRegistry.getForContextWithRAG(
|
|
1356
|
-
effectiveMessage,
|
|
1357
|
-
queryEmbedding,
|
|
1358
|
-
effectiveIsGroup,
|
|
1359
|
-
providerMeta.toolLimit,
|
|
1360
|
-
chatId,
|
|
1361
|
-
isAdmin
|
|
1362
|
-
);
|
|
1363
|
-
log3.info(`\u{1F50D} Tool RAG: ${tools.length}/${this.toolRegistry.count} tools selected`);
|
|
1364
|
-
} else {
|
|
1365
|
-
tools = this.toolRegistry?.getForContext(
|
|
1366
|
-
effectiveIsGroup,
|
|
1367
|
-
providerMeta.toolLimit,
|
|
1368
|
-
chatId,
|
|
1369
|
-
isAdmin
|
|
1370
|
-
);
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
const maxIterations = this.config.agent.max_agentic_iterations || 5;
|
|
1374
|
-
let iteration = 0;
|
|
1375
|
-
let overflowResets = 0;
|
|
1376
|
-
let rateLimitRetries = 0;
|
|
1377
|
-
let serverErrorRetries = 0;
|
|
1378
|
-
let finalResponse = null;
|
|
1379
|
-
const totalToolCalls = [];
|
|
1380
|
-
const accumulatedTexts = [];
|
|
1381
|
-
const accumulatedUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalCost: 0 };
|
|
1382
|
-
const seenToolSignatures = /* @__PURE__ */ new Set();
|
|
1383
|
-
while (iteration < maxIterations) {
|
|
1384
|
-
iteration++;
|
|
1385
|
-
log3.debug(`\u{1F504} Agentic iteration ${iteration}/${maxIterations}`);
|
|
1386
|
-
const iterationStartIndex = context.messages.length;
|
|
1387
|
-
const maskedMessages = maskOldToolResults(context.messages, {
|
|
1388
|
-
toolRegistry: this.toolRegistry ?? void 0,
|
|
1389
|
-
currentIterationStartIndex: iterationStartIndex
|
|
1390
|
-
});
|
|
1391
|
-
const maskedContext = { ...context, messages: maskedMessages };
|
|
1392
|
-
const response2 = await chatWithContext(this.config.agent, {
|
|
1393
|
-
systemPrompt,
|
|
1394
|
-
context: maskedContext,
|
|
1395
|
-
sessionId: session.sessionId,
|
|
1396
|
-
persistTranscript: true,
|
|
1397
|
-
tools
|
|
1398
|
-
});
|
|
1399
|
-
const assistantMsg = response2.message;
|
|
1400
|
-
if (assistantMsg.stopReason === "error") {
|
|
1401
|
-
const errorMsg = assistantMsg.errorMessage || "";
|
|
1402
|
-
if (this.hookRunner) {
|
|
1403
|
-
const errorCode = errorMsg.includes("429") || errorMsg.toLowerCase().includes("rate") ? "RATE_LIMIT" : isContextOverflowError(errorMsg) ? "CONTEXT_OVERFLOW" : errorMsg.includes("500") || errorMsg.includes("502") || errorMsg.includes("503") ? "PROVIDER_ERROR" : "UNKNOWN";
|
|
1404
|
-
const responseErrorEvent = {
|
|
1405
|
-
chatId,
|
|
1406
|
-
sessionId: session.sessionId,
|
|
1407
|
-
isGroup: effectiveIsGroup,
|
|
1408
|
-
error: errorMsg,
|
|
1409
|
-
errorCode,
|
|
1410
|
-
provider,
|
|
1411
|
-
model: this.config.agent.model,
|
|
1412
|
-
retryCount: rateLimitRetries + serverErrorRetries,
|
|
1413
|
-
durationMs: Date.now() - processStartTime
|
|
1414
|
-
};
|
|
1415
|
-
await this.hookRunner.runObservingHook("response:error", responseErrorEvent);
|
|
1416
|
-
}
|
|
1417
|
-
if (isContextOverflowError(errorMsg)) {
|
|
1418
|
-
overflowResets++;
|
|
1419
|
-
if (overflowResets > 1) {
|
|
1420
|
-
throw new Error(
|
|
1421
|
-
"Context overflow persists after session reset. Message may be too large for the model's context window."
|
|
1422
|
-
);
|
|
1423
|
-
}
|
|
1424
|
-
log3.error(`\u{1F6A8} Context overflow detected: ${errorMsg}`);
|
|
1425
|
-
log3.info(`\u{1F4BE} Saving session memory before reset...`);
|
|
1426
|
-
const summary = extractContextSummary(context, CONTEXT_OVERFLOW_SUMMARY_MESSAGES);
|
|
1427
|
-
appendToDailyLog(summary);
|
|
1428
|
-
log3.info(`\u2705 Memory saved to daily log`);
|
|
1429
|
-
const archived = archiveTranscript(session.sessionId);
|
|
1430
|
-
if (!archived) {
|
|
1431
|
-
log3.error(
|
|
1432
|
-
`\u26A0\uFE0F Failed to archive transcript ${session.sessionId}, proceeding with reset anyway`
|
|
1433
|
-
);
|
|
1434
|
-
}
|
|
1435
|
-
log3.info(`\u{1F504} Resetting session due to context overflow...`);
|
|
1436
|
-
session = resetSession(chatId);
|
|
1437
|
-
context = { messages: [userMsg] };
|
|
1438
|
-
appendToTranscript(session.sessionId, userMsg);
|
|
1439
|
-
log3.info(`\u{1F504} Retrying with fresh context...`);
|
|
1440
|
-
continue;
|
|
1441
|
-
} else if (errorMsg.toLowerCase().includes("rate") || errorMsg.includes("429")) {
|
|
1442
|
-
rateLimitRetries++;
|
|
1443
|
-
if (rateLimitRetries <= RATE_LIMIT_MAX_RETRIES) {
|
|
1444
|
-
const delay = 1e3 * Math.pow(2, rateLimitRetries - 1);
|
|
1445
|
-
log3.warn(
|
|
1446
|
-
`\u{1F6AB} Rate limited, retrying in ${delay}ms (attempt ${rateLimitRetries}/${RATE_LIMIT_MAX_RETRIES})...`
|
|
1447
|
-
);
|
|
1448
|
-
await new Promise((r3) => setTimeout(r3, delay));
|
|
1449
|
-
iteration--;
|
|
1450
|
-
continue;
|
|
1451
|
-
}
|
|
1452
|
-
log3.error(`\u{1F6AB} Rate limited after ${RATE_LIMIT_MAX_RETRIES} retries: ${errorMsg}`);
|
|
1453
|
-
throw new Error(
|
|
1454
|
-
`API rate limited after ${RATE_LIMIT_MAX_RETRIES} retries. Please try again later.`
|
|
1455
|
-
);
|
|
1456
|
-
} else if (errorMsg.includes("500") || errorMsg.includes("502") || errorMsg.includes("503") || errorMsg.includes("529")) {
|
|
1457
|
-
serverErrorRetries++;
|
|
1458
|
-
if (serverErrorRetries <= SERVER_ERROR_MAX_RETRIES) {
|
|
1459
|
-
const delay = 2e3 * Math.pow(2, serverErrorRetries - 1);
|
|
1460
|
-
log3.warn(
|
|
1461
|
-
`\u{1F504} Server error, retrying in ${delay}ms (attempt ${serverErrorRetries}/${SERVER_ERROR_MAX_RETRIES})...`
|
|
1462
|
-
);
|
|
1463
|
-
await new Promise((r3) => setTimeout(r3, delay));
|
|
1464
|
-
iteration--;
|
|
1465
|
-
continue;
|
|
1466
|
-
}
|
|
1467
|
-
log3.error(`\u{1F6A8} Server error after ${SERVER_ERROR_MAX_RETRIES} retries: ${errorMsg}`);
|
|
1468
|
-
throw new Error(
|
|
1469
|
-
`API server error after ${SERVER_ERROR_MAX_RETRIES} retries. The provider may be experiencing issues.`
|
|
1470
|
-
);
|
|
1471
|
-
} else {
|
|
1472
|
-
log3.error(`\u{1F6A8} API error: ${errorMsg}`);
|
|
1473
|
-
throw new Error(`API error: ${errorMsg || "Unknown error"}`);
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
const iterUsage = response2.message.usage;
|
|
1477
|
-
if (iterUsage) {
|
|
1478
|
-
accumulatedUsage.input += iterUsage.input;
|
|
1479
|
-
accumulatedUsage.output += iterUsage.output;
|
|
1480
|
-
accumulatedUsage.cacheRead += iterUsage.cacheRead ?? 0;
|
|
1481
|
-
accumulatedUsage.cacheWrite += iterUsage.cacheWrite ?? 0;
|
|
1482
|
-
accumulatedUsage.totalCost += iterUsage.cost?.total ?? 0;
|
|
1483
|
-
}
|
|
1484
|
-
if (response2.text) {
|
|
1485
|
-
accumulatedTexts.push(response2.text);
|
|
1486
|
-
}
|
|
1487
|
-
const toolCalls = response2.message.content.filter((block) => block.type === "toolCall");
|
|
1488
|
-
if (toolCalls.length === 0) {
|
|
1489
|
-
log3.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 done`);
|
|
1490
|
-
finalResponse = response2;
|
|
1491
|
-
break;
|
|
1492
|
-
}
|
|
1493
|
-
if (!this.toolRegistry || !toolContext) {
|
|
1494
|
-
log3.error("\u26A0\uFE0F Cannot execute tools: registry or context missing");
|
|
1495
|
-
break;
|
|
1496
|
-
}
|
|
1497
|
-
log3.debug(`\u{1F527} Executing ${toolCalls.length} tool call(s)`);
|
|
1498
|
-
context.messages.push(response2.message);
|
|
1499
|
-
const iterationToolNames = [];
|
|
1500
|
-
const fullContext = {
|
|
1501
|
-
...toolContext,
|
|
1502
|
-
chatId,
|
|
1503
|
-
isGroup: effectiveIsGroup
|
|
1504
|
-
};
|
|
1505
|
-
const toolPlans = [];
|
|
1506
|
-
for (const block of toolCalls) {
|
|
1507
|
-
if (block.type !== "toolCall") continue;
|
|
1508
|
-
let toolParams = block.arguments ?? {};
|
|
1509
|
-
let blocked = false;
|
|
1510
|
-
let blockReason = "";
|
|
1511
|
-
if (this.hookRunner) {
|
|
1512
|
-
const beforeEvent = {
|
|
1513
|
-
toolName: block.name,
|
|
1514
|
-
params: structuredClone(toolParams),
|
|
1515
|
-
chatId,
|
|
1516
|
-
isGroup: effectiveIsGroup,
|
|
1517
|
-
block: false,
|
|
1518
|
-
blockReason: ""
|
|
1519
|
-
};
|
|
1520
|
-
await this.hookRunner.runModifyingHook("tool:before", beforeEvent);
|
|
1521
|
-
if (beforeEvent.block) {
|
|
1522
|
-
blocked = true;
|
|
1523
|
-
blockReason = beforeEvent.blockReason || "Blocked by plugin hook";
|
|
1524
|
-
} else {
|
|
1525
|
-
toolParams = structuredClone(beforeEvent.params);
|
|
1526
|
-
}
|
|
1527
|
-
}
|
|
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 }
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
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);
|
|
1580
|
-
}
|
|
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"}`);
|
|
1599
|
-
totalToolCalls.push({
|
|
1600
|
-
name: block.name,
|
|
1601
|
-
input: block.arguments
|
|
1602
|
-
});
|
|
1603
|
-
let resultText = JSON.stringify(exec.result);
|
|
1604
|
-
if (resultText.length > MAX_TOOL_RESULT_SIZE) {
|
|
1605
|
-
log3.warn(`\u26A0\uFE0F Tool result too large (${resultText.length} chars), truncating...`);
|
|
1606
|
-
const data = exec.result.data;
|
|
1607
|
-
if (data?.summary || data?.message) {
|
|
1608
|
-
resultText = JSON.stringify({
|
|
1609
|
-
success: exec.result.success,
|
|
1610
|
-
data: {
|
|
1611
|
-
summary: data.summary || data.message,
|
|
1612
|
-
_truncated: true,
|
|
1613
|
-
_originalSize: resultText.length,
|
|
1614
|
-
_message: "Full data truncated. Use limit parameter for smaller results."
|
|
1615
|
-
}
|
|
1616
|
-
});
|
|
1617
|
-
} else {
|
|
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 });
|
|
1635
|
-
}
|
|
1636
|
-
}
|
|
1637
|
-
if (provider === "cocoon") {
|
|
1638
|
-
const { wrapToolResult } = await import("./tool-adapter-IVX2XQJE.js");
|
|
1639
|
-
const cocoonResultMsg = {
|
|
1640
|
-
role: "user",
|
|
1641
|
-
content: [
|
|
1642
|
-
{
|
|
1643
|
-
type: "text",
|
|
1644
|
-
text: wrapToolResult(resultText)
|
|
1645
|
-
}
|
|
1646
|
-
],
|
|
1647
|
-
timestamp: Date.now()
|
|
1648
|
-
};
|
|
1649
|
-
context.messages.push(cocoonResultMsg);
|
|
1650
|
-
appendToTranscript(session.sessionId, cocoonResultMsg);
|
|
1651
|
-
} else {
|
|
1652
|
-
const toolResultMsg = {
|
|
1653
|
-
role: "toolResult",
|
|
1654
|
-
toolCallId: block.id,
|
|
1655
|
-
toolName: block.name,
|
|
1656
|
-
content: [
|
|
1657
|
-
{
|
|
1658
|
-
type: "text",
|
|
1659
|
-
text: resultText
|
|
1660
|
-
}
|
|
1661
|
-
],
|
|
1662
|
-
isError: !exec.result.success,
|
|
1663
|
-
timestamp: Date.now()
|
|
1664
|
-
};
|
|
1665
|
-
context.messages.push(toolResultMsg);
|
|
1666
|
-
appendToTranscript(session.sessionId, toolResultMsg);
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
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
|
-
}
|
|
1682
|
-
if (iteration === maxIterations) {
|
|
1683
|
-
log3.info(`\u26A0\uFE0F Max iterations reached (${maxIterations})`);
|
|
1684
|
-
finalResponse = response2;
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
if (!finalResponse) {
|
|
1688
|
-
log3.error("\u26A0\uFE0F Agentic loop exited early without final response");
|
|
1689
|
-
return {
|
|
1690
|
-
content: "Internal error: Agent loop failed to produce a response.",
|
|
1691
|
-
toolCalls: []
|
|
1692
|
-
};
|
|
1693
|
-
}
|
|
1694
|
-
const response = finalResponse;
|
|
1695
|
-
const lastMsg = context.messages[context.messages.length - 1];
|
|
1696
|
-
if (lastMsg?.role !== "assistant") {
|
|
1697
|
-
context.messages.push(response.message);
|
|
1698
|
-
}
|
|
1699
|
-
const sessionUpdate = {
|
|
1700
|
-
updatedAt: Date.now(),
|
|
1701
|
-
messageCount: session.messageCount + 1,
|
|
1702
|
-
model: this.config.agent.model,
|
|
1703
|
-
provider: this.config.agent.provider,
|
|
1704
|
-
inputTokens: (session.inputTokens ?? 0) + accumulatedUsage.input + accumulatedUsage.cacheRead + accumulatedUsage.cacheWrite,
|
|
1705
|
-
outputTokens: (session.outputTokens ?? 0) + accumulatedUsage.output
|
|
1706
|
-
};
|
|
1707
|
-
updateSession(chatId, sessionUpdate);
|
|
1708
|
-
if (accumulatedUsage.input > 0 || accumulatedUsage.output > 0) {
|
|
1709
|
-
const u = accumulatedUsage;
|
|
1710
|
-
const totalInput = u.input + u.cacheRead + u.cacheWrite;
|
|
1711
|
-
const inK = (totalInput / 1e3).toFixed(1);
|
|
1712
|
-
const cacheParts = [];
|
|
1713
|
-
if (u.cacheRead) cacheParts.push(`${(u.cacheRead / 1e3).toFixed(1)}K cached`);
|
|
1714
|
-
if (u.cacheWrite) cacheParts.push(`${(u.cacheWrite / 1e3).toFixed(1)}K new`);
|
|
1715
|
-
const cacheInfo = cacheParts.length > 0 ? ` (${cacheParts.join(", ")})` : "";
|
|
1716
|
-
log3.info(`\u{1F4B0} ${inK}K in${cacheInfo}, ${u.output} out | $${u.totalCost.toFixed(3)}`);
|
|
1717
|
-
globalTokenUsage.totalTokens += u.input + u.output + u.cacheRead + u.cacheWrite;
|
|
1718
|
-
globalTokenUsage.totalCost += u.totalCost;
|
|
1719
|
-
}
|
|
1720
|
-
let content = accumulatedTexts.join("\n").trim() || response.text;
|
|
1721
|
-
const usedTelegramSendTool = totalToolCalls.some((tc) => TELEGRAM_SEND_TOOLS.has(tc.name));
|
|
1722
|
-
if (!content && totalToolCalls.length > 0 && !usedTelegramSendTool) {
|
|
1723
|
-
log3.warn("\u26A0\uFE0F Empty response after tool calls - generating fallback");
|
|
1724
|
-
content = "I executed the requested action but couldn't generate a response. Please try again.";
|
|
1725
|
-
} else if (!content && usedTelegramSendTool) {
|
|
1726
|
-
log3.info("\u2705 Response sent via Telegram tool - no additional text needed");
|
|
1727
|
-
content = "";
|
|
1728
|
-
} else if (!content && accumulatedUsage.input === 0 && accumulatedUsage.output === 0) {
|
|
1729
|
-
log3.warn("\u26A0\uFE0F Empty response with zero tokens - possible API issue");
|
|
1730
|
-
content = "I couldn't process your request. Please try again.";
|
|
1731
|
-
}
|
|
1732
|
-
let responseMetadata = {};
|
|
1733
|
-
if (this.hookRunner) {
|
|
1734
|
-
const responseBeforeEvent = {
|
|
1735
|
-
chatId,
|
|
1736
|
-
sessionId: session.sessionId,
|
|
1737
|
-
isGroup: effectiveIsGroup,
|
|
1738
|
-
originalText: content,
|
|
1739
|
-
text: content,
|
|
1740
|
-
block: false,
|
|
1741
|
-
blockReason: "",
|
|
1742
|
-
metadata: {}
|
|
1743
|
-
};
|
|
1744
|
-
await this.hookRunner.runModifyingHook("response:before", responseBeforeEvent);
|
|
1745
|
-
if (responseBeforeEvent.block) {
|
|
1746
|
-
log3.info(
|
|
1747
|
-
`\u{1F6AB} Response blocked by hook: ${responseBeforeEvent.blockReason || "no reason"}`
|
|
1748
|
-
);
|
|
1749
|
-
content = "";
|
|
1750
|
-
} else {
|
|
1751
|
-
content = responseBeforeEvent.text;
|
|
1752
|
-
}
|
|
1753
|
-
responseMetadata = responseBeforeEvent.metadata;
|
|
1754
|
-
}
|
|
1755
|
-
if (this.hookRunner) {
|
|
1756
|
-
const responseAfterEvent = {
|
|
1757
|
-
chatId,
|
|
1758
|
-
sessionId: session.sessionId,
|
|
1759
|
-
isGroup: effectiveIsGroup,
|
|
1760
|
-
text: content,
|
|
1761
|
-
durationMs: Date.now() - processStartTime,
|
|
1762
|
-
toolsUsed: totalToolCalls.map((tc) => tc.name),
|
|
1763
|
-
tokenUsage: accumulatedUsage.input > 0 || accumulatedUsage.output > 0 ? { input: accumulatedUsage.input, output: accumulatedUsage.output } : void 0,
|
|
1764
|
-
metadata: responseMetadata
|
|
1765
|
-
};
|
|
1766
|
-
await this.hookRunner.runObservingHook("response:after", responseAfterEvent);
|
|
1767
|
-
}
|
|
1768
|
-
return {
|
|
1769
|
-
content,
|
|
1770
|
-
toolCalls: totalToolCalls
|
|
1771
|
-
};
|
|
1772
|
-
} catch (error) {
|
|
1773
|
-
log3.error({ err: error }, "Agent error");
|
|
1774
|
-
throw error;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
clearHistory(chatId) {
|
|
1778
|
-
const db = getDatabase().getDb();
|
|
1779
|
-
db.prepare(
|
|
1780
|
-
`DELETE FROM tg_messages_vec WHERE id IN (
|
|
1781
|
-
SELECT id FROM tg_messages WHERE chat_id = ?
|
|
1782
|
-
)`
|
|
1783
|
-
).run(chatId);
|
|
1784
|
-
db.prepare(`DELETE FROM tg_messages WHERE chat_id = ?`).run(chatId);
|
|
1785
|
-
resetSession(chatId);
|
|
1786
|
-
log3.info(`\u{1F5D1}\uFE0F Cleared history for chat ${chatId}`);
|
|
1787
|
-
}
|
|
1788
|
-
getConfig() {
|
|
1789
|
-
return this.config;
|
|
1790
|
-
}
|
|
1791
|
-
getActiveChatIds() {
|
|
1792
|
-
const db = getDatabase().getDb();
|
|
1793
|
-
const rows = db.prepare(
|
|
1794
|
-
`
|
|
1795
|
-
SELECT DISTINCT chat_id
|
|
1796
|
-
FROM tg_messages
|
|
1797
|
-
ORDER BY timestamp DESC
|
|
1798
|
-
`
|
|
1799
|
-
).all();
|
|
1800
|
-
return rows.map((r3) => r3.chat_id);
|
|
1801
|
-
}
|
|
1802
|
-
setSoul(soul) {
|
|
1803
|
-
this.soul = soul;
|
|
1804
|
-
}
|
|
1805
|
-
configureCompaction(config) {
|
|
1806
|
-
this.compactionManager.updateConfig(config);
|
|
1807
|
-
log3.info({ config: this.compactionManager.getConfig() }, `\u{1F5DC}\uFE0F Compaction config updated`);
|
|
1808
|
-
}
|
|
1809
|
-
getCompactionConfig() {
|
|
1810
|
-
return this.compactionManager.getConfig();
|
|
1811
|
-
}
|
|
1812
|
-
_memoryStatsCache = null;
|
|
1813
|
-
getMemoryStats() {
|
|
1814
|
-
const now = Date.now();
|
|
1815
|
-
if (this._memoryStatsCache && now < this._memoryStatsCache.expiry) {
|
|
1816
|
-
return this._memoryStatsCache.data;
|
|
1817
|
-
}
|
|
1818
|
-
const db = getDatabase().getDb();
|
|
1819
|
-
const msgCount = db.prepare(`SELECT COUNT(*) as count FROM tg_messages`).get();
|
|
1820
|
-
const chatCount = db.prepare(`SELECT COUNT(DISTINCT chat_id) as count FROM tg_messages`).get();
|
|
1821
|
-
const knowledgeCount = db.prepare(`SELECT COUNT(*) as count FROM knowledge`).get();
|
|
1822
|
-
const data = {
|
|
1823
|
-
totalMessages: msgCount.count,
|
|
1824
|
-
totalChats: chatCount.count,
|
|
1825
|
-
knowledgeChunks: knowledgeCount.count
|
|
1826
|
-
};
|
|
1827
|
-
this._memoryStatsCache = { data, expiry: now + 5 * 60 * 1e3 };
|
|
1828
|
-
return data;
|
|
1829
|
-
}
|
|
1830
|
-
};
|
|
1831
|
-
|
|
1832
540
|
// src/bot/services/styled-keyboard.ts
|
|
1833
541
|
import { Api } from "telegram";
|
|
1834
542
|
import { InlineKeyboard } from "grammy";
|
|
@@ -2014,7 +722,7 @@ function unescapeHtml(text) {
|
|
|
2014
722
|
}
|
|
2015
723
|
|
|
2016
724
|
// src/bot/inline-router.ts
|
|
2017
|
-
var
|
|
725
|
+
var log2 = createLogger("InlineRouter");
|
|
2018
726
|
var INLINE_TIMEOUT_MS = 5e3;
|
|
2019
727
|
var CALLBACK_TIMEOUT_MS = 15e3;
|
|
2020
728
|
function compileGlob(pattern) {
|
|
@@ -2035,11 +743,11 @@ var InlineRouter = class {
|
|
|
2035
743
|
}
|
|
2036
744
|
registerPlugin(name, handlers) {
|
|
2037
745
|
this.plugins.set(name, handlers);
|
|
2038
|
-
|
|
746
|
+
log2.info(`Registered plugin "${name}" for inline routing`);
|
|
2039
747
|
}
|
|
2040
748
|
unregisterPlugin(name) {
|
|
2041
749
|
this.plugins.delete(name);
|
|
2042
|
-
|
|
750
|
+
log2.info(`Unregistered plugin "${name}" from inline routing`);
|
|
2043
751
|
}
|
|
2044
752
|
hasPlugin(name) {
|
|
2045
753
|
return this.plugins.has(name);
|
|
@@ -2111,7 +819,7 @@ var InlineRouter = class {
|
|
|
2111
819
|
is_personal: true
|
|
2112
820
|
});
|
|
2113
821
|
} catch (error) {
|
|
2114
|
-
|
|
822
|
+
log2.error({ err: error }, `Plugin "${pluginName}" inline query handler failed`);
|
|
2115
823
|
try {
|
|
2116
824
|
await ctx.answerInlineQuery([], { cache_time: 0, is_personal: true });
|
|
2117
825
|
} catch {
|
|
@@ -2171,7 +879,7 @@ var InlineRouter = class {
|
|
|
2171
879
|
} catch (error) {
|
|
2172
880
|
const errMsg = error?.errorMessage;
|
|
2173
881
|
if (errMsg === "MESSAGE_NOT_MODIFIED") return;
|
|
2174
|
-
|
|
882
|
+
log2.debug(`GramJS edit failed, falling back to Grammy: ${errMsg || error}`);
|
|
2175
883
|
}
|
|
2176
884
|
}
|
|
2177
885
|
const replyMarkup = styledButtons ? toGrammyKeyboard(styledButtons) : void 0;
|
|
@@ -2191,7 +899,7 @@ var InlineRouter = class {
|
|
|
2191
899
|
await ctx.answerCallbackQuery();
|
|
2192
900
|
}
|
|
2193
901
|
} catch (error) {
|
|
2194
|
-
|
|
902
|
+
log2.error({ err: error }, `Plugin "${pluginName}" callback handler failed`);
|
|
2195
903
|
if (!answered) {
|
|
2196
904
|
try {
|
|
2197
905
|
await ctx.answerCallbackQuery({ text: "Error processing action" });
|
|
@@ -2214,7 +922,7 @@ var InlineRouter = class {
|
|
|
2214
922
|
};
|
|
2215
923
|
await plugin.onChosenResult(crCtx);
|
|
2216
924
|
} catch (error) {
|
|
2217
|
-
|
|
925
|
+
log2.error({ err: error }, `Plugin "${pluginName}" chosen result handler failed`);
|
|
2218
926
|
}
|
|
2219
927
|
}
|
|
2220
928
|
/**
|
|
@@ -2324,7 +1032,7 @@ import { promisify } from "util";
|
|
|
2324
1032
|
|
|
2325
1033
|
// src/agent/tools/plugin-validator.ts
|
|
2326
1034
|
import { z } from "zod";
|
|
2327
|
-
var
|
|
1035
|
+
var log3 = createLogger("PluginValidator");
|
|
2328
1036
|
var ManifestSchema = z.object({
|
|
2329
1037
|
name: z.string().min(1).max(64).regex(
|
|
2330
1038
|
/^[a-z0-9][a-z0-9-]*$/,
|
|
@@ -2367,20 +1075,20 @@ function validateToolDefs(defs, pluginName) {
|
|
|
2367
1075
|
const valid = [];
|
|
2368
1076
|
for (const def of defs) {
|
|
2369
1077
|
if (!def || typeof def !== "object") {
|
|
2370
|
-
|
|
1078
|
+
log3.warn(`[${pluginName}] tool is not an object, skipping`);
|
|
2371
1079
|
continue;
|
|
2372
1080
|
}
|
|
2373
1081
|
const t = def;
|
|
2374
1082
|
if (!t.name || typeof t.name !== "string") {
|
|
2375
|
-
|
|
1083
|
+
log3.warn(`[${pluginName}] tool missing 'name', skipping`);
|
|
2376
1084
|
continue;
|
|
2377
1085
|
}
|
|
2378
1086
|
if (!t.description || typeof t.description !== "string") {
|
|
2379
|
-
|
|
1087
|
+
log3.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
|
|
2380
1088
|
continue;
|
|
2381
1089
|
}
|
|
2382
1090
|
if (!t.execute || typeof t.execute !== "function") {
|
|
2383
|
-
|
|
1091
|
+
log3.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
|
|
2384
1092
|
continue;
|
|
2385
1093
|
}
|
|
2386
1094
|
valid.push(t);
|
|
@@ -2440,25 +1148,25 @@ function withTxLock(fn) {
|
|
|
2440
1148
|
}
|
|
2441
1149
|
|
|
2442
1150
|
// src/ton/transfer.ts
|
|
2443
|
-
var
|
|
1151
|
+
var log4 = createLogger("TON");
|
|
2444
1152
|
async function sendTon(params) {
|
|
2445
1153
|
return withTxLock(async () => {
|
|
2446
1154
|
try {
|
|
2447
1155
|
const { toAddress: toAddress2, amount, comment = "", bounce = false } = params;
|
|
2448
1156
|
if (!Number.isFinite(amount) || amount <= 0) {
|
|
2449
|
-
|
|
1157
|
+
log4.error({ amount }, "Invalid transfer amount");
|
|
2450
1158
|
return null;
|
|
2451
1159
|
}
|
|
2452
1160
|
let recipientAddress;
|
|
2453
1161
|
try {
|
|
2454
1162
|
recipientAddress = Address.parse(toAddress2);
|
|
2455
1163
|
} catch (e) {
|
|
2456
|
-
|
|
1164
|
+
log4.error({ err: e }, `Invalid recipient address: ${toAddress2}`);
|
|
2457
1165
|
return null;
|
|
2458
1166
|
}
|
|
2459
1167
|
const keyPair = await getKeyPair();
|
|
2460
1168
|
if (!keyPair) {
|
|
2461
|
-
|
|
1169
|
+
log4.error("Wallet not initialized");
|
|
2462
1170
|
return null;
|
|
2463
1171
|
}
|
|
2464
1172
|
const wallet = WalletContractV5R1.create({
|
|
@@ -2482,7 +1190,7 @@ async function sendTon(params) {
|
|
|
2482
1190
|
]
|
|
2483
1191
|
});
|
|
2484
1192
|
const pseudoHash = `${seqno}_${Date.now()}_${amount.toFixed(2)}`;
|
|
2485
|
-
|
|
1193
|
+
log4.info(`Sent ${amount} TON to ${toAddress2.slice(0, 8)}... - seqno: ${seqno}`);
|
|
2486
1194
|
return pseudoHash;
|
|
2487
1195
|
} catch (error) {
|
|
2488
1196
|
const err = error;
|
|
@@ -2490,14 +1198,14 @@ async function sendTon(params) {
|
|
|
2490
1198
|
if (status === 429 || status !== void 0 && status >= 500) {
|
|
2491
1199
|
invalidateTonClientCache();
|
|
2492
1200
|
}
|
|
2493
|
-
|
|
1201
|
+
log4.error({ err: error }, "Error sending TON");
|
|
2494
1202
|
throw error;
|
|
2495
1203
|
}
|
|
2496
1204
|
});
|
|
2497
1205
|
}
|
|
2498
1206
|
|
|
2499
1207
|
// src/utils/retry.ts
|
|
2500
|
-
var
|
|
1208
|
+
var log5 = createLogger("Utils");
|
|
2501
1209
|
var DEFAULT_OPTIONS = {
|
|
2502
1210
|
maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
|
|
2503
1211
|
baseDelayMs: RETRY_DEFAULT_BASE_DELAY_MS,
|
|
@@ -2521,7 +1229,7 @@ async function withRetry(fn, options = {}) {
|
|
|
2521
1229
|
return result;
|
|
2522
1230
|
} catch (error) {
|
|
2523
1231
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
2524
|
-
|
|
1232
|
+
log5.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
|
|
2525
1233
|
if (attempt < opts.maxAttempts) {
|
|
2526
1234
|
const delay = Math.min(opts.baseDelayMs * Math.pow(2, attempt - 1), opts.maxDelayMs);
|
|
2527
1235
|
await sleep(delay);
|
|
@@ -5999,7 +4707,7 @@ var DEDUST_GAS = {
|
|
|
5999
4707
|
var NATIVE_TON_ADDRESS = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
|
|
6000
4708
|
|
|
6001
4709
|
// src/agent/tools/dedust/asset-cache.ts
|
|
6002
|
-
var
|
|
4710
|
+
var log6 = createLogger("Tools");
|
|
6003
4711
|
var ASSET_LIST_URL = "https://assets.dedust.io/list.json";
|
|
6004
4712
|
var CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
6005
4713
|
var cachedAssets = [];
|
|
@@ -6018,7 +4726,7 @@ async function getAssetList() {
|
|
|
6018
4726
|
return cachedAssets;
|
|
6019
4727
|
} catch (error) {
|
|
6020
4728
|
if (cachedAssets.length > 0) {
|
|
6021
|
-
|
|
4729
|
+
log6.warn({ err: error }, "Asset list fetch failed, using stale cache");
|
|
6022
4730
|
return cachedAssets;
|
|
6023
4731
|
}
|
|
6024
4732
|
throw error;
|
|
@@ -6071,7 +4779,7 @@ var stonApiClient = new StonApiClient();
|
|
|
6071
4779
|
function isTon(asset) {
|
|
6072
4780
|
return asset.toLowerCase() === "ton";
|
|
6073
4781
|
}
|
|
6074
|
-
async function getStonfiQuote(fromAsset, toAsset, amount, slippage,
|
|
4782
|
+
async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log10) {
|
|
6075
4783
|
try {
|
|
6076
4784
|
const isTonInput = isTon(fromAsset);
|
|
6077
4785
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6102,11 +4810,11 @@ async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log12) {
|
|
|
6102
4810
|
fee: feeAmount.toFixed(6)
|
|
6103
4811
|
};
|
|
6104
4812
|
} catch (err) {
|
|
6105
|
-
|
|
4813
|
+
log10.debug("dex.quoteSTONfi() failed:", err);
|
|
6106
4814
|
return null;
|
|
6107
4815
|
}
|
|
6108
4816
|
}
|
|
6109
|
-
async function getDedustQuote(fromAsset, toAsset, amount, slippage,
|
|
4817
|
+
async function getDedustQuote(fromAsset, toAsset, amount, slippage, log10) {
|
|
6110
4818
|
try {
|
|
6111
4819
|
const isTonInput = isTon(fromAsset);
|
|
6112
4820
|
const isTonOutput = isTon(toAsset);
|
|
@@ -6140,7 +4848,7 @@ async function getDedustQuote(fromAsset, toAsset, amount, slippage, log12) {
|
|
|
6140
4848
|
poolType
|
|
6141
4849
|
};
|
|
6142
4850
|
} catch (err) {
|
|
6143
|
-
|
|
4851
|
+
log10.debug("dex.quoteDeDust() failed:", err);
|
|
6144
4852
|
return null;
|
|
6145
4853
|
}
|
|
6146
4854
|
}
|
|
@@ -6312,14 +5020,14 @@ function validateDexParams(amount, slippage) {
|
|
|
6312
5020
|
throw new PluginSDKError("Slippage must be between 0 and 1", "OPERATION_FAILED");
|
|
6313
5021
|
}
|
|
6314
5022
|
}
|
|
6315
|
-
function createDexSDK(
|
|
5023
|
+
function createDexSDK(log10) {
|
|
6316
5024
|
return {
|
|
6317
5025
|
async quote(params) {
|
|
6318
5026
|
validateDexParams(params.amount, params.slippage);
|
|
6319
5027
|
const slippage = params.slippage ?? 0.01;
|
|
6320
5028
|
const [stonfi, dedust] = await Promise.all([
|
|
6321
|
-
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
6322
|
-
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage,
|
|
5029
|
+
getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage, log10),
|
|
5030
|
+
getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage, log10)
|
|
6323
5031
|
]);
|
|
6324
5032
|
if (!stonfi && !dedust) {
|
|
6325
5033
|
throw new PluginSDKError("No DEX has liquidity for this pair", "OPERATION_FAILED");
|
|
@@ -6333,7 +5041,14 @@ function createDexSDK(log12) {
|
|
|
6333
5041
|
} else {
|
|
6334
5042
|
const stonfiOut = parseFloat(stonfi.expectedOutput);
|
|
6335
5043
|
const dedustOut = parseFloat(dedust.expectedOutput);
|
|
6336
|
-
if (stonfiOut
|
|
5044
|
+
if (!Number.isFinite(stonfiOut) && !Number.isFinite(dedustOut)) {
|
|
5045
|
+
throw new PluginSDKError("Failed to parse DEX quotes", "OPERATION_FAILED");
|
|
5046
|
+
}
|
|
5047
|
+
if (!Number.isFinite(stonfiOut)) {
|
|
5048
|
+
recommended = "dedust";
|
|
5049
|
+
} else if (!Number.isFinite(dedustOut)) {
|
|
5050
|
+
recommended = "stonfi";
|
|
5051
|
+
} else if (stonfiOut >= dedustOut) {
|
|
6337
5052
|
recommended = "stonfi";
|
|
6338
5053
|
if (dedustOut > 0) {
|
|
6339
5054
|
savings = `${((stonfiOut - dedustOut) / dedustOut * 100).toFixed(2)}%`;
|
|
@@ -6353,7 +5068,7 @@ function createDexSDK(log12) {
|
|
|
6353
5068
|
params.toAsset,
|
|
6354
5069
|
params.amount,
|
|
6355
5070
|
params.slippage ?? 0.01,
|
|
6356
|
-
|
|
5071
|
+
log10
|
|
6357
5072
|
);
|
|
6358
5073
|
},
|
|
6359
5074
|
async quoteDeDust(params) {
|
|
@@ -6362,25 +5077,25 @@ function createDexSDK(log12) {
|
|
|
6362
5077
|
params.toAsset,
|
|
6363
5078
|
params.amount,
|
|
6364
5079
|
params.slippage ?? 0.01,
|
|
6365
|
-
|
|
5080
|
+
log10
|
|
6366
5081
|
);
|
|
6367
5082
|
},
|
|
6368
5083
|
async swap(params) {
|
|
6369
5084
|
validateDexParams(params.amount, params.slippage);
|
|
6370
5085
|
if (params.dex === "stonfi") {
|
|
6371
|
-
return executeSTONfiSwap(params,
|
|
5086
|
+
return executeSTONfiSwap(params, log10);
|
|
6372
5087
|
}
|
|
6373
5088
|
if (params.dex === "dedust") {
|
|
6374
|
-
return executeDedustSwap(params,
|
|
5089
|
+
return executeDedustSwap(params, log10);
|
|
6375
5090
|
}
|
|
6376
5091
|
const quoteResult = await this.quote(params);
|
|
6377
|
-
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params,
|
|
5092
|
+
return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log10) : executeDedustSwap(params, log10);
|
|
6378
5093
|
},
|
|
6379
5094
|
async swapSTONfi(params) {
|
|
6380
|
-
return executeSTONfiSwap(params,
|
|
5095
|
+
return executeSTONfiSwap(params, log10);
|
|
6381
5096
|
},
|
|
6382
5097
|
async swapDeDust(params) {
|
|
6383
|
-
return executeDedustSwap(params,
|
|
5098
|
+
return executeDedustSwap(params, log10);
|
|
6384
5099
|
}
|
|
6385
5100
|
};
|
|
6386
5101
|
}
|
|
@@ -6417,7 +5132,7 @@ function normalizeDomain(domain) {
|
|
|
6417
5132
|
if (!d.endsWith(".ton")) d += ".ton";
|
|
6418
5133
|
return d;
|
|
6419
5134
|
}
|
|
6420
|
-
function createDnsSDK(
|
|
5135
|
+
function createDnsSDK(log10) {
|
|
6421
5136
|
return {
|
|
6422
5137
|
async check(domain) {
|
|
6423
5138
|
const normalized = normalizeDomain(domain);
|
|
@@ -6440,7 +5155,7 @@ function createDnsSDK(log12) {
|
|
|
6440
5155
|
};
|
|
6441
5156
|
} catch (err) {
|
|
6442
5157
|
if (err instanceof PluginSDKError) throw err;
|
|
6443
|
-
|
|
5158
|
+
log10.debug("dns.check() failed:", err);
|
|
6444
5159
|
throw new PluginSDKError(
|
|
6445
5160
|
`Failed to check domain: ${err instanceof Error ? err.message : String(err)}`,
|
|
6446
5161
|
"OPERATION_FAILED"
|
|
@@ -6453,7 +5168,7 @@ function createDnsSDK(log12) {
|
|
|
6453
5168
|
const response = await tonapiFetch(`/dns/${encodeURIComponent(normalized)}`);
|
|
6454
5169
|
if (response.status === 404) return null;
|
|
6455
5170
|
if (!response.ok) {
|
|
6456
|
-
|
|
5171
|
+
log10.debug(`dns.resolve() TonAPI error: ${response.status}`);
|
|
6457
5172
|
return null;
|
|
6458
5173
|
}
|
|
6459
5174
|
const data = await response.json();
|
|
@@ -6465,7 +5180,7 @@ function createDnsSDK(log12) {
|
|
|
6465
5180
|
expirationDate: data.expiring_at || void 0
|
|
6466
5181
|
};
|
|
6467
5182
|
} catch (err) {
|
|
6468
|
-
|
|
5183
|
+
log10.debug("dns.resolve() failed:", err);
|
|
6469
5184
|
return null;
|
|
6470
5185
|
}
|
|
6471
5186
|
},
|
|
@@ -6475,7 +5190,7 @@ function createDnsSDK(log12) {
|
|
|
6475
5190
|
`/dns/auctions?tld=ton&limit=${Math.min(limit ?? 20, 100)}`
|
|
6476
5191
|
);
|
|
6477
5192
|
if (!response.ok) {
|
|
6478
|
-
|
|
5193
|
+
log10.debug(`dns.getAuctions() TonAPI error: ${response.status}`);
|
|
6479
5194
|
return [];
|
|
6480
5195
|
}
|
|
6481
5196
|
const data = await response.json();
|
|
@@ -6488,7 +5203,7 @@ function createDnsSDK(log12) {
|
|
|
6488
5203
|
bids: a.bids || 0
|
|
6489
5204
|
}));
|
|
6490
5205
|
} catch (err) {
|
|
6491
|
-
|
|
5206
|
+
log10.debug("dns.getAuctions() failed:", err);
|
|
6492
5207
|
return [];
|
|
6493
5208
|
}
|
|
6494
5209
|
},
|
|
@@ -6802,25 +5517,25 @@ function findJettonBalance(balances, jettonAddress) {
|
|
|
6802
5517
|
}
|
|
6803
5518
|
});
|
|
6804
5519
|
}
|
|
6805
|
-
function cleanupOldTransactions(db, retentionDays,
|
|
5520
|
+
function cleanupOldTransactions(db, retentionDays, log10) {
|
|
6806
5521
|
if (Math.random() > CLEANUP_PROBABILITY) return;
|
|
6807
5522
|
try {
|
|
6808
5523
|
const cutoff = Math.floor(Date.now() / 1e3) - retentionDays * 24 * 60 * 60;
|
|
6809
5524
|
const result = db.prepare("DELETE FROM used_transactions WHERE used_at < ?").run(cutoff);
|
|
6810
5525
|
if (result.changes > 0) {
|
|
6811
|
-
|
|
5526
|
+
log10.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
|
|
6812
5527
|
}
|
|
6813
5528
|
} catch (err) {
|
|
6814
|
-
|
|
5529
|
+
log10.error("Transaction cleanup failed:", err);
|
|
6815
5530
|
}
|
|
6816
5531
|
}
|
|
6817
|
-
function createTonSDK(
|
|
5532
|
+
function createTonSDK(log10, db) {
|
|
6818
5533
|
return {
|
|
6819
5534
|
getAddress() {
|
|
6820
5535
|
try {
|
|
6821
5536
|
return getWalletAddress();
|
|
6822
5537
|
} catch (err) {
|
|
6823
|
-
|
|
5538
|
+
log10.error("ton.getAddress() failed:", err);
|
|
6824
5539
|
return null;
|
|
6825
5540
|
}
|
|
6826
5541
|
},
|
|
@@ -6830,7 +5545,7 @@ function createTonSDK(log12, db) {
|
|
|
6830
5545
|
if (!addr) return null;
|
|
6831
5546
|
return await getWalletBalance(addr);
|
|
6832
5547
|
} catch (err) {
|
|
6833
|
-
|
|
5548
|
+
log10.error("ton.getBalance() failed:", err);
|
|
6834
5549
|
return null;
|
|
6835
5550
|
}
|
|
6836
5551
|
},
|
|
@@ -6838,7 +5553,7 @@ function createTonSDK(log12, db) {
|
|
|
6838
5553
|
try {
|
|
6839
5554
|
return await getTonPrice();
|
|
6840
5555
|
} catch (err) {
|
|
6841
|
-
|
|
5556
|
+
log10.error("ton.getPrice() failed:", err);
|
|
6842
5557
|
return null;
|
|
6843
5558
|
}
|
|
6844
5559
|
},
|
|
@@ -6889,7 +5604,7 @@ function createTonSDK(log12, db) {
|
|
|
6889
5604
|
);
|
|
6890
5605
|
return formatTransactions(transactions);
|
|
6891
5606
|
} catch (err) {
|
|
6892
|
-
|
|
5607
|
+
log10.error("ton.getTransactions() failed:", err);
|
|
6893
5608
|
return [];
|
|
6894
5609
|
}
|
|
6895
5610
|
},
|
|
@@ -6912,7 +5627,7 @@ function createTonSDK(log12, db) {
|
|
|
6912
5627
|
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
6913
5628
|
}
|
|
6914
5629
|
const maxAgeMinutes = params.maxAgeMinutes ?? DEFAULT_MAX_AGE_MINUTES;
|
|
6915
|
-
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS,
|
|
5630
|
+
cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log10);
|
|
6916
5631
|
try {
|
|
6917
5632
|
const txs = await this.getTransactions(address4, 20);
|
|
6918
5633
|
for (const tx of txs) {
|
|
@@ -6946,7 +5661,7 @@ function createTonSDK(log12, db) {
|
|
|
6946
5661
|
};
|
|
6947
5662
|
} catch (err) {
|
|
6948
5663
|
if (err instanceof PluginSDKError) throw err;
|
|
6949
|
-
|
|
5664
|
+
log10.error("ton.verifyPayment() failed:", err);
|
|
6950
5665
|
return {
|
|
6951
5666
|
verified: false,
|
|
6952
5667
|
error: `Verification failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -6960,7 +5675,7 @@ function createTonSDK(log12, db) {
|
|
|
6960
5675
|
if (!addr) return [];
|
|
6961
5676
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(addr)}/jettons`);
|
|
6962
5677
|
if (!response.ok) {
|
|
6963
|
-
|
|
5678
|
+
log10.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
|
|
6964
5679
|
return [];
|
|
6965
5680
|
}
|
|
6966
5681
|
const data = await response.json();
|
|
@@ -6984,7 +5699,7 @@ function createTonSDK(log12, db) {
|
|
|
6984
5699
|
}
|
|
6985
5700
|
return balances;
|
|
6986
5701
|
} catch (err) {
|
|
6987
|
-
|
|
5702
|
+
log10.error("ton.getJettonBalances() failed:", err);
|
|
6988
5703
|
return [];
|
|
6989
5704
|
}
|
|
6990
5705
|
},
|
|
@@ -6993,7 +5708,7 @@ function createTonSDK(log12, db) {
|
|
|
6993
5708
|
const response = await tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`);
|
|
6994
5709
|
if (response.status === 404) return null;
|
|
6995
5710
|
if (!response.ok) {
|
|
6996
|
-
|
|
5711
|
+
log10.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
|
|
6997
5712
|
return null;
|
|
6998
5713
|
}
|
|
6999
5714
|
const data = await response.json();
|
|
@@ -7011,7 +5726,7 @@ function createTonSDK(log12, db) {
|
|
|
7011
5726
|
image: data.preview || metadata.image || void 0
|
|
7012
5727
|
};
|
|
7013
5728
|
} catch (err) {
|
|
7014
|
-
|
|
5729
|
+
log10.error("ton.getJettonInfo() failed:", err);
|
|
7015
5730
|
return null;
|
|
7016
5731
|
}
|
|
7017
5732
|
},
|
|
@@ -7104,7 +5819,7 @@ function createTonSDK(log12, db) {
|
|
|
7104
5819
|
if (status === 429 || status && status >= 500) {
|
|
7105
5820
|
invalidateTonClientCache();
|
|
7106
5821
|
if (attempt < MAX_SEND_ATTEMPTS) {
|
|
7107
|
-
|
|
5822
|
+
log10.warn(
|
|
7108
5823
|
`sendJetton attempt ${attempt} failed (${status}): ${JSON.stringify(respData ?? err.message)}, retrying...`
|
|
7109
5824
|
);
|
|
7110
5825
|
await new Promise((r3) => setTimeout(r3, 1e3 * attempt));
|
|
@@ -7134,14 +5849,14 @@ function createTonSDK(log12, db) {
|
|
|
7134
5849
|
try {
|
|
7135
5850
|
const response = await tonapiFetch(`/accounts/${encodeURIComponent(ownerAddress)}/jettons`);
|
|
7136
5851
|
if (!response.ok) {
|
|
7137
|
-
|
|
5852
|
+
log10.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
|
|
7138
5853
|
return null;
|
|
7139
5854
|
}
|
|
7140
5855
|
const data = await response.json();
|
|
7141
5856
|
const match = findJettonBalance(data.balances ?? [], jettonAddress);
|
|
7142
5857
|
return match ? match.wallet_address.address : null;
|
|
7143
5858
|
} catch (err) {
|
|
7144
|
-
|
|
5859
|
+
log10.error("ton.getJettonWalletAddress() failed:", err);
|
|
7145
5860
|
return null;
|
|
7146
5861
|
}
|
|
7147
5862
|
},
|
|
@@ -7164,7 +5879,7 @@ function createTonSDK(log12, db) {
|
|
|
7164
5879
|
if (!keyPair) {
|
|
7165
5880
|
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
7166
5881
|
}
|
|
7167
|
-
const
|
|
5882
|
+
const txResult = await withTxLock(async () => {
|
|
7168
5883
|
const wallet = WalletContractV5R14.create({
|
|
7169
5884
|
workchain: 0,
|
|
7170
5885
|
publicKey: keyPair.publicKey
|
|
@@ -7172,8 +5887,10 @@ function createTonSDK(log12, db) {
|
|
|
7172
5887
|
const client = await getCachedTonClient();
|
|
7173
5888
|
const contract = client.open(wallet);
|
|
7174
5889
|
const seqno = await contract.getSeqno();
|
|
5890
|
+
const validUntil = Math.floor(Date.now() / 1e3) + 120;
|
|
7175
5891
|
const transferCell = wallet.createTransfer({
|
|
7176
5892
|
seqno,
|
|
5893
|
+
timeout: validUntil,
|
|
7177
5894
|
secretKey: keyPair.secretKey,
|
|
7178
5895
|
sendMode: SendMode4.PAY_GAS_SEPARATELY,
|
|
7179
5896
|
messages: [
|
|
@@ -7196,10 +5913,18 @@ function createTonSDK(log12, db) {
|
|
|
7196
5913
|
body: transferCell
|
|
7197
5914
|
})
|
|
7198
5915
|
).endCell();
|
|
7199
|
-
|
|
5916
|
+
const boc = extMsg.toBoc().toString("base64");
|
|
5917
|
+
return { boc, seqno, validUntil, walletAddress: wallet.address.toRawString() };
|
|
7200
5918
|
});
|
|
7201
5919
|
return {
|
|
7202
|
-
|
|
5920
|
+
// v2 fields
|
|
5921
|
+
signedBoc: txResult.boc,
|
|
5922
|
+
walletPublicKey: walletData.publicKey,
|
|
5923
|
+
walletAddress: txResult.walletAddress,
|
|
5924
|
+
seqno: txResult.seqno,
|
|
5925
|
+
validUntil: txResult.validUntil,
|
|
5926
|
+
// backward compat (deprecated)
|
|
5927
|
+
boc: txResult.boc,
|
|
7203
5928
|
publicKey: walletData.publicKey,
|
|
7204
5929
|
walletVersion: "v5r1"
|
|
7205
5930
|
};
|
|
@@ -7266,7 +5991,7 @@ function createTonSDK(log12, db) {
|
|
|
7266
5991
|
if (!keyPair) {
|
|
7267
5992
|
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
7268
5993
|
}
|
|
7269
|
-
const
|
|
5994
|
+
const txResult = await withTxLock(async () => {
|
|
7270
5995
|
const wallet = WalletContractV5R14.create({
|
|
7271
5996
|
workchain: 0,
|
|
7272
5997
|
publicKey: keyPair.publicKey
|
|
@@ -7274,8 +5999,10 @@ function createTonSDK(log12, db) {
|
|
|
7274
5999
|
const client = await getCachedTonClient();
|
|
7275
6000
|
const walletContract = client.open(wallet);
|
|
7276
6001
|
const seqno = await walletContract.getSeqno();
|
|
6002
|
+
const validUntil = Math.floor(Date.now() / 1e3) + 120;
|
|
7277
6003
|
const transferCell = wallet.createTransfer({
|
|
7278
6004
|
seqno,
|
|
6005
|
+
timeout: validUntil,
|
|
7279
6006
|
secretKey: keyPair.secretKey,
|
|
7280
6007
|
sendMode: SendMode4.PAY_GAS_SEPARATELY,
|
|
7281
6008
|
messages: [
|
|
@@ -7298,10 +6025,18 @@ function createTonSDK(log12, db) {
|
|
|
7298
6025
|
body: transferCell
|
|
7299
6026
|
})
|
|
7300
6027
|
).endCell();
|
|
7301
|
-
|
|
6028
|
+
const boc = extMsg.toBoc().toString("base64");
|
|
6029
|
+
return { boc, seqno, validUntil, walletAddress: wallet.address.toRawString() };
|
|
7302
6030
|
});
|
|
7303
6031
|
return {
|
|
7304
|
-
|
|
6032
|
+
// v2 fields
|
|
6033
|
+
signedBoc: txResult.boc,
|
|
6034
|
+
walletPublicKey: walletData.publicKey,
|
|
6035
|
+
walletAddress: txResult.walletAddress,
|
|
6036
|
+
seqno: txResult.seqno,
|
|
6037
|
+
validUntil: txResult.validUntil,
|
|
6038
|
+
// backward compat (deprecated)
|
|
6039
|
+
boc: txResult.boc,
|
|
7305
6040
|
publicKey: walletData.publicKey,
|
|
7306
6041
|
walletVersion: "v5r1"
|
|
7307
6042
|
};
|
|
@@ -7318,7 +6053,7 @@ function createTonSDK(log12, db) {
|
|
|
7318
6053
|
const wallet = loadWallet();
|
|
7319
6054
|
return wallet?.publicKey ?? null;
|
|
7320
6055
|
} catch (err) {
|
|
7321
|
-
|
|
6056
|
+
log10.error("ton.getPublicKey() failed:", err);
|
|
7322
6057
|
return null;
|
|
7323
6058
|
}
|
|
7324
6059
|
},
|
|
@@ -7334,14 +6069,14 @@ function createTonSDK(log12, db) {
|
|
|
7334
6069
|
`/accounts/${encodeURIComponent(addr)}/nfts?limit=100&indirect_ownership=true`
|
|
7335
6070
|
);
|
|
7336
6071
|
if (!response.ok) {
|
|
7337
|
-
|
|
6072
|
+
log10.error(`ton.getNftItems() TonAPI error: ${response.status}`);
|
|
7338
6073
|
return [];
|
|
7339
6074
|
}
|
|
7340
6075
|
const data = await response.json();
|
|
7341
6076
|
if (!Array.isArray(data.nft_items)) return [];
|
|
7342
6077
|
return data.nft_items.filter((item) => item.trust !== "blacklist").map((item) => mapNftItem(item));
|
|
7343
6078
|
} catch (err) {
|
|
7344
|
-
|
|
6079
|
+
log10.error("ton.getNftItems() failed:", err);
|
|
7345
6080
|
return [];
|
|
7346
6081
|
}
|
|
7347
6082
|
},
|
|
@@ -7350,13 +6085,13 @@ function createTonSDK(log12, db) {
|
|
|
7350
6085
|
const response = await tonapiFetch(`/nfts/${encodeURIComponent(nftAddress)}`);
|
|
7351
6086
|
if (response.status === 404) return null;
|
|
7352
6087
|
if (!response.ok) {
|
|
7353
|
-
|
|
6088
|
+
log10.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
|
|
7354
6089
|
return null;
|
|
7355
6090
|
}
|
|
7356
6091
|
const item = await response.json();
|
|
7357
6092
|
return mapNftItem(item);
|
|
7358
6093
|
} catch (err) {
|
|
7359
|
-
|
|
6094
|
+
log10.error("ton.getNftInfo() failed:", err);
|
|
7360
6095
|
return null;
|
|
7361
6096
|
}
|
|
7362
6097
|
},
|
|
@@ -7389,7 +6124,7 @@ function createTonSDK(log12, db) {
|
|
|
7389
6124
|
`/rates?tokens=${encodeURIComponent(jettonAddress)}¤cies=usd,ton`
|
|
7390
6125
|
);
|
|
7391
6126
|
if (!response.ok) {
|
|
7392
|
-
|
|
6127
|
+
log10.debug(`ton.getJettonPrice() TonAPI error: ${response.status}`);
|
|
7393
6128
|
return null;
|
|
7394
6129
|
}
|
|
7395
6130
|
const data = await response.json();
|
|
@@ -7403,7 +6138,7 @@ function createTonSDK(log12, db) {
|
|
|
7403
6138
|
change30d: rateData.diff_30d?.USD ?? null
|
|
7404
6139
|
};
|
|
7405
6140
|
} catch (err) {
|
|
7406
|
-
|
|
6141
|
+
log10.debug("ton.getJettonPrice() failed:", err);
|
|
7407
6142
|
return null;
|
|
7408
6143
|
}
|
|
7409
6144
|
},
|
|
@@ -7417,7 +6152,7 @@ function createTonSDK(log12, db) {
|
|
|
7417
6152
|
tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`)
|
|
7418
6153
|
]);
|
|
7419
6154
|
if (!holdersResponse.ok) {
|
|
7420
|
-
|
|
6155
|
+
log10.debug(`ton.getJettonHolders() TonAPI error: ${holdersResponse.status}`);
|
|
7421
6156
|
return [];
|
|
7422
6157
|
}
|
|
7423
6158
|
const data = await holdersResponse.json();
|
|
@@ -7437,7 +6172,7 @@ function createTonSDK(log12, db) {
|
|
|
7437
6172
|
};
|
|
7438
6173
|
});
|
|
7439
6174
|
} catch (err) {
|
|
7440
|
-
|
|
6175
|
+
log10.debug("ton.getJettonHolders() failed:", err);
|
|
7441
6176
|
return [];
|
|
7442
6177
|
}
|
|
7443
6178
|
},
|
|
@@ -7482,15 +6217,13 @@ function createTonSDK(log12, db) {
|
|
|
7482
6217
|
const geckoData = await geckoResponse.json();
|
|
7483
6218
|
const attrs = geckoData.data?.attributes;
|
|
7484
6219
|
if (attrs) {
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
if (attrs.market_cap_usd)
|
|
7492
|
-
marketCap = `$${parseFloat(attrs.market_cap_usd).toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
|
|
7493
|
-
}
|
|
6220
|
+
const fmtUsd = (raw) => {
|
|
6221
|
+
const val = parseFloat(raw);
|
|
6222
|
+
return Number.isFinite(val) ? `$${val.toLocaleString(void 0, { maximumFractionDigits: 0 })}` : "N/A";
|
|
6223
|
+
};
|
|
6224
|
+
if (attrs.volume_usd?.h24) volume24h = fmtUsd(attrs.volume_usd.h24);
|
|
6225
|
+
if (attrs.fdv_usd) fdv = fmtUsd(attrs.fdv_usd);
|
|
6226
|
+
if (attrs.market_cap_usd) marketCap = fmtUsd(attrs.market_cap_usd);
|
|
7494
6227
|
}
|
|
7495
6228
|
}
|
|
7496
6229
|
return {
|
|
@@ -7509,13 +6242,251 @@ function createTonSDK(log12, db) {
|
|
|
7509
6242
|
holders: holdersCount
|
|
7510
6243
|
};
|
|
7511
6244
|
} catch (err) {
|
|
7512
|
-
|
|
6245
|
+
log10.debug("ton.getJettonHistory() failed:", err);
|
|
7513
6246
|
return null;
|
|
7514
6247
|
}
|
|
7515
6248
|
},
|
|
6249
|
+
// ─── Low-level Transfer ─────────────────────────────────────────
|
|
6250
|
+
async getSeqno() {
|
|
6251
|
+
const walletData = loadWallet();
|
|
6252
|
+
if (!walletData) {
|
|
6253
|
+
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
6254
|
+
}
|
|
6255
|
+
try {
|
|
6256
|
+
const keyPair = await getKeyPair();
|
|
6257
|
+
if (!keyPair) {
|
|
6258
|
+
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
6259
|
+
}
|
|
6260
|
+
const wallet = WalletContractV5R14.create({
|
|
6261
|
+
workchain: 0,
|
|
6262
|
+
publicKey: keyPair.publicKey
|
|
6263
|
+
});
|
|
6264
|
+
const client = await getCachedTonClient();
|
|
6265
|
+
const contract = client.open(wallet);
|
|
6266
|
+
return await contract.getSeqno();
|
|
6267
|
+
} catch (err) {
|
|
6268
|
+
const httpErr = isHttpError(err) ? err : void 0;
|
|
6269
|
+
const status = httpErr?.status || httpErr?.response?.status;
|
|
6270
|
+
if (status === 429 || status !== void 0 && status >= 500) {
|
|
6271
|
+
invalidateTonClientCache();
|
|
6272
|
+
}
|
|
6273
|
+
if (err instanceof PluginSDKError) throw err;
|
|
6274
|
+
throw new PluginSDKError(
|
|
6275
|
+
`Failed to get seqno: ${err instanceof Error ? err.message : String(err)}`,
|
|
6276
|
+
"OPERATION_FAILED"
|
|
6277
|
+
);
|
|
6278
|
+
}
|
|
6279
|
+
},
|
|
6280
|
+
async runGetMethod(address4, method, stack) {
|
|
6281
|
+
try {
|
|
6282
|
+
TonAddress.parse(address4);
|
|
6283
|
+
} catch {
|
|
6284
|
+
throw new PluginSDKError("Invalid TON address format", "INVALID_ADDRESS");
|
|
6285
|
+
}
|
|
6286
|
+
try {
|
|
6287
|
+
const client = await getCachedTonClient();
|
|
6288
|
+
const result = await client.runMethodWithError(
|
|
6289
|
+
TonAddress.parse(address4),
|
|
6290
|
+
method,
|
|
6291
|
+
stack ?? []
|
|
6292
|
+
);
|
|
6293
|
+
const items = [];
|
|
6294
|
+
while (result.stack.remaining > 0) {
|
|
6295
|
+
items.push(result.stack.pop());
|
|
6296
|
+
}
|
|
6297
|
+
return { exitCode: result.exit_code, stack: items };
|
|
6298
|
+
} catch (err) {
|
|
6299
|
+
const httpErr = isHttpError(err) ? err : void 0;
|
|
6300
|
+
const status = httpErr?.status || httpErr?.response?.status;
|
|
6301
|
+
if (status === 429 || status !== void 0 && status >= 500) {
|
|
6302
|
+
invalidateTonClientCache();
|
|
6303
|
+
}
|
|
6304
|
+
if (err instanceof PluginSDKError) throw err;
|
|
6305
|
+
throw new PluginSDKError(
|
|
6306
|
+
`Failed to run get method: ${err instanceof Error ? err.message : String(err)}`,
|
|
6307
|
+
"OPERATION_FAILED"
|
|
6308
|
+
);
|
|
6309
|
+
}
|
|
6310
|
+
},
|
|
6311
|
+
async send(to, value, opts) {
|
|
6312
|
+
const walletData = loadWallet();
|
|
6313
|
+
if (!walletData) {
|
|
6314
|
+
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
6315
|
+
}
|
|
6316
|
+
try {
|
|
6317
|
+
TonAddress.parse(to);
|
|
6318
|
+
} catch {
|
|
6319
|
+
throw new PluginSDKError("Invalid TON address format", "INVALID_ADDRESS");
|
|
6320
|
+
}
|
|
6321
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
6322
|
+
throw new PluginSDKError("Amount must be a non-negative number", "OPERATION_FAILED");
|
|
6323
|
+
}
|
|
6324
|
+
if (opts?.sendMode !== void 0 && (opts.sendMode < 0 || opts.sendMode > 3)) {
|
|
6325
|
+
throw new PluginSDKError("Unsafe sendMode", "OPERATION_FAILED");
|
|
6326
|
+
}
|
|
6327
|
+
const keyPair = await getKeyPair();
|
|
6328
|
+
if (!keyPair) {
|
|
6329
|
+
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
6330
|
+
}
|
|
6331
|
+
try {
|
|
6332
|
+
const seqno = await withTxLock(async () => {
|
|
6333
|
+
const wallet = WalletContractV5R14.create({
|
|
6334
|
+
workchain: 0,
|
|
6335
|
+
publicKey: keyPair.publicKey
|
|
6336
|
+
});
|
|
6337
|
+
const client = await getCachedTonClient();
|
|
6338
|
+
const contract = client.open(wallet);
|
|
6339
|
+
const seq = await contract.getSeqno();
|
|
6340
|
+
await contract.sendTransfer({
|
|
6341
|
+
seqno: seq,
|
|
6342
|
+
secretKey: keyPair.secretKey,
|
|
6343
|
+
sendMode: opts?.sendMode ?? SendMode4.PAY_GAS_SEPARATELY,
|
|
6344
|
+
messages: [
|
|
6345
|
+
internal4({
|
|
6346
|
+
to: TonAddress.parse(to),
|
|
6347
|
+
value: tonToNano(value.toString()),
|
|
6348
|
+
body: opts?.body,
|
|
6349
|
+
bounce: opts?.bounce ?? true,
|
|
6350
|
+
init: opts?.stateInit
|
|
6351
|
+
})
|
|
6352
|
+
]
|
|
6353
|
+
});
|
|
6354
|
+
return seq;
|
|
6355
|
+
});
|
|
6356
|
+
return { hash: `${seqno}_${Date.now()}_send`, seqno };
|
|
6357
|
+
} catch (err) {
|
|
6358
|
+
const httpErr = isHttpError(err) ? err : void 0;
|
|
6359
|
+
const status = httpErr?.status || httpErr?.response?.status;
|
|
6360
|
+
if (status === 429 || status !== void 0 && status >= 500) {
|
|
6361
|
+
invalidateTonClientCache();
|
|
6362
|
+
}
|
|
6363
|
+
if (err instanceof PluginSDKError) throw err;
|
|
6364
|
+
throw new PluginSDKError(
|
|
6365
|
+
`Failed to send: ${err instanceof Error ? err.message : String(err)}`,
|
|
6366
|
+
"OPERATION_FAILED"
|
|
6367
|
+
);
|
|
6368
|
+
}
|
|
6369
|
+
},
|
|
6370
|
+
async sendMessages(messages, opts) {
|
|
6371
|
+
const walletData = loadWallet();
|
|
6372
|
+
if (!walletData) {
|
|
6373
|
+
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
6374
|
+
}
|
|
6375
|
+
if (messages.length < 1) {
|
|
6376
|
+
throw new PluginSDKError("At least one message required", "OPERATION_FAILED");
|
|
6377
|
+
}
|
|
6378
|
+
if (messages.length > 255) {
|
|
6379
|
+
throw new PluginSDKError("Maximum 255 messages per transfer", "OPERATION_FAILED");
|
|
6380
|
+
}
|
|
6381
|
+
if (opts?.sendMode !== void 0 && (opts.sendMode < 0 || opts.sendMode > 3)) {
|
|
6382
|
+
throw new PluginSDKError("Unsafe sendMode", "OPERATION_FAILED");
|
|
6383
|
+
}
|
|
6384
|
+
for (const m of messages) {
|
|
6385
|
+
try {
|
|
6386
|
+
TonAddress.parse(m.to);
|
|
6387
|
+
} catch {
|
|
6388
|
+
throw new PluginSDKError(`Invalid TON address format: ${m.to}`, "INVALID_ADDRESS");
|
|
6389
|
+
}
|
|
6390
|
+
if (!Number.isFinite(m.value) || m.value < 0) {
|
|
6391
|
+
throw new PluginSDKError("Amount must be a non-negative number", "OPERATION_FAILED");
|
|
6392
|
+
}
|
|
6393
|
+
}
|
|
6394
|
+
const keyPair = await getKeyPair();
|
|
6395
|
+
if (!keyPair) {
|
|
6396
|
+
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
6397
|
+
}
|
|
6398
|
+
try {
|
|
6399
|
+
const seqno = await withTxLock(async () => {
|
|
6400
|
+
const wallet = WalletContractV5R14.create({
|
|
6401
|
+
workchain: 0,
|
|
6402
|
+
publicKey: keyPair.publicKey
|
|
6403
|
+
});
|
|
6404
|
+
const client = await getCachedTonClient();
|
|
6405
|
+
const contract = client.open(wallet);
|
|
6406
|
+
const seq = await contract.getSeqno();
|
|
6407
|
+
await contract.sendTransfer({
|
|
6408
|
+
seqno: seq,
|
|
6409
|
+
secretKey: keyPair.secretKey,
|
|
6410
|
+
sendMode: opts?.sendMode ?? SendMode4.PAY_GAS_SEPARATELY,
|
|
6411
|
+
messages: messages.map(
|
|
6412
|
+
(m) => internal4({
|
|
6413
|
+
to: TonAddress.parse(m.to),
|
|
6414
|
+
value: tonToNano(m.value.toString()),
|
|
6415
|
+
body: m.body,
|
|
6416
|
+
bounce: m.bounce ?? true,
|
|
6417
|
+
init: m.stateInit
|
|
6418
|
+
})
|
|
6419
|
+
)
|
|
6420
|
+
});
|
|
6421
|
+
return seq;
|
|
6422
|
+
});
|
|
6423
|
+
return { hash: `${seqno}_${Date.now()}_sendMessages`, seqno };
|
|
6424
|
+
} catch (err) {
|
|
6425
|
+
const httpErr = isHttpError(err) ? err : void 0;
|
|
6426
|
+
const status = httpErr?.status || httpErr?.response?.status;
|
|
6427
|
+
if (status === 429 || status !== void 0 && status >= 500) {
|
|
6428
|
+
invalidateTonClientCache();
|
|
6429
|
+
}
|
|
6430
|
+
if (err instanceof PluginSDKError) throw err;
|
|
6431
|
+
throw new PluginSDKError(
|
|
6432
|
+
`Failed to send messages: ${err instanceof Error ? err.message : String(err)}`,
|
|
6433
|
+
"OPERATION_FAILED"
|
|
6434
|
+
);
|
|
6435
|
+
}
|
|
6436
|
+
},
|
|
6437
|
+
async createSender() {
|
|
6438
|
+
const walletData = loadWallet();
|
|
6439
|
+
if (!walletData) {
|
|
6440
|
+
throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
|
|
6441
|
+
}
|
|
6442
|
+
const keyPair = await getKeyPair();
|
|
6443
|
+
if (!keyPair) {
|
|
6444
|
+
throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
|
|
6445
|
+
}
|
|
6446
|
+
const wallet = WalletContractV5R14.create({
|
|
6447
|
+
workchain: 0,
|
|
6448
|
+
publicKey: keyPair.publicKey
|
|
6449
|
+
});
|
|
6450
|
+
return {
|
|
6451
|
+
address: wallet.address,
|
|
6452
|
+
send: async (args) => {
|
|
6453
|
+
if (args.sendMode !== void 0 && (args.sendMode < 0 || args.sendMode > 3)) {
|
|
6454
|
+
throw new PluginSDKError("Unsafe sendMode", "OPERATION_FAILED");
|
|
6455
|
+
}
|
|
6456
|
+
try {
|
|
6457
|
+
await withTxLock(async () => {
|
|
6458
|
+
const client = await getCachedTonClient();
|
|
6459
|
+
const contract = client.open(wallet);
|
|
6460
|
+
const seqno = await contract.getSeqno();
|
|
6461
|
+
await contract.sendTransfer({
|
|
6462
|
+
seqno,
|
|
6463
|
+
secretKey: keyPair.secretKey,
|
|
6464
|
+
sendMode: args.sendMode ?? SendMode4.PAY_GAS_SEPARATELY,
|
|
6465
|
+
messages: [
|
|
6466
|
+
internal4({
|
|
6467
|
+
to: args.to,
|
|
6468
|
+
value: args.value,
|
|
6469
|
+
body: args.body,
|
|
6470
|
+
bounce: args.bounce ?? true,
|
|
6471
|
+
init: args.init ?? void 0
|
|
6472
|
+
})
|
|
6473
|
+
]
|
|
6474
|
+
});
|
|
6475
|
+
});
|
|
6476
|
+
} catch (err) {
|
|
6477
|
+
const httpErr = isHttpError(err) ? err : void 0;
|
|
6478
|
+
const status = httpErr?.status || httpErr?.response?.status;
|
|
6479
|
+
if (status === 429 || status !== void 0 && status >= 500) {
|
|
6480
|
+
invalidateTonClientCache();
|
|
6481
|
+
}
|
|
6482
|
+
throw err;
|
|
6483
|
+
}
|
|
6484
|
+
}
|
|
6485
|
+
};
|
|
6486
|
+
},
|
|
7516
6487
|
// ─── Sub-namespaces ───────────────────────────────────────────
|
|
7517
|
-
dex: Object.freeze(createDexSDK(
|
|
7518
|
-
dns: Object.freeze(createDnsSDK(
|
|
6488
|
+
dex: Object.freeze(createDexSDK(log10)),
|
|
6489
|
+
dns: Object.freeze(createDnsSDK(log10))
|
|
7519
6490
|
};
|
|
7520
6491
|
}
|
|
7521
6492
|
function mapNftItem(item) {
|
|
@@ -7568,7 +6539,7 @@ async function getApi() {
|
|
|
7568
6539
|
}
|
|
7569
6540
|
|
|
7570
6541
|
// src/sdk/telegram-messages.ts
|
|
7571
|
-
function createTelegramMessagesSDK(bridge,
|
|
6542
|
+
function createTelegramMessagesSDK(bridge, log10) {
|
|
7572
6543
|
function requireBridge2() {
|
|
7573
6544
|
requireBridge(bridge);
|
|
7574
6545
|
}
|
|
@@ -7675,7 +6646,7 @@ function createTelegramMessagesSDK(bridge, log12) {
|
|
|
7675
6646
|
return (resultData.messages ?? []).map(toSimpleMessage);
|
|
7676
6647
|
} catch (err) {
|
|
7677
6648
|
if (err instanceof PluginSDKError) throw err;
|
|
7678
|
-
|
|
6649
|
+
log10.error("telegram.searchMessages() failed:", err);
|
|
7679
6650
|
return [];
|
|
7680
6651
|
}
|
|
7681
6652
|
},
|
|
@@ -7918,7 +6889,7 @@ function createTelegramMessagesSDK(bridge, log12) {
|
|
|
7918
6889
|
return messages;
|
|
7919
6890
|
} catch (err) {
|
|
7920
6891
|
if (err instanceof PluginSDKError) throw err;
|
|
7921
|
-
|
|
6892
|
+
log10.error("telegram.getScheduledMessages() failed:", err);
|
|
7922
6893
|
return [];
|
|
7923
6894
|
}
|
|
7924
6895
|
},
|
|
@@ -7979,7 +6950,7 @@ function createTelegramMessagesSDK(bridge, log12) {
|
|
|
7979
6950
|
}
|
|
7980
6951
|
|
|
7981
6952
|
// src/sdk/telegram-social.ts
|
|
7982
|
-
function createTelegramSocialSDK(bridge,
|
|
6953
|
+
function createTelegramSocialSDK(bridge, log10) {
|
|
7983
6954
|
function requireBridge2() {
|
|
7984
6955
|
requireBridge(bridge);
|
|
7985
6956
|
}
|
|
@@ -8054,7 +7025,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8054
7025
|
return null;
|
|
8055
7026
|
} catch (err) {
|
|
8056
7027
|
if (err instanceof PluginSDKError) throw err;
|
|
8057
|
-
|
|
7028
|
+
log10.error("telegram.getChatInfo() failed:", err);
|
|
8058
7029
|
return null;
|
|
8059
7030
|
}
|
|
8060
7031
|
},
|
|
@@ -8168,7 +7139,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8168
7139
|
});
|
|
8169
7140
|
} catch (err) {
|
|
8170
7141
|
if (err instanceof PluginSDKError) throw err;
|
|
8171
|
-
|
|
7142
|
+
log10.error("telegram.getParticipants() failed:", err);
|
|
8172
7143
|
return [];
|
|
8173
7144
|
}
|
|
8174
7145
|
},
|
|
@@ -8530,7 +7501,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8530
7501
|
}));
|
|
8531
7502
|
} catch (err) {
|
|
8532
7503
|
if (err instanceof PluginSDKError) throw err;
|
|
8533
|
-
|
|
7504
|
+
log10.error("telegram.getDialogs() failed:", err);
|
|
8534
7505
|
return [];
|
|
8535
7506
|
}
|
|
8536
7507
|
},
|
|
@@ -8544,7 +7515,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8544
7515
|
return messages.map(toSimpleMessage);
|
|
8545
7516
|
} catch (err) {
|
|
8546
7517
|
if (err instanceof PluginSDKError) throw err;
|
|
8547
|
-
|
|
7518
|
+
log10.error("telegram.getHistory() failed:", err);
|
|
8548
7519
|
return [];
|
|
8549
7520
|
}
|
|
8550
7521
|
},
|
|
@@ -8575,7 +7546,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8575
7546
|
}));
|
|
8576
7547
|
} catch (err) {
|
|
8577
7548
|
if (err instanceof PluginSDKError) throw err;
|
|
8578
|
-
|
|
7549
|
+
log10.error("telegram.getStarsTransactions() failed:", err);
|
|
8579
7550
|
return [];
|
|
8580
7551
|
}
|
|
8581
7552
|
},
|
|
@@ -8680,7 +7651,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8680
7651
|
};
|
|
8681
7652
|
} catch (err) {
|
|
8682
7653
|
if (err instanceof PluginSDKError) throw err;
|
|
8683
|
-
|
|
7654
|
+
log10.error("telegram.getCollectibleInfo() failed:", err);
|
|
8684
7655
|
return null;
|
|
8685
7656
|
}
|
|
8686
7657
|
},
|
|
@@ -8718,7 +7689,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8718
7689
|
} catch (err) {
|
|
8719
7690
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
8720
7691
|
if (err instanceof PluginSDKError) throw err;
|
|
8721
|
-
|
|
7692
|
+
log10.error("telegram.getUniqueGift() failed:", err);
|
|
8722
7693
|
return null;
|
|
8723
7694
|
}
|
|
8724
7695
|
},
|
|
@@ -8744,7 +7715,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8744
7715
|
} catch (err) {
|
|
8745
7716
|
if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
|
|
8746
7717
|
if (err instanceof PluginSDKError) throw err;
|
|
8747
|
-
|
|
7718
|
+
log10.error("telegram.getUniqueGiftValue() failed:", err);
|
|
8748
7719
|
return null;
|
|
8749
7720
|
}
|
|
8750
7721
|
},
|
|
@@ -8779,7 +7750,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8779
7750
|
const client = getClient2();
|
|
8780
7751
|
const { Api: Api4, helpers } = await import("telegram");
|
|
8781
7752
|
const { CustomFile } = await import("telegram/client/uploads.js");
|
|
8782
|
-
const { readFileSync:
|
|
7753
|
+
const { readFileSync: readFileSync6, statSync: statSync2 } = await import("fs");
|
|
8783
7754
|
const { basename: basename2 } = await import("path");
|
|
8784
7755
|
const { resolve: resolve2, normalize: normalize2 } = await import("path");
|
|
8785
7756
|
const { homedir: homedir2 } = await import("os");
|
|
@@ -8804,7 +7775,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8804
7775
|
}
|
|
8805
7776
|
const fileName = basename2(filePath);
|
|
8806
7777
|
const fileSize = statSync2(filePath).size;
|
|
8807
|
-
const fileBuffer =
|
|
7778
|
+
const fileBuffer = readFileSync6(filePath);
|
|
8808
7779
|
const isVideo = filePath.toLowerCase().match(/\.(mp4|mov|avi|webm|mkv|m4v)$/);
|
|
8809
7780
|
const customFile = new CustomFile(fileName, fileSize, filePath, fileBuffer);
|
|
8810
7781
|
const uploadedFile = await client.uploadFile({
|
|
@@ -8855,7 +7826,7 @@ function createTelegramSocialSDK(bridge, log12) {
|
|
|
8855
7826
|
}
|
|
8856
7827
|
|
|
8857
7828
|
// src/sdk/telegram.ts
|
|
8858
|
-
function createTelegramSDK(bridge,
|
|
7829
|
+
function createTelegramSDK(bridge, log10) {
|
|
8859
7830
|
function requireBridge2() {
|
|
8860
7831
|
requireBridge(bridge);
|
|
8861
7832
|
}
|
|
@@ -8959,7 +7930,7 @@ function createTelegramSDK(bridge, log12) {
|
|
|
8959
7930
|
timestamp: m.timestamp
|
|
8960
7931
|
}));
|
|
8961
7932
|
} catch (err) {
|
|
8962
|
-
|
|
7933
|
+
log10.error("telegram.getMessages() failed:", err);
|
|
8963
7934
|
return [];
|
|
8964
7935
|
}
|
|
8965
7936
|
},
|
|
@@ -8981,7 +7952,7 @@ function createTelegramSDK(bridge, log12) {
|
|
|
8981
7952
|
return bridge.isAvailable();
|
|
8982
7953
|
},
|
|
8983
7954
|
getRawClient() {
|
|
8984
|
-
|
|
7955
|
+
log10.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
|
|
8985
7956
|
if (!bridge.isAvailable()) return null;
|
|
8986
7957
|
try {
|
|
8987
7958
|
return bridge.getClient().getClient();
|
|
@@ -8990,8 +7961,8 @@ function createTelegramSDK(bridge, log12) {
|
|
|
8990
7961
|
}
|
|
8991
7962
|
},
|
|
8992
7963
|
// Spread extended methods from sub-modules
|
|
8993
|
-
...createTelegramMessagesSDK(bridge,
|
|
8994
|
-
...createTelegramSocialSDK(bridge,
|
|
7964
|
+
...createTelegramMessagesSDK(bridge, log10),
|
|
7965
|
+
...createTelegramSocialSDK(bridge, log10)
|
|
8995
7966
|
};
|
|
8996
7967
|
}
|
|
8997
7968
|
|
|
@@ -9032,23 +8003,23 @@ function deletePluginSecret(pluginName, key) {
|
|
|
9032
8003
|
function listPluginSecretKeys(pluginName) {
|
|
9033
8004
|
return Object.keys(readSecretsFile(pluginName));
|
|
9034
8005
|
}
|
|
9035
|
-
function createSecretsSDK(pluginName, pluginConfig,
|
|
8006
|
+
function createSecretsSDK(pluginName, pluginConfig, log10) {
|
|
9036
8007
|
const envPrefix = pluginName.replace(/-/g, "_").toUpperCase();
|
|
9037
8008
|
function get(key) {
|
|
9038
8009
|
const envKey = `${envPrefix}_${key.toUpperCase()}`;
|
|
9039
8010
|
const envValue = process.env[envKey];
|
|
9040
8011
|
if (envValue) {
|
|
9041
|
-
|
|
8012
|
+
log10.debug(`Secret "${key}" resolved from env var ${envKey}`);
|
|
9042
8013
|
return envValue;
|
|
9043
8014
|
}
|
|
9044
8015
|
const stored = readSecretsFile(pluginName);
|
|
9045
8016
|
if (key in stored && stored[key]) {
|
|
9046
|
-
|
|
8017
|
+
log10.debug(`Secret "${key}" resolved from secrets store`);
|
|
9047
8018
|
return stored[key];
|
|
9048
8019
|
}
|
|
9049
8020
|
const configValue = pluginConfig[key];
|
|
9050
8021
|
if (configValue !== void 0 && configValue !== null) {
|
|
9051
|
-
|
|
8022
|
+
log10.debug(`Secret "${key}" resolved from pluginConfig`);
|
|
9052
8023
|
return String(configValue);
|
|
9053
8024
|
}
|
|
9054
8025
|
return void 0;
|
|
@@ -9150,7 +8121,7 @@ function createStorageSDK(db) {
|
|
|
9150
8121
|
}
|
|
9151
8122
|
|
|
9152
8123
|
// src/sdk/bot.ts
|
|
9153
|
-
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter,
|
|
8124
|
+
function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter, log10) {
|
|
9154
8125
|
if (!router || !manifest || !manifest.inline && !manifest.callbacks) {
|
|
9155
8126
|
return null;
|
|
9156
8127
|
}
|
|
@@ -9173,7 +8144,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9173
8144
|
},
|
|
9174
8145
|
onInlineQuery(handler) {
|
|
9175
8146
|
if (handlers.onInlineQuery) {
|
|
9176
|
-
|
|
8147
|
+
log10.warn("onInlineQuery called again \u2014 overwriting previous handler");
|
|
9177
8148
|
}
|
|
9178
8149
|
handlers.onInlineQuery = async (ctx) => {
|
|
9179
8150
|
if (rateLimiter) {
|
|
@@ -9219,7 +8190,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9219
8190
|
return;
|
|
9220
8191
|
} catch (error) {
|
|
9221
8192
|
if (error?.errorMessage === "MESSAGE_NOT_MODIFIED") return;
|
|
9222
|
-
|
|
8193
|
+
log10.warn(`GramJS edit failed, falling back to Grammy: ${error?.errorMessage || error}`);
|
|
9223
8194
|
}
|
|
9224
8195
|
}
|
|
9225
8196
|
if (grammyBot) {
|
|
@@ -9232,7 +8203,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
|
|
|
9232
8203
|
});
|
|
9233
8204
|
} catch (error) {
|
|
9234
8205
|
if (error?.description?.includes("message is not modified")) return;
|
|
9235
|
-
|
|
8206
|
+
log10.error(`Failed to edit inline message: ${error?.description || error}`);
|
|
9236
8207
|
}
|
|
9237
8208
|
}
|
|
9238
8209
|
},
|
|
@@ -9291,13 +8262,13 @@ function createSafeDb(db) {
|
|
|
9291
8262
|
});
|
|
9292
8263
|
}
|
|
9293
8264
|
function createPluginSDK(deps, opts) {
|
|
9294
|
-
const
|
|
8265
|
+
const log10 = createLogger2(opts.pluginName);
|
|
9295
8266
|
const safeDb = opts.db ? createSafeDb(opts.db) : null;
|
|
9296
|
-
const ton = Object.freeze(createTonSDK(
|
|
9297
|
-
const telegram = Object.freeze(createTelegramSDK(deps.bridge,
|
|
9298
|
-
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig,
|
|
8267
|
+
const ton = Object.freeze(createTonSDK(log10, safeDb));
|
|
8268
|
+
const telegram = Object.freeze(createTelegramSDK(deps.bridge, log10));
|
|
8269
|
+
const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log10));
|
|
9299
8270
|
const storage = safeDb ? Object.freeze(createStorageSDK(safeDb)) : null;
|
|
9300
|
-
const frozenLog = Object.freeze(
|
|
8271
|
+
const frozenLog = Object.freeze(log10);
|
|
9301
8272
|
const frozenConfig = Object.freeze(JSON.parse(JSON.stringify(opts.sanitizedConfig ?? {})));
|
|
9302
8273
|
const frozenPluginConfig = Object.freeze(JSON.parse(JSON.stringify(opts.pluginConfig ?? {})));
|
|
9303
8274
|
let cachedBot;
|
|
@@ -9328,20 +8299,20 @@ function createPluginSDK(deps, opts) {
|
|
|
9328
8299
|
},
|
|
9329
8300
|
on(hookName, handler, onOpts) {
|
|
9330
8301
|
if (!opts.hookRegistry) {
|
|
9331
|
-
|
|
8302
|
+
log10.warn(`Hook registration unavailable \u2014 sdk.on() ignored`);
|
|
9332
8303
|
return;
|
|
9333
8304
|
}
|
|
9334
8305
|
if (opts.declaredHooks) {
|
|
9335
8306
|
const declared = opts.declaredHooks.some((h2) => h2.name === hookName);
|
|
9336
8307
|
if (!declared) {
|
|
9337
|
-
|
|
8308
|
+
log10.warn(`Hook "${hookName}" not declared in manifest \u2014 registration rejected`);
|
|
9338
8309
|
return;
|
|
9339
8310
|
}
|
|
9340
8311
|
}
|
|
9341
8312
|
const rawPriority = Number(onOpts?.priority) || 0;
|
|
9342
8313
|
const clampedPriority = Math.max(-1e3, Math.min(1e3, rawPriority));
|
|
9343
8314
|
if (rawPriority !== clampedPriority) {
|
|
9344
|
-
|
|
8315
|
+
log10.debug(`Hook "${hookName}" priority ${rawPriority} clamped to ${clampedPriority}`);
|
|
9345
8316
|
}
|
|
9346
8317
|
const registered = opts.hookRegistry.register({
|
|
9347
8318
|
pluginId: opts.pluginName,
|
|
@@ -9351,7 +8322,7 @@ function createPluginSDK(deps, opts) {
|
|
|
9351
8322
|
globalPriority: opts.globalPriority ?? 0
|
|
9352
8323
|
});
|
|
9353
8324
|
if (!registered) {
|
|
9354
|
-
|
|
8325
|
+
log10.warn(
|
|
9355
8326
|
`Hook registration limit reached for plugin "${opts.pluginName}" \u2014 "${hookName}" rejected`
|
|
9356
8327
|
);
|
|
9357
8328
|
}
|
|
@@ -9470,7 +8441,7 @@ var HookRegistry = class {
|
|
|
9470
8441
|
|
|
9471
8442
|
// src/agent/tools/plugin-loader.ts
|
|
9472
8443
|
var execFileAsync = promisify(execFile);
|
|
9473
|
-
var
|
|
8444
|
+
var log7 = createLogger("PluginLoader");
|
|
9474
8445
|
var PLUGIN_DATA_DIR = join3(TELETON_ROOT, "plugins", "data");
|
|
9475
8446
|
function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookRegistry, pluginPriorities) {
|
|
9476
8447
|
let manifest = null;
|
|
@@ -9478,7 +8449,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookReg
|
|
|
9478
8449
|
try {
|
|
9479
8450
|
manifest = validateManifest(raw.manifest);
|
|
9480
8451
|
} catch (err) {
|
|
9481
|
-
|
|
8452
|
+
log7.warn(
|
|
9482
8453
|
`[${entryName}] invalid manifest, ignoring: ${err instanceof Error ? err.message : err}`
|
|
9483
8454
|
);
|
|
9484
8455
|
}
|
|
@@ -9672,7 +8643,7 @@ async function ensurePluginDeps(pluginDir, pluginEntry) {
|
|
|
9672
8643
|
const nodeModules = join3(pluginDir, "node_modules");
|
|
9673
8644
|
if (!existsSync5(pkgJson)) return;
|
|
9674
8645
|
if (!existsSync5(lockfile)) {
|
|
9675
|
-
|
|
8646
|
+
log7.warn(
|
|
9676
8647
|
`[${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
|
|
9677
8648
|
);
|
|
9678
8649
|
return;
|
|
@@ -9681,16 +8652,16 @@ async function ensurePluginDeps(pluginDir, pluginEntry) {
|
|
|
9681
8652
|
const marker = join3(nodeModules, ".package-lock.json");
|
|
9682
8653
|
if (existsSync5(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
|
|
9683
8654
|
}
|
|
9684
|
-
|
|
8655
|
+
log7.info(`[${pluginEntry}] Installing dependencies...`);
|
|
9685
8656
|
try {
|
|
9686
8657
|
await execFileAsync("npm", ["ci", "--ignore-scripts", "--no-audit", "--no-fund"], {
|
|
9687
8658
|
cwd: pluginDir,
|
|
9688
8659
|
timeout: 6e4,
|
|
9689
8660
|
env: { ...process.env, NODE_ENV: "production" }
|
|
9690
8661
|
});
|
|
9691
|
-
|
|
8662
|
+
log7.info(`[${pluginEntry}] Dependencies installed`);
|
|
9692
8663
|
} catch (err) {
|
|
9693
|
-
|
|
8664
|
+
log7.error(`[${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
|
|
9694
8665
|
}
|
|
9695
8666
|
}
|
|
9696
8667
|
async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
@@ -9743,7 +8714,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
9743
8714
|
);
|
|
9744
8715
|
for (const result of loadResults) {
|
|
9745
8716
|
if (result.status === "rejected") {
|
|
9746
|
-
|
|
8717
|
+
log7.error(
|
|
9747
8718
|
`Plugin failed to load: ${result.reason instanceof Error ? result.reason.message : result.reason}`
|
|
9748
8719
|
);
|
|
9749
8720
|
continue;
|
|
@@ -9751,7 +8722,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
9751
8722
|
const { entry, mod } = result.value;
|
|
9752
8723
|
try {
|
|
9753
8724
|
if (!mod.tools || typeof mod.tools !== "function" && !Array.isArray(mod.tools)) {
|
|
9754
|
-
|
|
8725
|
+
log7.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
|
|
9755
8726
|
continue;
|
|
9756
8727
|
}
|
|
9757
8728
|
const adapted = adaptPlugin(
|
|
@@ -9764,676 +8735,42 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
|
|
|
9764
8735
|
pluginPriorities
|
|
9765
8736
|
);
|
|
9766
8737
|
if (loadedNames.has(adapted.name)) {
|
|
9767
|
-
|
|
8738
|
+
log7.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
|
|
9768
8739
|
continue;
|
|
9769
8740
|
}
|
|
9770
8741
|
loadedNames.add(adapted.name);
|
|
9771
8742
|
modules.push(adapted);
|
|
9772
8743
|
} catch (err) {
|
|
9773
|
-
|
|
8744
|
+
log7.error(`Plugin "${entry}" failed to adapt: ${err instanceof Error ? err.message : err}`);
|
|
9774
8745
|
}
|
|
9775
8746
|
}
|
|
9776
8747
|
return { modules, hookRegistry };
|
|
9777
8748
|
}
|
|
9778
8749
|
|
|
9779
|
-
// src/
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
var identity = (v) => v;
|
|
9784
|
-
var nonEmpty = (v) => v.length > 0 ? void 0 : "Must not be empty";
|
|
9785
|
-
function numberInRange(min, max) {
|
|
9786
|
-
return (v) => {
|
|
9787
|
-
const n2 = Number(v);
|
|
9788
|
-
if (isNaN(n2)) return "Must be a number";
|
|
9789
|
-
if (n2 < min || n2 > max) return `Must be between ${min} and ${max}`;
|
|
9790
|
-
return void 0;
|
|
9791
|
-
};
|
|
8750
|
+
// src/agent/token-usage.ts
|
|
8751
|
+
var globalTokenUsage = { totalTokens: 0, totalCost: 0 };
|
|
8752
|
+
function getTokenUsage() {
|
|
8753
|
+
return { ...globalTokenUsage };
|
|
9792
8754
|
}
|
|
9793
|
-
function
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
function positiveInteger(v) {
|
|
9797
|
-
const n2 = Number(v);
|
|
9798
|
-
if (!Number.isInteger(n2) || n2 <= 0) return "Must be a positive integer";
|
|
9799
|
-
return void 0;
|
|
9800
|
-
}
|
|
9801
|
-
function validateUrl(v) {
|
|
9802
|
-
if (v === "") return void 0;
|
|
9803
|
-
if (v.startsWith("http://") || v.startsWith("https://")) return void 0;
|
|
9804
|
-
return "Must be empty or start with http:// or https://";
|
|
9805
|
-
}
|
|
9806
|
-
var CONFIGURABLE_KEYS = {
|
|
9807
|
-
// ─── API Keys ──────────────────────────────────────────────────────
|
|
9808
|
-
"agent.api_key": {
|
|
9809
|
-
type: "string",
|
|
9810
|
-
category: "API Keys",
|
|
9811
|
-
label: "LLM API Key",
|
|
9812
|
-
description: "LLM provider API key",
|
|
9813
|
-
sensitive: true,
|
|
9814
|
-
hotReload: "instant",
|
|
9815
|
-
validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
|
|
9816
|
-
mask: (v) => v.slice(0, 8) + "****",
|
|
9817
|
-
parse: identity
|
|
9818
|
-
},
|
|
9819
|
-
tavily_api_key: {
|
|
9820
|
-
type: "string",
|
|
9821
|
-
category: "API Keys",
|
|
9822
|
-
label: "Tavily API Key",
|
|
9823
|
-
description: "Tavily API key for web search",
|
|
9824
|
-
sensitive: true,
|
|
9825
|
-
hotReload: "instant",
|
|
9826
|
-
validate: (v) => v.startsWith("tvly-") ? void 0 : "Must start with 'tvly-'",
|
|
9827
|
-
mask: (v) => v.slice(0, 9) + "****",
|
|
9828
|
-
parse: identity
|
|
9829
|
-
},
|
|
9830
|
-
tonapi_key: {
|
|
9831
|
-
type: "string",
|
|
9832
|
-
category: "API Keys",
|
|
9833
|
-
label: "TonAPI Key",
|
|
9834
|
-
description: "TonAPI key for higher rate limits",
|
|
9835
|
-
sensitive: true,
|
|
9836
|
-
hotReload: "instant",
|
|
9837
|
-
validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
|
|
9838
|
-
mask: (v) => v.slice(0, 10) + "****",
|
|
9839
|
-
parse: identity
|
|
9840
|
-
},
|
|
9841
|
-
toncenter_api_key: {
|
|
9842
|
-
type: "string",
|
|
9843
|
-
category: "API Keys",
|
|
9844
|
-
label: "TonCenter API Key",
|
|
9845
|
-
description: "TonCenter API key for dedicated RPC endpoint (free at toncenter.com)",
|
|
9846
|
-
sensitive: true,
|
|
9847
|
-
hotReload: "instant",
|
|
9848
|
-
validate: (v) => v.length >= 10 ? void 0 : "Must be at least 10 characters",
|
|
9849
|
-
mask: (v) => v.slice(0, 10) + "****",
|
|
9850
|
-
parse: identity
|
|
9851
|
-
},
|
|
9852
|
-
"telegram.bot_token": {
|
|
9853
|
-
type: "string",
|
|
9854
|
-
category: "API Keys",
|
|
9855
|
-
label: "Bot Token",
|
|
9856
|
-
description: "Bot token from @BotFather",
|
|
9857
|
-
sensitive: true,
|
|
9858
|
-
hotReload: "instant",
|
|
9859
|
-
validate: (v) => v.includes(":") ? void 0 : "Must contain ':' (e.g., 123456:ABC...)",
|
|
9860
|
-
mask: (v) => v.split(":")[0] + ":****",
|
|
9861
|
-
parse: identity
|
|
9862
|
-
},
|
|
9863
|
-
// ─── Agent ─────────────────────────────────────────────────────────
|
|
9864
|
-
"agent.provider": {
|
|
9865
|
-
type: "enum",
|
|
9866
|
-
category: "Agent",
|
|
9867
|
-
label: "Provider",
|
|
9868
|
-
description: "LLM provider",
|
|
9869
|
-
sensitive: false,
|
|
9870
|
-
hotReload: "instant",
|
|
9871
|
-
options: getSupportedProviders().map((p2) => p2.id),
|
|
9872
|
-
validate: enumValidator(getSupportedProviders().map((p2) => p2.id)),
|
|
9873
|
-
mask: identity,
|
|
9874
|
-
parse: identity
|
|
9875
|
-
},
|
|
9876
|
-
"agent.model": {
|
|
9877
|
-
type: "string",
|
|
9878
|
-
category: "Agent",
|
|
9879
|
-
label: "Model",
|
|
9880
|
-
description: "Main LLM model ID",
|
|
9881
|
-
sensitive: false,
|
|
9882
|
-
hotReload: "instant",
|
|
9883
|
-
validate: nonEmpty,
|
|
9884
|
-
mask: identity,
|
|
9885
|
-
parse: identity
|
|
9886
|
-
},
|
|
9887
|
-
"agent.utility_model": {
|
|
9888
|
-
type: "string",
|
|
9889
|
-
category: "Agent",
|
|
9890
|
-
label: "Utility Model",
|
|
9891
|
-
description: "Cheap model for summarization (auto-detected if empty)",
|
|
9892
|
-
sensitive: false,
|
|
9893
|
-
hotReload: "instant",
|
|
9894
|
-
validate: noValidation,
|
|
9895
|
-
mask: identity,
|
|
9896
|
-
parse: identity
|
|
9897
|
-
},
|
|
9898
|
-
"agent.temperature": {
|
|
9899
|
-
type: "number",
|
|
9900
|
-
category: "Agent",
|
|
9901
|
-
label: "Temperature",
|
|
9902
|
-
description: "Response creativity (0.0 = deterministic, 2.0 = max)",
|
|
9903
|
-
sensitive: false,
|
|
9904
|
-
hotReload: "instant",
|
|
9905
|
-
validate: numberInRange(0, 2),
|
|
9906
|
-
mask: identity,
|
|
9907
|
-
parse: (v) => Number(v)
|
|
9908
|
-
},
|
|
9909
|
-
"agent.max_tokens": {
|
|
9910
|
-
type: "number",
|
|
9911
|
-
category: "Agent",
|
|
9912
|
-
label: "Max Tokens",
|
|
9913
|
-
description: "Maximum response length in tokens",
|
|
9914
|
-
sensitive: false,
|
|
9915
|
-
hotReload: "instant",
|
|
9916
|
-
validate: numberInRange(256, 128e3),
|
|
9917
|
-
mask: identity,
|
|
9918
|
-
parse: (v) => Number(v)
|
|
9919
|
-
},
|
|
9920
|
-
"agent.max_agentic_iterations": {
|
|
9921
|
-
type: "number",
|
|
9922
|
-
category: "Agent",
|
|
9923
|
-
label: "Max Iterations",
|
|
9924
|
-
description: "Max tool-call loop iterations per message",
|
|
9925
|
-
sensitive: false,
|
|
9926
|
-
hotReload: "instant",
|
|
9927
|
-
validate: numberInRange(1, 20),
|
|
9928
|
-
mask: identity,
|
|
9929
|
-
parse: (v) => Number(v)
|
|
9930
|
-
},
|
|
9931
|
-
"agent.base_url": {
|
|
9932
|
-
type: "string",
|
|
9933
|
-
category: "Agent",
|
|
9934
|
-
label: "API Base URL",
|
|
9935
|
-
description: "Base URL for local LLM server (requires restart)",
|
|
9936
|
-
sensitive: false,
|
|
9937
|
-
hotReload: "restart",
|
|
9938
|
-
validate: validateUrl,
|
|
9939
|
-
mask: identity,
|
|
9940
|
-
parse: identity
|
|
9941
|
-
},
|
|
9942
|
-
"cocoon.port": {
|
|
9943
|
-
type: "number",
|
|
9944
|
-
category: "Agent",
|
|
9945
|
-
label: "Cocoon Port",
|
|
9946
|
-
description: "Cocoon proxy port (requires restart)",
|
|
9947
|
-
sensitive: false,
|
|
9948
|
-
hotReload: "restart",
|
|
9949
|
-
validate: numberInRange(1, 65535),
|
|
9950
|
-
mask: identity,
|
|
9951
|
-
parse: (v) => Number(v)
|
|
9952
|
-
},
|
|
9953
|
-
// ─── Session ───────────────────────────────────────────────────
|
|
9954
|
-
"agent.session_reset_policy.daily_reset_enabled": {
|
|
9955
|
-
type: "boolean",
|
|
9956
|
-
category: "Session",
|
|
9957
|
-
label: "Daily Reset",
|
|
9958
|
-
description: "Enable daily session reset at specified hour",
|
|
9959
|
-
sensitive: false,
|
|
9960
|
-
hotReload: "instant",
|
|
9961
|
-
validate: enumValidator(["true", "false"]),
|
|
9962
|
-
mask: identity,
|
|
9963
|
-
parse: (v) => v === "true"
|
|
9964
|
-
},
|
|
9965
|
-
"agent.session_reset_policy.daily_reset_hour": {
|
|
9966
|
-
type: "number",
|
|
9967
|
-
category: "Session",
|
|
9968
|
-
label: "Reset Hour",
|
|
9969
|
-
description: "Hour (0-23 UTC) for daily session reset",
|
|
9970
|
-
sensitive: false,
|
|
9971
|
-
hotReload: "instant",
|
|
9972
|
-
validate: numberInRange(0, 23),
|
|
9973
|
-
mask: identity,
|
|
9974
|
-
parse: (v) => Number(v)
|
|
9975
|
-
},
|
|
9976
|
-
"agent.session_reset_policy.idle_expiry_enabled": {
|
|
9977
|
-
type: "boolean",
|
|
9978
|
-
category: "Session",
|
|
9979
|
-
label: "Idle Expiry",
|
|
9980
|
-
description: "Enable automatic session expiry after idle period",
|
|
9981
|
-
sensitive: false,
|
|
9982
|
-
hotReload: "instant",
|
|
9983
|
-
validate: enumValidator(["true", "false"]),
|
|
9984
|
-
mask: identity,
|
|
9985
|
-
parse: (v) => v === "true"
|
|
9986
|
-
},
|
|
9987
|
-
"agent.session_reset_policy.idle_expiry_minutes": {
|
|
9988
|
-
type: "number",
|
|
9989
|
-
category: "Session",
|
|
9990
|
-
label: "Idle Minutes",
|
|
9991
|
-
description: "Idle minutes before session expires (minimum 1)",
|
|
9992
|
-
sensitive: false,
|
|
9993
|
-
hotReload: "instant",
|
|
9994
|
-
validate: numberInRange(1, Number.MAX_SAFE_INTEGER),
|
|
9995
|
-
mask: identity,
|
|
9996
|
-
parse: (v) => Number(v)
|
|
9997
|
-
},
|
|
9998
|
-
// ─── Telegram ──────────────────────────────────────────────────────
|
|
9999
|
-
"telegram.bot_username": {
|
|
10000
|
-
type: "string",
|
|
10001
|
-
category: "Telegram",
|
|
10002
|
-
label: "Bot Username",
|
|
10003
|
-
description: "Bot username without @",
|
|
10004
|
-
sensitive: false,
|
|
10005
|
-
hotReload: "instant",
|
|
10006
|
-
validate: (v) => v.length >= 3 ? void 0 : "Must be at least 3 characters",
|
|
10007
|
-
mask: identity,
|
|
10008
|
-
parse: identity
|
|
10009
|
-
},
|
|
10010
|
-
"telegram.dm_policy": {
|
|
10011
|
-
type: "enum",
|
|
10012
|
-
category: "Telegram",
|
|
10013
|
-
label: "DM Policy",
|
|
10014
|
-
description: "Who can message the bot in private",
|
|
10015
|
-
sensitive: false,
|
|
10016
|
-
hotReload: "instant",
|
|
10017
|
-
options: ["admin-only", "allowlist", "open", "disabled"],
|
|
10018
|
-
optionLabels: {
|
|
10019
|
-
"admin-only": "Admin Only",
|
|
10020
|
-
allowlist: "Allow Users",
|
|
10021
|
-
open: "Open",
|
|
10022
|
-
disabled: "Disabled"
|
|
10023
|
-
},
|
|
10024
|
-
validate: enumValidator(["open", "allowlist", "admin-only", "disabled"]),
|
|
10025
|
-
mask: identity,
|
|
10026
|
-
parse: identity
|
|
10027
|
-
},
|
|
10028
|
-
"telegram.group_policy": {
|
|
10029
|
-
type: "enum",
|
|
10030
|
-
category: "Telegram",
|
|
10031
|
-
label: "Group Policy",
|
|
10032
|
-
description: "Which groups the bot can respond in",
|
|
10033
|
-
sensitive: false,
|
|
10034
|
-
hotReload: "instant",
|
|
10035
|
-
options: ["open", "allowlist", "admin-only", "disabled"],
|
|
10036
|
-
optionLabels: {
|
|
10037
|
-
open: "Open",
|
|
10038
|
-
allowlist: "Allow Groups",
|
|
10039
|
-
"admin-only": "Admin Only",
|
|
10040
|
-
disabled: "Disabled"
|
|
10041
|
-
},
|
|
10042
|
-
validate: enumValidator(["open", "allowlist", "admin-only", "disabled"]),
|
|
10043
|
-
mask: identity,
|
|
10044
|
-
parse: identity
|
|
10045
|
-
},
|
|
10046
|
-
"telegram.require_mention": {
|
|
10047
|
-
type: "boolean",
|
|
10048
|
-
category: "Telegram",
|
|
10049
|
-
label: "Require Mention",
|
|
10050
|
-
description: "Require @mention in groups to respond",
|
|
10051
|
-
sensitive: false,
|
|
10052
|
-
hotReload: "instant",
|
|
10053
|
-
validate: enumValidator(["true", "false"]),
|
|
10054
|
-
mask: identity,
|
|
10055
|
-
parse: (v) => v === "true"
|
|
10056
|
-
},
|
|
10057
|
-
"telegram.owner_name": {
|
|
10058
|
-
type: "string",
|
|
10059
|
-
category: "Telegram",
|
|
10060
|
-
label: "Owner Name",
|
|
10061
|
-
description: "Owner's first name (used in system prompt)",
|
|
10062
|
-
sensitive: false,
|
|
10063
|
-
hotReload: "instant",
|
|
10064
|
-
validate: noValidation,
|
|
10065
|
-
mask: identity,
|
|
10066
|
-
parse: identity
|
|
10067
|
-
},
|
|
10068
|
-
"telegram.owner_username": {
|
|
10069
|
-
type: "string",
|
|
10070
|
-
category: "Telegram",
|
|
10071
|
-
label: "Owner Username",
|
|
10072
|
-
description: "Owner's Telegram username (without @)",
|
|
10073
|
-
sensitive: false,
|
|
10074
|
-
hotReload: "instant",
|
|
10075
|
-
validate: noValidation,
|
|
10076
|
-
mask: identity,
|
|
10077
|
-
parse: identity
|
|
10078
|
-
},
|
|
10079
|
-
"telegram.debounce_ms": {
|
|
10080
|
-
type: "number",
|
|
10081
|
-
category: "Telegram",
|
|
10082
|
-
label: "Debounce (ms)",
|
|
10083
|
-
description: "Group message debounce delay in ms (0 = disabled)",
|
|
10084
|
-
sensitive: false,
|
|
10085
|
-
hotReload: "instant",
|
|
10086
|
-
validate: numberInRange(0, 1e4),
|
|
10087
|
-
mask: identity,
|
|
10088
|
-
parse: (v) => Number(v)
|
|
10089
|
-
},
|
|
10090
|
-
"telegram.agent_channel": {
|
|
10091
|
-
type: "string",
|
|
10092
|
-
category: "Telegram",
|
|
10093
|
-
label: "Agent Channel",
|
|
10094
|
-
description: "Channel username for auto-publishing",
|
|
10095
|
-
sensitive: false,
|
|
10096
|
-
hotReload: "instant",
|
|
10097
|
-
validate: noValidation,
|
|
10098
|
-
mask: identity,
|
|
10099
|
-
parse: identity
|
|
10100
|
-
},
|
|
10101
|
-
"telegram.typing_simulation": {
|
|
10102
|
-
type: "boolean",
|
|
10103
|
-
category: "Telegram",
|
|
10104
|
-
label: "Typing Simulation",
|
|
10105
|
-
description: "Simulate typing indicator before sending replies",
|
|
10106
|
-
sensitive: false,
|
|
10107
|
-
hotReload: "instant",
|
|
10108
|
-
validate: enumValidator(["true", "false"]),
|
|
10109
|
-
mask: identity,
|
|
10110
|
-
parse: (v) => v === "true"
|
|
10111
|
-
},
|
|
10112
|
-
"telegram.owner_id": {
|
|
10113
|
-
type: "number",
|
|
10114
|
-
category: "Telegram",
|
|
10115
|
-
label: "Admin ID",
|
|
10116
|
-
description: "Primary admin Telegram user ID (auto-added to Admin IDs)",
|
|
10117
|
-
sensitive: false,
|
|
10118
|
-
hotReload: "instant",
|
|
10119
|
-
validate: positiveInteger,
|
|
10120
|
-
mask: identity,
|
|
10121
|
-
parse: (v) => Number(v)
|
|
10122
|
-
},
|
|
10123
|
-
"telegram.max_message_length": {
|
|
10124
|
-
type: "number",
|
|
10125
|
-
category: "Telegram",
|
|
10126
|
-
label: "Max Message Length",
|
|
10127
|
-
description: "Maximum message length in characters",
|
|
10128
|
-
sensitive: false,
|
|
10129
|
-
hotReload: "instant",
|
|
10130
|
-
validate: numberInRange(1, 32768),
|
|
10131
|
-
mask: identity,
|
|
10132
|
-
parse: (v) => Number(v)
|
|
10133
|
-
},
|
|
10134
|
-
"telegram.rate_limit_messages_per_second": {
|
|
10135
|
-
type: "number",
|
|
10136
|
-
category: "Telegram",
|
|
10137
|
-
label: "Rate Limit \u2014 Messages/sec",
|
|
10138
|
-
description: "Rate limit: messages per second (requires restart)",
|
|
10139
|
-
sensitive: false,
|
|
10140
|
-
hotReload: "restart",
|
|
10141
|
-
validate: numberInRange(0.1, 10),
|
|
10142
|
-
mask: identity,
|
|
10143
|
-
parse: (v) => Number(v)
|
|
10144
|
-
},
|
|
10145
|
-
"telegram.rate_limit_groups_per_minute": {
|
|
10146
|
-
type: "number",
|
|
10147
|
-
category: "Telegram",
|
|
10148
|
-
label: "Rate Limit \u2014 Groups/min",
|
|
10149
|
-
description: "Rate limit: groups per minute (requires restart)",
|
|
10150
|
-
sensitive: false,
|
|
10151
|
-
hotReload: "restart",
|
|
10152
|
-
validate: numberInRange(1, 60),
|
|
10153
|
-
mask: identity,
|
|
10154
|
-
parse: (v) => Number(v)
|
|
10155
|
-
},
|
|
10156
|
-
"telegram.admin_ids": {
|
|
10157
|
-
type: "array",
|
|
10158
|
-
itemType: "number",
|
|
10159
|
-
category: "Telegram",
|
|
10160
|
-
label: "Admin IDs",
|
|
10161
|
-
description: "Admin user IDs with elevated access",
|
|
10162
|
-
sensitive: false,
|
|
10163
|
-
hotReload: "instant",
|
|
10164
|
-
validate: positiveInteger,
|
|
10165
|
-
mask: identity,
|
|
10166
|
-
parse: (v) => Number(v)
|
|
10167
|
-
},
|
|
10168
|
-
"telegram.allow_from": {
|
|
10169
|
-
type: "array",
|
|
10170
|
-
itemType: "number",
|
|
10171
|
-
category: "Telegram",
|
|
10172
|
-
label: "Allowed Users",
|
|
10173
|
-
description: "User IDs allowed for DM access",
|
|
10174
|
-
sensitive: false,
|
|
10175
|
-
hotReload: "instant",
|
|
10176
|
-
validate: positiveInteger,
|
|
10177
|
-
mask: identity,
|
|
10178
|
-
parse: (v) => Number(v)
|
|
10179
|
-
},
|
|
10180
|
-
"telegram.group_allow_from": {
|
|
10181
|
-
type: "array",
|
|
10182
|
-
itemType: "number",
|
|
10183
|
-
category: "Telegram",
|
|
10184
|
-
label: "Allowed Groups",
|
|
10185
|
-
description: "Group IDs allowed for group access",
|
|
10186
|
-
sensitive: false,
|
|
10187
|
-
hotReload: "instant",
|
|
10188
|
-
validate: positiveInteger,
|
|
10189
|
-
mask: identity,
|
|
10190
|
-
parse: (v) => Number(v)
|
|
10191
|
-
},
|
|
10192
|
-
// ─── Embedding ─────────────────────────────────────────────────────
|
|
10193
|
-
"embedding.provider": {
|
|
10194
|
-
type: "enum",
|
|
10195
|
-
category: "Embedding",
|
|
10196
|
-
label: "Embedding Provider",
|
|
10197
|
-
description: "Embedding provider for RAG",
|
|
10198
|
-
sensitive: false,
|
|
10199
|
-
hotReload: "instant",
|
|
10200
|
-
options: ["local", "anthropic", "none"],
|
|
10201
|
-
validate: enumValidator(["local", "anthropic", "none"]),
|
|
10202
|
-
mask: identity,
|
|
10203
|
-
parse: identity
|
|
10204
|
-
},
|
|
10205
|
-
"embedding.model": {
|
|
10206
|
-
type: "string",
|
|
10207
|
-
category: "Embedding",
|
|
10208
|
-
label: "Embedding Model",
|
|
10209
|
-
description: "Embedding model ID (requires restart)",
|
|
10210
|
-
sensitive: false,
|
|
10211
|
-
hotReload: "restart",
|
|
10212
|
-
validate: noValidation,
|
|
10213
|
-
mask: identity,
|
|
10214
|
-
parse: identity
|
|
10215
|
-
},
|
|
10216
|
-
// ─── WebUI ─────────────────────────────────────────────────────────
|
|
10217
|
-
"webui.port": {
|
|
10218
|
-
type: "number",
|
|
10219
|
-
category: "WebUI",
|
|
10220
|
-
label: "WebUI Port",
|
|
10221
|
-
description: "HTTP server port (requires restart)",
|
|
10222
|
-
sensitive: false,
|
|
10223
|
-
hotReload: "restart",
|
|
10224
|
-
validate: numberInRange(1024, 65535),
|
|
10225
|
-
mask: identity,
|
|
10226
|
-
parse: (v) => Number(v)
|
|
10227
|
-
},
|
|
10228
|
-
"webui.log_requests": {
|
|
10229
|
-
type: "boolean",
|
|
10230
|
-
category: "WebUI",
|
|
10231
|
-
label: "Log HTTP Requests",
|
|
10232
|
-
description: "Log all HTTP requests to console",
|
|
10233
|
-
sensitive: false,
|
|
10234
|
-
hotReload: "instant",
|
|
10235
|
-
validate: enumValidator(["true", "false"]),
|
|
10236
|
-
mask: identity,
|
|
10237
|
-
parse: (v) => v === "true"
|
|
10238
|
-
},
|
|
10239
|
-
// ─── Deals ─────────────────────────────────────────────────────────
|
|
10240
|
-
"deals.enabled": {
|
|
10241
|
-
type: "boolean",
|
|
10242
|
-
category: "Deals",
|
|
10243
|
-
label: "Deals Enabled",
|
|
10244
|
-
description: "Enable the deals/escrow module",
|
|
10245
|
-
sensitive: false,
|
|
10246
|
-
hotReload: "instant",
|
|
10247
|
-
validate: enumValidator(["true", "false"]),
|
|
10248
|
-
mask: identity,
|
|
10249
|
-
parse: (v) => v === "true"
|
|
10250
|
-
},
|
|
10251
|
-
"deals.expiry_seconds": {
|
|
10252
|
-
type: "number",
|
|
10253
|
-
category: "Deals",
|
|
10254
|
-
label: "Deal Expiry",
|
|
10255
|
-
description: "Deal expiry timeout in seconds",
|
|
10256
|
-
sensitive: false,
|
|
10257
|
-
hotReload: "instant",
|
|
10258
|
-
validate: numberInRange(10, 3600),
|
|
10259
|
-
mask: identity,
|
|
10260
|
-
parse: (v) => Number(v)
|
|
10261
|
-
},
|
|
10262
|
-
"deals.buy_max_floor_percent": {
|
|
10263
|
-
type: "number",
|
|
10264
|
-
category: "Deals",
|
|
10265
|
-
label: "Buy Max Floor %",
|
|
10266
|
-
description: "Maximum floor % for buy deals",
|
|
10267
|
-
sensitive: false,
|
|
10268
|
-
hotReload: "instant",
|
|
10269
|
-
validate: numberInRange(1, 100),
|
|
10270
|
-
mask: identity,
|
|
10271
|
-
parse: (v) => Number(v)
|
|
10272
|
-
},
|
|
10273
|
-
"deals.sell_min_floor_percent": {
|
|
10274
|
-
type: "number",
|
|
10275
|
-
category: "Deals",
|
|
10276
|
-
label: "Sell Min Floor %",
|
|
10277
|
-
description: "Minimum floor % for sell deals",
|
|
10278
|
-
sensitive: false,
|
|
10279
|
-
hotReload: "instant",
|
|
10280
|
-
validate: numberInRange(100, 500),
|
|
10281
|
-
mask: identity,
|
|
10282
|
-
parse: (v) => Number(v)
|
|
10283
|
-
},
|
|
10284
|
-
// ─── TON Proxy ────────────────────────────────────────────────────
|
|
10285
|
-
"ton_proxy.enabled": {
|
|
10286
|
-
type: "boolean",
|
|
10287
|
-
category: "TON Proxy",
|
|
10288
|
-
label: "TON Proxy Enabled",
|
|
10289
|
-
description: "Enable Tonutils-Proxy for .ton site access (auto-downloads binary on first run)",
|
|
10290
|
-
sensitive: false,
|
|
10291
|
-
hotReload: "instant",
|
|
10292
|
-
validate: enumValidator(["true", "false"]),
|
|
10293
|
-
mask: identity,
|
|
10294
|
-
parse: (v) => v === "true"
|
|
10295
|
-
},
|
|
10296
|
-
"ton_proxy.port": {
|
|
10297
|
-
type: "number",
|
|
10298
|
-
category: "TON Proxy",
|
|
10299
|
-
label: "Proxy Port",
|
|
10300
|
-
description: "HTTP proxy port for .ton sites (default: 8080)",
|
|
10301
|
-
sensitive: false,
|
|
10302
|
-
hotReload: "restart",
|
|
10303
|
-
validate: numberInRange(1, 65535),
|
|
10304
|
-
mask: identity,
|
|
10305
|
-
parse: (v) => Number(v)
|
|
10306
|
-
},
|
|
10307
|
-
"ton_proxy.binary_path": {
|
|
10308
|
-
type: "string",
|
|
10309
|
-
category: "TON Proxy",
|
|
10310
|
-
label: "Binary Path",
|
|
10311
|
-
description: "Custom path to tonutils-proxy-cli (leave empty for auto-download)",
|
|
10312
|
-
sensitive: false,
|
|
10313
|
-
hotReload: "restart",
|
|
10314
|
-
validate: noValidation,
|
|
10315
|
-
mask: identity,
|
|
10316
|
-
parse: identity
|
|
10317
|
-
},
|
|
10318
|
-
// ─── Capabilities ──────────────────────────────────────────────────
|
|
10319
|
-
"capabilities.exec.mode": {
|
|
10320
|
-
type: "enum",
|
|
10321
|
-
category: "Coding Agent",
|
|
10322
|
-
label: "Exec Mode",
|
|
10323
|
-
description: "System execution: off (disabled) or yolo (full system access)",
|
|
10324
|
-
sensitive: false,
|
|
10325
|
-
hotReload: "restart",
|
|
10326
|
-
options: ["off", "yolo"],
|
|
10327
|
-
optionLabels: { off: "Disabled", yolo: "YOLO" },
|
|
10328
|
-
validate: enumValidator(["off", "yolo"]),
|
|
10329
|
-
mask: identity,
|
|
10330
|
-
parse: identity
|
|
10331
|
-
},
|
|
10332
|
-
"capabilities.exec.scope": {
|
|
10333
|
-
type: "enum",
|
|
10334
|
-
category: "Coding Agent",
|
|
10335
|
-
label: "Exec Scope",
|
|
10336
|
-
description: "Who can trigger exec tools",
|
|
10337
|
-
sensitive: false,
|
|
10338
|
-
hotReload: "restart",
|
|
10339
|
-
options: ["admin-only", "allowlist", "all"],
|
|
10340
|
-
optionLabels: { "admin-only": "Admin Only", allowlist: "Allowlist", all: "Everyone" },
|
|
10341
|
-
validate: enumValidator(["admin-only", "allowlist", "all"]),
|
|
10342
|
-
mask: identity,
|
|
10343
|
-
parse: identity
|
|
10344
|
-
},
|
|
10345
|
-
// ─── Developer ─────────────────────────────────────────────────────
|
|
10346
|
-
"dev.hot_reload": {
|
|
10347
|
-
type: "boolean",
|
|
10348
|
-
category: "Developer",
|
|
10349
|
-
label: "Hot Reload",
|
|
10350
|
-
description: "Watch ~/.teleton/plugins/ for live changes",
|
|
10351
|
-
sensitive: false,
|
|
10352
|
-
hotReload: "instant",
|
|
10353
|
-
validate: enumValidator(["true", "false"]),
|
|
10354
|
-
mask: identity,
|
|
10355
|
-
parse: (v) => v === "true"
|
|
10356
|
-
}
|
|
10357
|
-
};
|
|
10358
|
-
var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
10359
|
-
function assertSafePath(parts) {
|
|
10360
|
-
if (parts.some((p2) => FORBIDDEN_SEGMENTS.has(p2))) {
|
|
10361
|
-
throw new Error("Invalid config path: forbidden segment");
|
|
10362
|
-
}
|
|
10363
|
-
}
|
|
10364
|
-
function getNestedValue(obj, path) {
|
|
10365
|
-
const parts = path.split(".");
|
|
10366
|
-
assertSafePath(parts);
|
|
10367
|
-
let current = obj;
|
|
10368
|
-
for (const part of parts) {
|
|
10369
|
-
if (current == null || typeof current !== "object") return void 0;
|
|
10370
|
-
current = current[part];
|
|
10371
|
-
}
|
|
10372
|
-
return current;
|
|
10373
|
-
}
|
|
10374
|
-
function setNestedValue(obj, path, value) {
|
|
10375
|
-
const parts = path.split(".");
|
|
10376
|
-
assertSafePath(parts);
|
|
10377
|
-
let current = obj;
|
|
10378
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
10379
|
-
if (current[parts[i]] == null || typeof current[parts[i]] !== "object") {
|
|
10380
|
-
current[parts[i]] = {};
|
|
10381
|
-
}
|
|
10382
|
-
current = current[parts[i]];
|
|
10383
|
-
}
|
|
10384
|
-
current[parts[parts.length - 1]] = value;
|
|
10385
|
-
}
|
|
10386
|
-
function deleteNestedValue(obj, path) {
|
|
10387
|
-
const parts = path.split(".");
|
|
10388
|
-
assertSafePath(parts);
|
|
10389
|
-
let current = obj;
|
|
10390
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
10391
|
-
if (current == null || typeof current !== "object") return;
|
|
10392
|
-
current = current[parts[i]];
|
|
10393
|
-
}
|
|
10394
|
-
if (current != null && typeof current === "object") {
|
|
10395
|
-
delete current[parts[parts.length - 1]];
|
|
10396
|
-
}
|
|
10397
|
-
}
|
|
10398
|
-
function readRawConfig(configPath) {
|
|
10399
|
-
const fullPath = expandPath(configPath);
|
|
10400
|
-
if (!existsSync6(fullPath)) {
|
|
10401
|
-
throw new Error(`Config file not found: ${fullPath}
|
|
10402
|
-
Run 'teleton setup' to create one.`);
|
|
10403
|
-
}
|
|
10404
|
-
const raw = parse(readFileSync5(fullPath, "utf-8"));
|
|
10405
|
-
if (!raw || typeof raw !== "object") {
|
|
10406
|
-
throw new Error(`Invalid config file: ${fullPath}`);
|
|
10407
|
-
}
|
|
10408
|
-
return raw;
|
|
10409
|
-
}
|
|
10410
|
-
function writeRawConfig(raw, configPath) {
|
|
10411
|
-
const clone = { ...raw };
|
|
10412
|
-
delete clone.market;
|
|
10413
|
-
const result = ConfigSchema.safeParse(clone);
|
|
10414
|
-
if (!result.success) {
|
|
10415
|
-
throw new Error(`Refusing to save invalid config: ${result.error.message}`);
|
|
10416
|
-
}
|
|
10417
|
-
raw.meta = raw.meta ?? {};
|
|
10418
|
-
raw.meta.last_modified_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10419
|
-
const fullPath = expandPath(configPath);
|
|
10420
|
-
writeFileSync2(fullPath, stringify(raw), { encoding: "utf-8", mode: 384 });
|
|
8755
|
+
function accumulateTokenUsage(usage) {
|
|
8756
|
+
globalTokenUsage.totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
8757
|
+
globalTokenUsage.totalCost += usage.totalCost;
|
|
10421
8758
|
}
|
|
10422
8759
|
|
|
10423
8760
|
// src/ton-proxy/manager.ts
|
|
10424
8761
|
import { spawn, execSync } from "child_process";
|
|
10425
8762
|
import {
|
|
10426
|
-
existsSync as
|
|
8763
|
+
existsSync as existsSync6,
|
|
10427
8764
|
chmodSync,
|
|
10428
8765
|
createWriteStream,
|
|
10429
|
-
readFileSync as
|
|
10430
|
-
writeFileSync as
|
|
8766
|
+
readFileSync as readFileSync5,
|
|
8767
|
+
writeFileSync as writeFileSync2,
|
|
10431
8768
|
unlinkSync
|
|
10432
8769
|
} from "fs";
|
|
10433
8770
|
import { mkdir } from "fs/promises";
|
|
10434
8771
|
import { join as join4 } from "path";
|
|
10435
8772
|
import { pipeline } from "stream/promises";
|
|
10436
|
-
var
|
|
8773
|
+
var log8 = createLogger("TonProxy");
|
|
10437
8774
|
var GITHUB_REPO = "xssnick/Tonutils-Proxy";
|
|
10438
8775
|
var BINARY_DIR = join4(TELETON_ROOT, "bin");
|
|
10439
8776
|
var PID_FILE = join4(TELETON_ROOT, "ton-proxy.pid");
|
|
@@ -10456,7 +8793,7 @@ var TonProxyManager = class {
|
|
|
10456
8793
|
}
|
|
10457
8794
|
/** Check if the binary exists on disk */
|
|
10458
8795
|
isInstalled() {
|
|
10459
|
-
return
|
|
8796
|
+
return existsSync6(this.getBinaryPath());
|
|
10460
8797
|
}
|
|
10461
8798
|
/** Whether the proxy process is currently running */
|
|
10462
8799
|
isRunning() {
|
|
@@ -10468,7 +8805,7 @@ var TonProxyManager = class {
|
|
|
10468
8805
|
*/
|
|
10469
8806
|
async install() {
|
|
10470
8807
|
const binaryName = getBinaryName();
|
|
10471
|
-
|
|
8808
|
+
log8.info(`Downloading TON Proxy binary (${binaryName})...`);
|
|
10472
8809
|
await mkdir(BINARY_DIR, { recursive: true });
|
|
10473
8810
|
const releaseUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
10474
8811
|
const releaseRes = await fetch(releaseUrl, {
|
|
@@ -10480,7 +8817,7 @@ var TonProxyManager = class {
|
|
|
10480
8817
|
const release = await releaseRes.json();
|
|
10481
8818
|
const tag = release.tag_name;
|
|
10482
8819
|
const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${binaryName}`;
|
|
10483
|
-
|
|
8820
|
+
log8.info(`Downloading ${downloadUrl}`);
|
|
10484
8821
|
const res = await fetch(downloadUrl);
|
|
10485
8822
|
if (!res.ok || !res.body) {
|
|
10486
8823
|
throw new Error(`Download failed: ${res.status} ${res.statusText}`);
|
|
@@ -10489,17 +8826,17 @@ var TonProxyManager = class {
|
|
|
10489
8826
|
const fileStream = createWriteStream(dest);
|
|
10490
8827
|
await pipeline(res.body, fileStream);
|
|
10491
8828
|
chmodSync(dest, 493);
|
|
10492
|
-
|
|
8829
|
+
log8.info(`TON Proxy installed: ${dest} (${tag})`);
|
|
10493
8830
|
}
|
|
10494
8831
|
/** Kill any orphan proxy process from a previous session */
|
|
10495
8832
|
killOrphan() {
|
|
10496
|
-
if (
|
|
8833
|
+
if (existsSync6(PID_FILE)) {
|
|
10497
8834
|
try {
|
|
10498
|
-
const pid = parseInt(
|
|
8835
|
+
const pid = parseInt(readFileSync5(PID_FILE, "utf-8").trim(), 10);
|
|
10499
8836
|
if (pid && !isNaN(pid)) {
|
|
10500
8837
|
try {
|
|
10501
8838
|
process.kill(pid, 0);
|
|
10502
|
-
|
|
8839
|
+
log8.warn(`Killing orphan TON Proxy (PID ${pid}) from previous session`);
|
|
10503
8840
|
process.kill(pid, "SIGTERM");
|
|
10504
8841
|
} catch {
|
|
10505
8842
|
}
|
|
@@ -10516,7 +8853,7 @@ var TonProxyManager = class {
|
|
|
10516
8853
|
const pidMatch = out.match(/pid=(\d+)/);
|
|
10517
8854
|
if (pidMatch) {
|
|
10518
8855
|
const pid = parseInt(pidMatch[1], 10);
|
|
10519
|
-
|
|
8856
|
+
log8.warn(`Port ${this.config.port} occupied by PID ${pid}, killing it`);
|
|
10520
8857
|
try {
|
|
10521
8858
|
process.kill(pid, "SIGTERM");
|
|
10522
8859
|
} catch {
|
|
@@ -10529,22 +8866,22 @@ var TonProxyManager = class {
|
|
|
10529
8866
|
/** Write PID to file for orphan detection */
|
|
10530
8867
|
writePidFile(pid) {
|
|
10531
8868
|
try {
|
|
10532
|
-
|
|
8869
|
+
writeFileSync2(PID_FILE, String(pid), { mode: 384 });
|
|
10533
8870
|
} catch {
|
|
10534
|
-
|
|
8871
|
+
log8.warn("Failed to write TON Proxy PID file");
|
|
10535
8872
|
}
|
|
10536
8873
|
}
|
|
10537
8874
|
/** Remove PID file */
|
|
10538
8875
|
removePidFile() {
|
|
10539
8876
|
try {
|
|
10540
|
-
if (
|
|
8877
|
+
if (existsSync6(PID_FILE)) unlinkSync(PID_FILE);
|
|
10541
8878
|
} catch {
|
|
10542
8879
|
}
|
|
10543
8880
|
}
|
|
10544
8881
|
/** Start the proxy process */
|
|
10545
8882
|
async start() {
|
|
10546
8883
|
if (this.isRunning()) {
|
|
10547
|
-
|
|
8884
|
+
log8.warn("TON Proxy is already running");
|
|
10548
8885
|
return;
|
|
10549
8886
|
}
|
|
10550
8887
|
this.restartCount = 0;
|
|
@@ -10555,7 +8892,7 @@ var TonProxyManager = class {
|
|
|
10555
8892
|
}
|
|
10556
8893
|
const binaryPath = this.getBinaryPath();
|
|
10557
8894
|
const port = String(this.config.port);
|
|
10558
|
-
|
|
8895
|
+
log8.info(`Starting TON Proxy on 127.0.0.1:${port}`);
|
|
10559
8896
|
this.process = spawn(binaryPath, ["-addr", `127.0.0.1:${port}`], {
|
|
10560
8897
|
cwd: BINARY_DIR,
|
|
10561
8898
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -10563,24 +8900,24 @@ var TonProxyManager = class {
|
|
|
10563
8900
|
});
|
|
10564
8901
|
this.process.stdout?.on("data", (chunk) => {
|
|
10565
8902
|
const line = chunk.toString().trim();
|
|
10566
|
-
if (line)
|
|
8903
|
+
if (line) log8.debug(`[proxy] ${line}`);
|
|
10567
8904
|
});
|
|
10568
8905
|
this.process.stderr?.on("data", (chunk) => {
|
|
10569
8906
|
const line = chunk.toString().trim();
|
|
10570
|
-
if (line)
|
|
8907
|
+
if (line) log8.warn(`[proxy:err] ${line}`);
|
|
10571
8908
|
});
|
|
10572
8909
|
this.process.on("exit", (code, signal) => {
|
|
10573
|
-
|
|
8910
|
+
log8.info(`TON Proxy exited (code=${code}, signal=${signal})`);
|
|
10574
8911
|
this.process = null;
|
|
10575
8912
|
this.removePidFile();
|
|
10576
8913
|
if (code !== 0 && code !== null && this.restartCount < this.maxRestarts) {
|
|
10577
8914
|
this.restartCount++;
|
|
10578
|
-
|
|
10579
|
-
this.start().catch((err) =>
|
|
8915
|
+
log8.warn(`Auto-restarting TON Proxy (attempt ${this.restartCount}/${this.maxRestarts})`);
|
|
8916
|
+
this.start().catch((err) => log8.error({ err }, "Failed to auto-restart TON Proxy"));
|
|
10580
8917
|
}
|
|
10581
8918
|
});
|
|
10582
8919
|
this.process.on("error", (err) => {
|
|
10583
|
-
|
|
8920
|
+
log8.error({ err }, "TON Proxy process error");
|
|
10584
8921
|
this.process = null;
|
|
10585
8922
|
});
|
|
10586
8923
|
this.startHealthCheck();
|
|
@@ -10598,14 +8935,14 @@ var TonProxyManager = class {
|
|
|
10598
8935
|
});
|
|
10599
8936
|
});
|
|
10600
8937
|
if (this.process?.pid) this.writePidFile(this.process.pid);
|
|
10601
|
-
|
|
8938
|
+
log8.info(`TON Proxy running on 127.0.0.1:${port} (PID ${this.process?.pid})`);
|
|
10602
8939
|
}
|
|
10603
8940
|
/** Stop the proxy process gracefully */
|
|
10604
8941
|
async stop() {
|
|
10605
8942
|
this.stopHealthCheck();
|
|
10606
8943
|
if (!this.process) return;
|
|
10607
8944
|
this.maxRestarts = 0;
|
|
10608
|
-
|
|
8945
|
+
log8.info("Stopping TON Proxy...");
|
|
10609
8946
|
return new Promise((resolve2) => {
|
|
10610
8947
|
if (!this.process) {
|
|
10611
8948
|
resolve2();
|
|
@@ -10613,7 +8950,7 @@ var TonProxyManager = class {
|
|
|
10613
8950
|
}
|
|
10614
8951
|
const forceKill = setTimeout(() => {
|
|
10615
8952
|
if (this.process) {
|
|
10616
|
-
|
|
8953
|
+
log8.warn("TON Proxy did not exit gracefully, sending SIGKILL");
|
|
10617
8954
|
this.process.kill("SIGKILL");
|
|
10618
8955
|
}
|
|
10619
8956
|
}, KILL_GRACE_MS);
|
|
@@ -10632,10 +8969,10 @@ var TonProxyManager = class {
|
|
|
10632
8969
|
await this.stop();
|
|
10633
8970
|
}
|
|
10634
8971
|
const binaryPath = this.getBinaryPath();
|
|
10635
|
-
if (
|
|
8972
|
+
if (existsSync6(binaryPath)) {
|
|
10636
8973
|
const { unlink } = await import("fs/promises");
|
|
10637
8974
|
await unlink(binaryPath);
|
|
10638
|
-
|
|
8975
|
+
log8.info(`TON Proxy binary removed: ${binaryPath}`);
|
|
10639
8976
|
}
|
|
10640
8977
|
}
|
|
10641
8978
|
/** Get proxy status for WebUI / tools */
|
|
@@ -10669,7 +9006,7 @@ var TonProxyManager = class {
|
|
|
10669
9006
|
}).catch(() => null);
|
|
10670
9007
|
clearTimeout(timeout);
|
|
10671
9008
|
if (!res) {
|
|
10672
|
-
|
|
9009
|
+
log8.warn("TON Proxy health check failed (no response)");
|
|
10673
9010
|
}
|
|
10674
9011
|
} catch {
|
|
10675
9012
|
}
|
|
@@ -10726,7 +9063,7 @@ var tonProxyStatusExecutor = async () => {
|
|
|
10726
9063
|
};
|
|
10727
9064
|
|
|
10728
9065
|
// src/ton-proxy/module.ts
|
|
10729
|
-
var
|
|
9066
|
+
var log9 = createLogger("TonProxyModule");
|
|
10730
9067
|
var manager = null;
|
|
10731
9068
|
function getTonProxyManager() {
|
|
10732
9069
|
return manager;
|
|
@@ -10753,9 +9090,9 @@ var tonProxyModule = {
|
|
|
10753
9090
|
setProxyManager(manager);
|
|
10754
9091
|
try {
|
|
10755
9092
|
await manager.start();
|
|
10756
|
-
|
|
9093
|
+
log9.info(`TON Proxy started on port ${proxyConfig.port}`);
|
|
10757
9094
|
} catch (err) {
|
|
10758
|
-
|
|
9095
|
+
log9.error({ err }, "Failed to start TON Proxy");
|
|
10759
9096
|
manager = null;
|
|
10760
9097
|
}
|
|
10761
9098
|
},
|
|
@@ -10812,13 +9149,15 @@ export {
|
|
|
10812
9149
|
validateReadPath,
|
|
10813
9150
|
validateWritePath,
|
|
10814
9151
|
validateDirectory,
|
|
9152
|
+
appendToDailyLog,
|
|
9153
|
+
writeSummaryToDailyLog,
|
|
10815
9154
|
sanitizeForPrompt,
|
|
10816
9155
|
sanitizeForContext,
|
|
10817
9156
|
clearPromptCache,
|
|
10818
9157
|
loadSoul,
|
|
10819
|
-
|
|
9158
|
+
buildSystemPrompt,
|
|
10820
9159
|
getTokenUsage,
|
|
10821
|
-
|
|
9160
|
+
accumulateTokenUsage,
|
|
10822
9161
|
writePluginSecret,
|
|
10823
9162
|
deletePluginSecret,
|
|
10824
9163
|
listPluginSecretKeys,
|
|
@@ -10851,12 +9190,6 @@ export {
|
|
|
10851
9190
|
adaptPlugin,
|
|
10852
9191
|
ensurePluginDeps,
|
|
10853
9192
|
loadEnhancedPlugins,
|
|
10854
|
-
CONFIGURABLE_KEYS,
|
|
10855
|
-
getNestedValue,
|
|
10856
|
-
setNestedValue,
|
|
10857
|
-
deleteNestedValue,
|
|
10858
|
-
readRawConfig,
|
|
10859
|
-
writeRawConfig,
|
|
10860
9193
|
TonProxyManager,
|
|
10861
9194
|
getTonProxyManager,
|
|
10862
9195
|
setTonProxyManager,
|