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.
Files changed (42) hide show
  1. package/README.md +241 -301
  2. package/dist/{bootstrap-DDFVEMYI.js → bootstrap-SPDT3XBQ.js} +5 -7
  3. package/dist/{chunk-GHMXWAXI.js → chunk-2MZP75SH.js} +72 -202
  4. package/dist/chunk-35X3V6OW.js +139 -0
  5. package/dist/{chunk-LVTKJQ7O.js → chunk-4KURCUWD.js} +1 -1
  6. package/dist/{chunk-XDZDOKIF.js → chunk-5K4YDCVU.js} +1 -1
  7. package/dist/{chunk-7MWKT67G.js → chunk-6U6VA2OT.js} +520 -2187
  8. package/dist/{chunk-OIMAE24Q.js → chunk-7ZXUUDQQ.js} +18 -7
  9. package/dist/{chunk-AERHOXGC.js → chunk-FSL2MOYK.js} +236 -93
  10. package/dist/{chunk-LC4TV3KL.js → chunk-GUX6ZFVF.js} +1 -1
  11. package/dist/{chunk-CUE4UZXR.js → chunk-KYSAHDYE.js} +2 -2
  12. package/dist/{chunk-C4NKJT2Z.js → chunk-L3LPVF4Z.js} +1 -1
  13. package/dist/{chunk-EYWNOHMJ.js → chunk-L653KKCR.js} +1 -0
  14. package/dist/{chunk-ALKAAG4O.js → chunk-LD24DWWE.js} +4 -4
  15. package/dist/{chunk-33Z47EXI.js → chunk-LM6AL6LN.js} +2417 -867
  16. package/dist/chunk-M6M4DCDU.js +942 -0
  17. package/dist/{chunk-35MX4ZUI.js → chunk-PK3TVFBT.js} +2 -2
  18. package/dist/{chunk-2ERTYRHA.js → chunk-Z63KUQX4.js} +37 -13
  19. package/dist/cli/index.js +35 -25
  20. package/dist/{client-5KD25NOP.js → client-G62EZT6U.js} +3 -3
  21. package/dist/harden-permissions-6BLHRCQJ.js +100 -0
  22. package/dist/index.js +14 -14
  23. package/dist/{local-IHKJFQJS.js → local-HQ3UJ7KR.js} +2 -2
  24. package/dist/{memory-QMJRM3XJ.js → memory-BJH724PQ.js} +6 -5
  25. package/dist/{memory-hook-VUNWZ3NY.js → memory-hook-LUAKTXU5.js} +5 -5
  26. package/dist/{migrate-5VBAP52B.js → migrate-C4LBLOZH.js} +6 -5
  27. package/dist/{paths-XA2RJH4S.js → paths-WMVV7ZAJ.js} +1 -1
  28. package/dist/{server-JF6FX772.js → server-4J56HS62.js} +8 -15
  29. package/dist/{server-N4T7E25M.js → server-I6TYJ36S.js} +7 -15
  30. package/dist/{setup-server-IX3BFPPH.js → setup-server-VJ3MGUSM.js} +16 -17
  31. package/dist/{store-BY7S6IFN.js → store-2IGAMTES.js} +7 -6
  32. package/dist/{task-dependency-resolver-L6UUMTHK.js → task-dependency-resolver-CQ432Z7J.js} +1 -1
  33. package/dist/{task-executor-XBNJLUCS.js → task-executor-JELRREUV.js} +1 -1
  34. package/dist/{tool-index-FTERJSZK.js → tool-index-XPCMWBYY.js} +4 -4
  35. package/dist/{transcript-IM7G25OS.js → transcript-OEO3HA4Z.js} +2 -2
  36. package/dist/web/assets/{index-BfYCdwLI.js → index-Dn5ZH1Y6.js} +13 -13
  37. package/dist/web/assets/{index.es-DitvF-9H.js → index.es-eSR4Qv6s.js} +1 -1
  38. package/dist/web/index.html +1 -1
  39. package/package.json +2 -6
  40. package/src/templates/HEARTBEAT.md +5 -0
  41. package/dist/chunk-AEHTQI3H.js +0 -142
  42. package/dist/chunk-FUNF6H4W.js +0 -251
@@ -6,32 +6,12 @@ import {
6
6
  getWalletBalance,
7
7
  invalidateTonClientCache,
8
8
  loadWallet
9
- } from "./chunk-FUNF6H4W.js";
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-GHMXWAXI.js";
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
- COMPACTION_KEEP_RECENT,
55
- COMPACTION_MAX_MESSAGES,
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-EYWNOHMJ.js";
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 AI
280
+ var DEFAULT_SOUL = `# Teleton
337
281
 
338
- You are Teleton, a personal AI assistant that operates through Telegram.
282
+ You are Teleton, an autonomous AI agent operating through Telegram with TON blockchain capabilities.
339
283
 
340
- ## Personality
341
- - Helpful and concise
342
- - Direct and honest
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
- ## Guidelines
346
- - Keep responses short and actionable
347
- - Use markdown when appropriate
348
- - Respect user privacy
349
- - Be transparent about capabilities and limitations
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
- return parts.join("\n");
526
- }
527
-
528
- // src/constants/tools.ts
529
- var TELEGRAM_SEND_TOOLS = /* @__PURE__ */ new Set([
530
- "telegram_send_message",
531
- "telegram_send_gif",
532
- "telegram_send_voice",
533
- "telegram_send_sticker",
534
- "telegram_send_document",
535
- "telegram_send_photo",
536
- "telegram_send_video",
537
- "telegram_send_poll",
538
- "telegram_forward_message",
539
- "telegram_reply_message",
540
- "deal_propose"
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
- // src/memory/envelope.ts
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
- // src/memory/compaction.ts
633
- import { randomUUID } from "crypto";
634
- var DEFAULT_COMPACTION_CONFIG = {
635
- enabled: true,
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
- if (hasOrphanedToolResults(context.messages.slice(cutIndex))) {
756
- log2.warn(`Compaction: couldn't find clean cut point, keeping all messages`);
757
- return context;
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
- try {
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 log4 = createLogger("InlineRouter");
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
- log4.info(`Registered plugin "${name}" for inline routing`);
746
+ log2.info(`Registered plugin "${name}" for inline routing`);
2039
747
  }
2040
748
  unregisterPlugin(name) {
2041
749
  this.plugins.delete(name);
2042
- log4.info(`Unregistered plugin "${name}" from inline routing`);
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
- log4.error({ err: error }, `Plugin "${pluginName}" inline query handler failed`);
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
- log4.debug(`GramJS edit failed, falling back to Grammy: ${errMsg || error}`);
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
- log4.error({ err: error }, `Plugin "${pluginName}" callback handler failed`);
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
- log4.error({ err: error }, `Plugin "${pluginName}" chosen result handler failed`);
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 log5 = createLogger("PluginValidator");
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
- log5.warn(`[${pluginName}] tool is not an object, skipping`);
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
- log5.warn(`[${pluginName}] tool missing 'name', skipping`);
1083
+ log3.warn(`[${pluginName}] tool missing 'name', skipping`);
2376
1084
  continue;
2377
1085
  }
2378
1086
  if (!t.description || typeof t.description !== "string") {
2379
- log5.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
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
- log5.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
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 log6 = createLogger("TON");
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
- log6.error({ amount }, "Invalid transfer amount");
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
- log6.error({ err: e }, `Invalid recipient address: ${toAddress2}`);
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
- log6.error("Wallet not initialized");
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
- log6.info(`Sent ${amount} TON to ${toAddress2.slice(0, 8)}... - seqno: ${seqno}`);
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
- log6.error({ err: error }, "Error sending TON");
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 log7 = createLogger("Utils");
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
- log7.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
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 log8 = createLogger("Tools");
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
- log8.warn({ err: error }, "Asset list fetch failed, using stale cache");
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, log12) {
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
- log12.debug("dex.quoteSTONfi() failed:", err);
4813
+ log10.debug("dex.quoteSTONfi() failed:", err);
6106
4814
  return null;
6107
4815
  }
6108
4816
  }
6109
- async function getDedustQuote(fromAsset, toAsset, amount, slippage, log12) {
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
- log12.debug("dex.quoteDeDust() failed:", err);
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(log12) {
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, log12),
6322
- getDedustQuote(params.fromAsset, params.toAsset, params.amount, slippage, log12)
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 >= dedustOut) {
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
- log12
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
- log12
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, log12);
5086
+ return executeSTONfiSwap(params, log10);
6372
5087
  }
6373
5088
  if (params.dex === "dedust") {
6374
- return executeDedustSwap(params, log12);
5089
+ return executeDedustSwap(params, log10);
6375
5090
  }
6376
5091
  const quoteResult = await this.quote(params);
6377
- return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log12) : executeDedustSwap(params, log12);
5092
+ return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log10) : executeDedustSwap(params, log10);
6378
5093
  },
6379
5094
  async swapSTONfi(params) {
6380
- return executeSTONfiSwap(params, log12);
5095
+ return executeSTONfiSwap(params, log10);
6381
5096
  },
6382
5097
  async swapDeDust(params) {
6383
- return executeDedustSwap(params, log12);
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(log12) {
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
- log12.debug("dns.check() failed:", err);
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
- log12.debug(`dns.resolve() TonAPI error: ${response.status}`);
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
- log12.debug("dns.resolve() failed:", err);
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
- log12.debug(`dns.getAuctions() TonAPI error: ${response.status}`);
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
- log12.debug("dns.getAuctions() failed:", err);
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, log12) {
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
- log12.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
5526
+ log10.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
6812
5527
  }
6813
5528
  } catch (err) {
6814
- log12.error("Transaction cleanup failed:", err);
5529
+ log10.error("Transaction cleanup failed:", err);
6815
5530
  }
6816
5531
  }
6817
- function createTonSDK(log12, db) {
5532
+ function createTonSDK(log10, db) {
6818
5533
  return {
6819
5534
  getAddress() {
6820
5535
  try {
6821
5536
  return getWalletAddress();
6822
5537
  } catch (err) {
6823
- log12.error("ton.getAddress() failed:", err);
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
- log12.error("ton.getBalance() failed:", err);
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
- log12.error("ton.getPrice() failed:", err);
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
- log12.error("ton.getTransactions() failed:", err);
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, log12);
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
- log12.error("ton.verifyPayment() failed:", err);
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
- log12.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
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
- log12.error("ton.getJettonBalances() failed:", err);
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
- log12.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
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
- log12.error("ton.getJettonInfo() failed:", err);
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
- log12.warn(
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
- log12.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
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
- log12.error("ton.getJettonWalletAddress() failed:", err);
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 boc = await withTxLock(async () => {
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
- return extMsg.toBoc().toString("base64");
5916
+ const boc = extMsg.toBoc().toString("base64");
5917
+ return { boc, seqno, validUntil, walletAddress: wallet.address.toRawString() };
7200
5918
  });
7201
5919
  return {
7202
- boc,
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 boc = await withTxLock(async () => {
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
- return extMsg.toBoc().toString("base64");
6028
+ const boc = extMsg.toBoc().toString("base64");
6029
+ return { boc, seqno, validUntil, walletAddress: wallet.address.toRawString() };
7302
6030
  });
7303
6031
  return {
7304
- boc,
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
- log12.error("ton.getPublicKey() failed:", err);
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
- log12.error(`ton.getNftItems() TonAPI error: ${response.status}`);
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
- log12.error("ton.getNftItems() failed:", err);
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
- log12.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
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
- log12.error("ton.getNftInfo() failed:", err);
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)}&currencies=usd,ton`
7390
6125
  );
7391
6126
  if (!response.ok) {
7392
- log12.debug(`ton.getJettonPrice() TonAPI error: ${response.status}`);
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
- log12.debug("ton.getJettonPrice() failed:", err);
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
- log12.debug(`ton.getJettonHolders() TonAPI error: ${holdersResponse.status}`);
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
- log12.debug("ton.getJettonHolders() failed:", err);
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
- if (attrs.volume_usd?.h24) {
7486
- volume24h = `$${parseFloat(attrs.volume_usd.h24).toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
7487
- }
7488
- if (attrs.fdv_usd) {
7489
- fdv = `$${parseFloat(attrs.fdv_usd).toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
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
- log12.debug("ton.getJettonHistory() failed:", err);
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(log12)),
7518
- dns: Object.freeze(createDnsSDK(log12))
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, log12) {
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
- log12.error("telegram.searchMessages() failed:", err);
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
- log12.error("telegram.getScheduledMessages() failed:", err);
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, log12) {
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
- log12.error("telegram.getChatInfo() failed:", err);
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
- log12.error("telegram.getParticipants() failed:", err);
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
- log12.error("telegram.getDialogs() failed:", err);
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
- log12.error("telegram.getHistory() failed:", err);
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
- log12.error("telegram.getStarsTransactions() failed:", err);
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
- log12.error("telegram.getCollectibleInfo() failed:", err);
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
- log12.error("telegram.getUniqueGift() failed:", err);
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
- log12.error("telegram.getUniqueGiftValue() failed:", err);
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: readFileSync7, statSync: statSync2 } = await import("fs");
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 = readFileSync7(filePath);
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, log12) {
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
- log12.error("telegram.getMessages() failed:", err);
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
- log12.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
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, log12),
8994
- ...createTelegramSocialSDK(bridge, log12)
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, log12) {
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
- log12.debug(`Secret "${key}" resolved from env var ${envKey}`);
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
- log12.debug(`Secret "${key}" resolved from secrets store`);
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
- log12.debug(`Secret "${key}" resolved from pluginConfig`);
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, log12) {
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
- log12.warn("onInlineQuery called again \u2014 overwriting previous handler");
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
- log12.warn(`GramJS edit failed, falling back to Grammy: ${error?.errorMessage || error}`);
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
- log12.error(`Failed to edit inline message: ${error?.description || error}`);
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 log12 = createLogger2(opts.pluginName);
8265
+ const log10 = createLogger2(opts.pluginName);
9295
8266
  const safeDb = opts.db ? createSafeDb(opts.db) : null;
9296
- const ton = Object.freeze(createTonSDK(log12, safeDb));
9297
- const telegram = Object.freeze(createTelegramSDK(deps.bridge, log12));
9298
- const secrets = Object.freeze(createSecretsSDK(opts.pluginName, opts.pluginConfig, log12));
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(log12);
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
- log12.warn(`Hook registration unavailable \u2014 sdk.on() ignored`);
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
- log12.warn(`Hook "${hookName}" not declared in manifest \u2014 registration rejected`);
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
- log12.debug(`Hook "${hookName}" priority ${rawPriority} clamped to ${clampedPriority}`);
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
- log12.warn(
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 log9 = createLogger("PluginLoader");
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
- log9.warn(
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
- log9.warn(
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
- log9.info(`[${pluginEntry}] Installing dependencies...`);
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
- log9.info(`[${pluginEntry}] Dependencies installed`);
8662
+ log7.info(`[${pluginEntry}] Dependencies installed`);
9692
8663
  } catch (err) {
9693
- log9.error(`[${pluginEntry}] Failed to install deps: ${String(err).slice(0, 300)}`);
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
- log9.error(
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
- log9.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
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
- log9.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
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
- log9.error(`Plugin "${entry}" failed to adapt: ${err instanceof Error ? err.message : err}`);
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/config/configurable-keys.ts
9780
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6 } from "fs";
9781
- import { parse, stringify } from "yaml";
9782
- var noValidation = () => void 0;
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 enumValidator(options) {
9794
- return (v) => options.includes(v) ? void 0 : `Must be one of: ${options.join(", ")}`;
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 existsSync7,
8763
+ existsSync as existsSync6,
10427
8764
  chmodSync,
10428
8765
  createWriteStream,
10429
- readFileSync as readFileSync6,
10430
- writeFileSync as writeFileSync3,
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 log10 = createLogger("TonProxy");
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 existsSync7(this.getBinaryPath());
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
- log10.info(`Downloading TON Proxy binary (${binaryName})...`);
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
- log10.info(`Downloading ${downloadUrl}`);
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
- log10.info(`TON Proxy installed: ${dest} (${tag})`);
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 (existsSync7(PID_FILE)) {
8833
+ if (existsSync6(PID_FILE)) {
10497
8834
  try {
10498
- const pid = parseInt(readFileSync6(PID_FILE, "utf-8").trim(), 10);
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
- log10.warn(`Killing orphan TON Proxy (PID ${pid}) from previous session`);
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
- log10.warn(`Port ${this.config.port} occupied by PID ${pid}, killing it`);
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
- writeFileSync3(PID_FILE, String(pid), { mode: 384 });
8869
+ writeFileSync2(PID_FILE, String(pid), { mode: 384 });
10533
8870
  } catch {
10534
- log10.warn("Failed to write TON Proxy PID file");
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 (existsSync7(PID_FILE)) unlinkSync(PID_FILE);
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
- log10.warn("TON Proxy is already running");
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
- log10.info(`Starting TON Proxy on 127.0.0.1:${port}`);
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) log10.debug(`[proxy] ${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) log10.warn(`[proxy:err] ${line}`);
8907
+ if (line) log8.warn(`[proxy:err] ${line}`);
10571
8908
  });
10572
8909
  this.process.on("exit", (code, signal) => {
10573
- log10.info(`TON Proxy exited (code=${code}, signal=${signal})`);
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
- log10.warn(`Auto-restarting TON Proxy (attempt ${this.restartCount}/${this.maxRestarts})`);
10579
- this.start().catch((err) => log10.error({ err }, "Failed to auto-restart TON Proxy"));
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
- log10.error({ err }, "TON Proxy process error");
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
- log10.info(`TON Proxy running on 127.0.0.1:${port} (PID ${this.process?.pid})`);
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
- log10.info("Stopping TON Proxy...");
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
- log10.warn("TON Proxy did not exit gracefully, sending SIGKILL");
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 (existsSync7(binaryPath)) {
8972
+ if (existsSync6(binaryPath)) {
10636
8973
  const { unlink } = await import("fs/promises");
10637
8974
  await unlink(binaryPath);
10638
- log10.info(`TON Proxy binary removed: ${binaryPath}`);
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
- log10.warn("TON Proxy health check failed (no response)");
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 log11 = createLogger("TonProxyModule");
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
- log11.info(`TON Proxy started on port ${proxyConfig.port}`);
9093
+ log9.info(`TON Proxy started on port ${proxyConfig.port}`);
10757
9094
  } catch (err) {
10758
- log11.error({ err }, "Failed to start TON Proxy");
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
- TELEGRAM_SEND_TOOLS,
9158
+ buildSystemPrompt,
10820
9159
  getTokenUsage,
10821
- AgentRuntime,
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,