teleton 0.8.4 → 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 (40) hide show
  1. package/README.md +16 -12
  2. package/dist/{bootstrap-NNEI3Z5H.js → bootstrap-SPDT3XBQ.js} +4 -4
  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-LZQOX6YY.js → chunk-6U6VA2OT.js} +512 -1522
  8. package/dist/{chunk-5LOHRZYY.js → chunk-7ZXUUDQQ.js} +5 -5
  9. package/dist/{chunk-NH2CNRKJ.js → chunk-FSL2MOYK.js} +14 -3
  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-UMUONAD6.js → chunk-LM6AL6LN.js} +2413 -861
  16. package/dist/{chunk-JROBTXWY.js → chunk-M6M4DCDU.js} +36 -2
  17. package/dist/{chunk-35MX4ZUI.js → chunk-PK3TVFBT.js} +2 -2
  18. package/dist/{chunk-G7PCW63M.js → chunk-Z63KUQX4.js} +29 -5
  19. package/dist/cli/index.js +28 -15
  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 -13
  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-WWGVDFPW.js → server-4J56HS62.js} +8 -14
  29. package/dist/{server-AJCOURH7.js → server-I6TYJ36S.js} +7 -14
  30. package/dist/{setup-server-VDY64CWW.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
@@ -6,26 +6,12 @@ import {
6
6
  getWalletBalance,
7
7
  invalidateTonClientCache,
8
8
  loadWallet
9
- } from "./chunk-JROBTXWY.js";
9
+ } from "./chunk-M6M4DCDU.js";
10
10
  import {
11
- getOrCreateSession,
12
- getSession,
13
- resetSession,
14
- resetSessionWithPolicy,
15
- shouldResetSession,
16
- updateSession
17
- } from "./chunk-XDZDOKIF.js";
18
- import {
19
- ContextBuilder,
20
11
  createDbWrapper,
21
- getDatabase,
22
12
  migrateFromMainDb,
23
13
  openModuleDb
24
- } from "./chunk-GHMXWAXI.js";
25
- import {
26
- saveSessionMemory,
27
- summarizeWithFallback
28
- } from "./chunk-ALKAAG4O.js";
14
+ } from "./chunk-35X3V6OW.js";
29
15
  import {
30
16
  getErrorMessage,
31
17
  isHttpError
@@ -45,38 +31,8 @@ import {
45
31
  tonapiFetch
46
32
  } from "./chunk-VFA7QMCZ.js";
47
33
  import {
48
- COMPACTION_KEEP_RECENT,
49
- COMPACTION_MAX_MESSAGES,
50
- COMPACTION_MAX_TOKENS_RATIO,
51
- COMPACTION_SOFT_THRESHOLD_RATIO,
52
- CONTEXT_MAX_RECENT_MESSAGES,
53
- CONTEXT_MAX_RELEVANT_CHUNKS,
54
- CONTEXT_OVERFLOW_SUMMARY_MESSAGES,
55
- DEFAULT_CONTEXT_WINDOW,
56
- DEFAULT_MAX_SUMMARY_TOKENS,
57
- DEFAULT_MAX_TOKENS,
58
- DEFAULT_SOFT_THRESHOLD_TOKENS,
59
- EMBEDDING_QUERY_MAX_CHARS,
60
- FALLBACK_SOFT_THRESHOLD_TOKENS,
61
- MASKING_KEEP_RECENT_COUNT,
62
- MAX_TOOL_RESULT_SIZE,
63
- MEMORY_FLUSH_RECENT_MESSAGES,
64
- PAYMENT_TOLERANCE_RATIO,
65
- RATE_LIMIT_MAX_RETRIES,
66
- RESULT_TRUNCATION_KEEP_CHARS,
67
- RESULT_TRUNCATION_THRESHOLD,
68
- SERVER_ERROR_MAX_RETRIES,
69
- TOOL_CONCURRENCY_LIMIT
70
- } from "./chunk-C4NKJT2Z.js";
71
- import {
72
- chatWithContext,
73
- getEffectiveApiKey,
74
- getProviderModel,
75
- loadContextFromTranscript
76
- } from "./chunk-LVTKJQ7O.js";
77
- import {
78
- getProviderMetadata
79
- } from "./chunk-6OOHHJ4N.js";
34
+ PAYMENT_TOLERANCE_RATIO
35
+ } from "./chunk-L3LPVF4Z.js";
80
36
  import {
81
37
  fetchWithTimeout
82
38
  } from "./chunk-XQUHC3JZ.js";
@@ -89,17 +45,12 @@ import {
89
45
  RETRY_DEFAULT_MAX_DELAY_MS,
90
46
  RETRY_DEFAULT_TIMEOUT_MS
91
47
  } from "./chunk-R4YSJ4EY.js";
92
- import {
93
- appendToTranscript,
94
- archiveTranscript,
95
- transcriptExists
96
- } from "./chunk-LC4TV3KL.js";
97
48
  import {
98
49
  ALLOWED_EXTENSIONS,
99
50
  TELETON_ROOT,
100
51
  WORKSPACE_PATHS,
101
52
  WORKSPACE_ROOT
102
- } from "./chunk-EYWNOHMJ.js";
53
+ } from "./chunk-L653KKCR.js";
103
54
  import {
104
55
  createLogger
105
56
  } from "./chunk-NQ6FZKCE.js";
@@ -326,20 +277,30 @@ var SOUL_PATHS = [WORKSPACE_PATHS.SOUL];
326
277
  var STRATEGY_PATHS = [WORKSPACE_PATHS.STRATEGY];
327
278
  var SECURITY_PATHS = [WORKSPACE_PATHS.SECURITY];
328
279
  var MEMORY_PATH = WORKSPACE_PATHS.MEMORY;
329
- var DEFAULT_SOUL = `# Teleton AI
280
+ var DEFAULT_SOUL = `# Teleton
330
281
 
331
- 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.
332
283
 
333
- ## Personality
334
- - Helpful and concise
335
- - Direct and honest
336
- - 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.
337
287
 
338
- ## Guidelines
339
- - Keep responses short and actionable
340
- - Use markdown when appropriate
341
- - Respect user privacy
342
- - 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.
343
304
  `;
344
305
  var fileCache = /* @__PURE__ */ new Map();
345
306
  var FILE_CACHE_TTL = 6e4;
@@ -410,6 +371,15 @@ ${sanitizeForContext(persistentMemory)}`);
410
371
  }
411
372
  return parts.join("\n\n---\n\n");
412
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
+ }
413
383
  function buildSystemPrompt(options) {
414
384
  const soul = options.soul ?? loadSoul();
415
385
  const parts = [soul];
@@ -449,6 +419,10 @@ You have a personal workspace at \`~/.teleton/workspace/\` where you can store a
449
419
  - \`workspace_rename\` - Rename or move a file
450
420
  - \`workspace_info\` - Get workspace stats
451
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
+
452
426
  **Tips:**
453
427
  - Save interesting memes to \`memes/\` with descriptive names for easy retrieval
454
428
  - Use \`memory_write\` for important facts (goes to MEMORY.md)
@@ -474,6 +448,18 @@ You are owned and operated by: ${ownerLabel}
474
448
  When the owner gives instructions, follow them with higher trust.`
475
449
  );
476
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
+ }
477
463
  const includeMemory = options.includeMemory ?? true;
478
464
  if (includeMemory) {
479
465
  const memoryContext = loadMemoryContext();
@@ -515,1312 +501,41 @@ Your conversation context is approaching the limit and may be compacted soon.
515
501
  - \`target: "daily"\` for session notes, events, temporary context
516
502
  `);
517
503
  }
518
- return parts.join("\n");
519
- }
520
-
521
- // src/constants/tools.ts
522
- var TELEGRAM_SEND_TOOLS = /* @__PURE__ */ new Set([
523
- "telegram_send_message",
524
- "telegram_send_gif",
525
- "telegram_send_voice",
526
- "telegram_send_sticker",
527
- "telegram_send_document",
528
- "telegram_send_photo",
529
- "telegram_send_video",
530
- "telegram_send_poll",
531
- "telegram_forward_message",
532
- "telegram_reply_message",
533
- "deal_propose"
534
- ]);
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.
535
525
 
536
- // src/memory/envelope.ts
537
- function formatElapsed(elapsedMs) {
538
- if (!Number.isFinite(elapsedMs) || elapsedMs < 0) {
539
- return "";
540
- }
541
- const seconds = Math.floor(elapsedMs / 1e3);
542
- if (seconds < 60) {
543
- return `${seconds}s`;
544
- }
545
- const minutes = Math.floor(seconds / 60);
546
- if (minutes < 60) {
547
- return `${minutes}m`;
548
- }
549
- const hours = Math.floor(minutes / 60);
550
- if (hours < 24) {
551
- return `${hours}h`;
552
- }
553
- const days = Math.floor(hours / 24);
554
- return `${days}d`;
555
- }
556
- function formatTimestamp(timestamp) {
557
- const date = new Date(timestamp);
558
- const yyyy = date.getFullYear();
559
- const mm = String(date.getMonth() + 1).padStart(2, "0");
560
- const dd = String(date.getDate()).padStart(2, "0");
561
- const hh = String(date.getHours()).padStart(2, "0");
562
- const min = String(date.getMinutes()).padStart(2, "0");
563
- const tz = Intl.DateTimeFormat("en", {
564
- timeZoneName: "short"
565
- }).formatToParts(date).find((part) => part.type === "timeZoneName")?.value;
566
- return `${yyyy}-${mm}-${dd} ${hh}:${min}${tz ? ` ${tz}` : ""}`;
567
- }
568
- function buildSenderLabel(params) {
569
- const name = params.senderName ? sanitizeForPrompt(params.senderName) : void 0;
570
- const username = params.senderUsername ? `@${sanitizeForPrompt(params.senderUsername)}` : void 0;
571
- const idTag = params.senderId ? `id:${params.senderId}` : void 0;
572
- const primary = name || username;
573
- const meta = [username, idTag].filter((v) => v && v !== primary);
574
- let label;
575
- if (primary) {
576
- label = meta.length > 0 ? `${primary} (${meta.join(", ")})` : primary;
577
- } else {
578
- label = idTag || "unknown";
579
- }
580
- if (params.senderRank) {
581
- label = `[${sanitizeForPrompt(params.senderRank)}] ${label}`;
582
- }
583
- return label;
584
- }
585
- function formatMessageEnvelope(params) {
586
- const parts = [params.channel];
587
- const senderLabel = buildSenderLabel(params);
588
- if (!params.isGroup) {
589
- parts.push(senderLabel);
590
- }
591
- if (params.previousTimestamp) {
592
- const elapsed = formatElapsed(params.timestamp - params.previousTimestamp);
593
- if (elapsed) {
594
- parts.push(`+${elapsed}`);
595
- }
596
- }
597
- const ts = formatTimestamp(params.timestamp);
598
- parts.push(ts);
599
- const header = `[${parts.join(" ")}]`;
600
- const safeBody = sanitizeForContext(params.body.replace(/<\/?user_message>/gi, ""));
601
- let body = params.isGroup ? `${senderLabel}: <user_message>${safeBody}</user_message>` : `<user_message>${safeBody}</user_message>`;
602
- if (params.hasMedia && params.mediaType) {
603
- const mediaEmoji = {
604
- photo: "\u{1F4F7}",
605
- video: "\u{1F3AC}",
606
- audio: "\u{1F3B5}",
607
- voice: "\u{1F3A4}",
608
- document: "\u{1F4CE}",
609
- sticker: "\u{1F3A8}"
610
- }[params.mediaType] || "\u{1F4CE}";
611
- const msgIdHint = params.messageId ? ` msg_id=${params.messageId}` : "";
612
- body = `[${mediaEmoji} ${params.mediaType}${msgIdHint}] ${body}`;
613
- }
614
- if (params.replyContext) {
615
- const sender = params.replyContext.isAgent ? "agent" : sanitizeForPrompt(params.replyContext.senderName ?? "unknown");
616
- let quotedText = sanitizeForContext(params.replyContext.text);
617
- if (quotedText.length > 200) quotedText = quotedText.slice(0, 200) + "...";
618
- return `${header}
619
- [\u21A9 reply to ${sender}: "${quotedText}"]
620
- ${body}`;
621
- }
622
- return `${header} ${body}`;
623
- }
526
+ ${heartbeatContent}
624
527
 
625
- // src/memory/compaction.ts
626
- import { randomUUID } from "crypto";
627
- var DEFAULT_COMPACTION_CONFIG = {
628
- enabled: true,
629
- maxMessages: COMPACTION_MAX_MESSAGES,
630
- maxTokens: DEFAULT_MAX_TOKENS,
631
- keepRecentMessages: COMPACTION_KEEP_RECENT,
632
- memoryFlushEnabled: true,
633
- softThresholdTokens: DEFAULT_SOFT_THRESHOLD_TOKENS
634
- };
635
- var log2 = createLogger("Memory");
636
- function estimateContextTokens(context) {
637
- let charCount = 0;
638
- if (context.systemPrompt) {
639
- charCount += context.systemPrompt.length;
640
- }
641
- for (const message of context.messages) {
642
- if (message.role === "user") {
643
- if (typeof message.content === "string") {
644
- charCount += message.content.length;
645
- } else if (Array.isArray(message.content)) {
646
- for (const block of message.content) {
647
- if (block.type === "text") charCount += block.text.length;
648
- }
649
- }
650
- } else if (message.role === "assistant") {
651
- for (const block of message.content) {
652
- if (block.type === "text") {
653
- charCount += block.text.length;
654
- }
655
- }
656
- }
657
- }
658
- return Math.ceil(charCount / 4);
659
- }
660
- function shouldFlushMemory(context, config, tokenCount) {
661
- if (!config.enabled || !config.memoryFlushEnabled) {
662
- return false;
663
- }
664
- const tokens = tokenCount ?? estimateContextTokens(context);
665
- const softThreshold = config.softThresholdTokens ?? FALLBACK_SOFT_THRESHOLD_TOKENS;
666
- if (tokens >= softThreshold) {
667
- log2.info(`Memory flush needed: ~${tokens} tokens (soft threshold: ${softThreshold})`);
668
- return true;
669
- }
670
- return false;
671
- }
672
- function flushMemoryToDailyLog(context) {
673
- const recentMessages = context.messages.slice(-MEMORY_FLUSH_RECENT_MESSAGES);
674
- const summary = [];
675
- summary.push("**Recent Context:**\n");
676
- for (const msg of recentMessages) {
677
- if (msg.role === "user") {
678
- const content = typeof msg.content === "string" ? msg.content : "[complex content]";
679
- summary.push(`- User: ${content.substring(0, 100)}${content.length > 100 ? "..." : ""}`);
680
- } else if (msg.role === "assistant") {
681
- const textBlocks = msg.content.filter((b) => b.type === "text");
682
- if (textBlocks.length > 0) {
683
- const text = textBlocks[0].text || "";
684
- summary.push(`- Assistant: ${text.substring(0, 100)}${text.length > 100 ? "..." : ""}`);
685
- }
686
- }
687
- }
688
- writeSummaryToDailyLog(summary.join("\n"));
689
- log2.info(`Memory flushed to daily log`);
690
- }
691
- function shouldCompact(context, config, tokenCount) {
692
- if (!config.enabled) {
693
- return false;
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.`);
694
532
  }
695
- const messageCount = context.messages.length;
696
- if (config.maxMessages && messageCount >= config.maxMessages) {
697
- log2.info(`Compaction needed: ${messageCount} messages (max: ${config.maxMessages})`);
698
- return true;
699
- }
700
- if (config.maxTokens) {
701
- const tokens = tokenCount ?? estimateContextTokens(context);
702
- if (tokens >= config.maxTokens) {
703
- log2.info(`Compaction needed: ~${tokens} tokens (max: ${config.maxTokens})`);
704
- return true;
705
- }
706
- }
707
- return false;
708
- }
709
- async function compactContext(context, config, apiKey, provider, utilityModel) {
710
- const keepCount = config.keepRecentMessages ?? 10;
711
- if (context.messages.length <= keepCount) {
712
- return context;
713
- }
714
- let cutIndex = context.messages.length - keepCount;
715
- const collectToolUseIds = (msgs) => {
716
- const ids = /* @__PURE__ */ new Set();
717
- for (const msg of msgs) {
718
- if (msg.role === "assistant" && Array.isArray(msg.content)) {
719
- for (const block of msg.content) {
720
- if (block.type === "toolCall") {
721
- if (block.id) ids.add(block.id);
722
- }
723
- }
724
- }
725
- }
726
- return ids;
727
- };
728
- const hasOrphanedToolResults = (msgs) => {
729
- const toolUseIds = collectToolUseIds(msgs);
730
- for (const msg of msgs) {
731
- if (msg.role === "toolResult") {
732
- if (msg.toolCallId && !toolUseIds.has(msg.toolCallId)) {
733
- return true;
734
- }
735
- }
736
- }
737
- return false;
738
- };
739
- let iterations = 0;
740
- while (cutIndex > 0 && iterations < 50) {
741
- const keptMessages = context.messages.slice(cutIndex);
742
- if (!hasOrphanedToolResults(keptMessages)) {
743
- break;
744
- }
745
- cutIndex--;
746
- iterations++;
747
- }
748
- if (hasOrphanedToolResults(context.messages.slice(cutIndex))) {
749
- log2.warn(`Compaction: couldn't find clean cut point, keeping all messages`);
750
- return context;
751
- }
752
- const recentMessages = context.messages.slice(cutIndex);
753
- const oldMessages = context.messages.slice(0, cutIndex);
754
- log2.info(
755
- `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"}_`
756
536
  );
757
- try {
758
- const result = await summarizeWithFallback({
759
- messages: oldMessages,
760
- apiKey,
761
- contextWindow: config.maxTokens ?? DEFAULT_CONTEXT_WINDOW,
762
- maxSummaryTokens: DEFAULT_MAX_SUMMARY_TOKENS,
763
- customInstructions: `Output a structured summary using EXACTLY these sections:
764
-
765
- ## User Intent
766
- What the user is trying to accomplish (1-2 sentences).
767
-
768
- ## Key Decisions
769
- Bullet list of decisions made and commitments agreed upon.
770
-
771
- ## Important Context
772
- Critical facts, preferences, constraints, or technical details needed for continuity.
773
-
774
- ## Actions Taken
775
- What was done: tools used, messages sent, transactions made (with specific values/addresses if relevant).
776
-
777
- ## Open Items
778
- Unfinished tasks, pending questions, or next steps.
779
-
780
- Keep each section concise. Omit a section if empty. Preserve specific names, numbers, and identifiers.`,
781
- provider,
782
- utilityModel
783
- });
784
- log2.info(`AI Summary: ${result.tokensUsed} tokens, ${result.chunksProcessed} chunks processed`);
785
- const summaryText = `[Auto-compacted ${oldMessages.length} messages]
786
-
787
- ${result.summary}`;
788
- const summaryMessage = {
789
- role: "user",
790
- content: summaryText,
791
- timestamp: oldMessages[0]?.timestamp ?? Date.now()
792
- };
793
- return {
794
- ...context,
795
- messages: [summaryMessage, ...recentMessages]
796
- };
797
- } catch (error) {
798
- log2.error({ err: error }, "AI summarization failed, using fallback");
799
- const summaryText = `[Auto-compacted: ${oldMessages.length} earlier messages from this conversation]`;
800
- const summaryMessage = {
801
- role: "user",
802
- content: summaryText,
803
- timestamp: oldMessages[0]?.timestamp ?? Date.now()
804
- };
805
- return {
806
- ...context,
807
- messages: [summaryMessage, ...recentMessages]
808
- };
809
- }
810
- }
811
- async function compactAndSaveTranscript(sessionId, context, config, apiKey, chatId, provider, utilityModel) {
812
- const newSessionId = randomUUID();
813
- log2.info(`Creating compacted transcript: ${sessionId} \u2192 ${newSessionId}`);
814
- if (chatId) {
815
- await saveSessionMemory({
816
- oldSessionId: sessionId,
817
- newSessionId,
818
- context,
819
- chatId,
820
- apiKey,
821
- provider,
822
- utilityModel
823
- });
824
- }
825
- const compactedContext = await compactContext(context, config, apiKey, provider, utilityModel);
826
- for (const message of compactedContext.messages) {
827
- appendToTranscript(newSessionId, message);
828
- }
829
- return newSessionId;
830
- }
831
- var CompactionManager = class {
832
- config;
833
- constructor(config = DEFAULT_COMPACTION_CONFIG) {
834
- this.config = config;
835
- }
836
- async checkAndCompact(sessionId, context, apiKey, chatId, provider, utilityModel) {
837
- const tokenCount = estimateContextTokens(context);
838
- if (shouldFlushMemory(context, this.config, tokenCount)) {
839
- flushMemoryToDailyLog(context);
840
- }
841
- if (!shouldCompact(context, this.config, tokenCount)) {
842
- return null;
843
- }
844
- if (this.config.memoryFlushEnabled) {
845
- flushMemoryToDailyLog(context);
846
- }
847
- log2.info(`Auto-compacting session ${sessionId}`);
848
- const newSessionId = await compactAndSaveTranscript(
849
- sessionId,
850
- context,
851
- this.config,
852
- apiKey,
853
- chatId,
854
- provider,
855
- utilityModel
856
- );
857
- log2.info(`Compaction complete: ${newSessionId}`);
858
- return newSessionId;
859
- }
860
- updateConfig(config) {
861
- this.config = { ...this.config, ...config };
862
- }
863
- getConfig() {
864
- return { ...this.config };
865
- }
866
- };
867
-
868
- // src/memory/observation-masking.ts
869
- var DEFAULT_MASKING_CONFIG = {
870
- keepRecentCount: MASKING_KEEP_RECENT_COUNT,
871
- keepErrorResults: true,
872
- truncationThreshold: RESULT_TRUNCATION_THRESHOLD,
873
- truncationKeepChars: RESULT_TRUNCATION_KEEP_CHARS
874
- };
875
- var isCocoonToolResult = (msg) => msg.role === "user" && Array.isArray(msg.content) && msg.content.some((c2) => c2.type === "text" && c2.text.includes("<tool_response>"));
876
- function isExempt(toolMsg, config, toolRegistry) {
877
- if (config.keepErrorResults && toolMsg.isError) return true;
878
- if (toolRegistry && toolRegistry.getToolCategory(toolMsg.toolName) === "data-bearing")
879
- return true;
880
- return false;
881
- }
882
- function truncateToolResult(text, keepChars) {
883
- try {
884
- const parsed = JSON.parse(text);
885
- if (parsed.data?.summary) {
886
- return JSON.stringify({
887
- success: parsed.success,
888
- data: { summary: parsed.data.summary, _truncated: true }
889
- });
890
- }
891
- if (parsed.data?.message) {
892
- return JSON.stringify({
893
- success: parsed.success,
894
- data: { summary: parsed.data.message, _truncated: true }
895
- });
896
- }
897
- } catch {
898
- }
899
- return text.slice(0, keepChars) + `
900
- ...[truncated, original: ${text.length} chars]`;
901
- }
902
- function maskOldToolResults(messages, options) {
903
- const config = options?.config ?? DEFAULT_MASKING_CONFIG;
904
- const toolRegistry = options?.toolRegistry;
905
- const iterStart = options?.currentIterationStartIndex;
906
- const toolResults = messages.map((msg, index) => ({ msg, index })).filter(({ msg }) => msg.role === "toolResult" || isCocoonToolResult(msg));
907
- const needsMasking = toolResults.length > config.keepRecentCount;
908
- const needsTruncation = iterStart !== void 0 && config.truncationThreshold > 0;
909
- if (!needsMasking && !needsTruncation) {
910
- return messages;
911
- }
912
- const result = [...messages];
913
- if (needsMasking) {
914
- const toMask = toolResults.slice(0, -config.keepRecentCount);
915
- for (const { msg, index } of toMask) {
916
- if (isCocoonToolResult(msg)) {
917
- result[index] = {
918
- ...msg,
919
- content: [{ type: "text", text: "[Tool response masked]" }]
920
- };
921
- continue;
922
- }
923
- const toolMsg = msg;
924
- if (isExempt(toolMsg, config, toolRegistry)) continue;
925
- let summaryText = "";
926
- try {
927
- const textBlock = toolMsg.content.find((c2) => c2.type === "text");
928
- if (textBlock) {
929
- const parsed = JSON.parse(textBlock.text);
930
- if (parsed.data?.summary) {
931
- summaryText = ` - ${parsed.data.summary}`;
932
- } else if (parsed.data?.message) {
933
- summaryText = ` - ${parsed.data.message}`;
934
- }
935
- }
936
- } catch {
937
- }
938
- result[index] = {
939
- ...toolMsg,
940
- content: [
941
- {
942
- type: "text",
943
- text: `[Tool: ${toolMsg.toolName} - ${toolMsg.isError ? "ERROR" : "OK"}${summaryText}]`
944
- }
945
- ]
946
- };
947
- }
948
- }
949
- if (needsTruncation) {
950
- const recentResults = needsMasking ? toolResults.slice(-config.keepRecentCount) : toolResults;
951
- for (const { msg, index } of recentResults) {
952
- if (index >= iterStart) continue;
953
- if (isCocoonToolResult(msg)) {
954
- const userMsg = msg;
955
- if (!Array.isArray(userMsg.content)) continue;
956
- const textBlock2 = userMsg.content.find((c2) => c2.type === "text");
957
- if (textBlock2 && textBlock2.text.length > config.truncationThreshold) {
958
- result[index] = {
959
- ...userMsg,
960
- content: [
961
- {
962
- type: "text",
963
- text: truncateToolResult(textBlock2.text, config.truncationKeepChars)
964
- }
965
- ]
966
- };
967
- }
968
- continue;
969
- }
970
- const toolMsg = msg;
971
- if (isExempt(toolMsg, config, toolRegistry)) continue;
972
- const textBlock = toolMsg.content.find((c2) => c2.type === "text");
973
- if (!textBlock || textBlock.text.length <= config.truncationThreshold) continue;
974
- result[index] = {
975
- ...toolMsg,
976
- content: [
977
- {
978
- type: "text",
979
- text: truncateToolResult(textBlock.text, config.truncationKeepChars)
980
- }
981
- ]
982
- };
983
- }
984
- }
985
- return result;
986
- }
987
-
988
- // src/agent/runtime.ts
989
- var log3 = createLogger("Agent");
990
- var globalTokenUsage = { totalTokens: 0, totalCost: 0 };
991
- function getTokenUsage() {
992
- return { ...globalTokenUsage };
993
- }
994
- function isContextOverflowError(errorMessage) {
995
- if (!errorMessage) return false;
996
- const lower = errorMessage.toLowerCase();
997
- 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");
998
- }
999
- function isTrivialMessage(text) {
1000
- const stripped = text.trim();
1001
- if (!stripped) return true;
1002
- if (!/[a-zA-Z0-9а-яА-ЯёЁ]/.test(stripped)) return true;
1003
- 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;
1004
- return trivial.test(stripped);
1005
- }
1006
- function extractContextSummary(context, maxMessages = 10) {
1007
- const recentMessages = context.messages.slice(-maxMessages);
1008
- const summaryParts = [];
1009
- summaryParts.push("### Session Summary (Auto-saved before overflow reset)\n");
1010
- for (const msg of recentMessages) {
1011
- if (msg.role === "user") {
1012
- const content = typeof msg.content === "string" ? msg.content : "[complex]";
1013
- const bodyMatch = content.match(/\] (.+)/s);
1014
- const body = bodyMatch ? bodyMatch[1] : content;
1015
- summaryParts.push(`- **User**: ${body.substring(0, 150)}${body.length > 150 ? "..." : ""}`);
1016
- } else if (msg.role === "assistant") {
1017
- const textBlocks = msg.content.filter((b) => b.type === "text");
1018
- const toolBlocks = msg.content.filter((b) => b.type === "toolCall");
1019
- if (textBlocks.length > 0) {
1020
- const text = textBlocks[0].text || "";
1021
- summaryParts.push(
1022
- `- **Agent**: ${text.substring(0, 150)}${text.length > 150 ? "..." : ""}`
1023
- );
1024
- }
1025
- if (toolBlocks.length > 0) {
1026
- const toolNames = toolBlocks.map((b) => b.name).join(", ");
1027
- summaryParts.push(` - *Tools used: ${toolNames}*`);
1028
- }
1029
- } else if (msg.role === "toolResult") {
1030
- const status = msg.isError ? "ERROR" : "OK";
1031
- summaryParts.push(` - *Tool result: ${msg.toolName} \u2192 ${status}*`);
1032
- }
1033
- }
1034
- return summaryParts.join("\n");
537
+ return parts.join("\n");
1035
538
  }
1036
- var AgentRuntime = class {
1037
- config;
1038
- soul;
1039
- compactionManager;
1040
- contextBuilder = null;
1041
- toolRegistry = null;
1042
- embedder = null;
1043
- hookRunner;
1044
- userHookEvaluator;
1045
- constructor(config, soul, toolRegistry) {
1046
- this.config = config;
1047
- this.soul = soul ?? "";
1048
- this.toolRegistry = toolRegistry ?? null;
1049
- const provider = config.agent.provider || "anthropic";
1050
- try {
1051
- const model = getProviderModel(provider, config.agent.model);
1052
- const ctx = model.contextWindow;
1053
- this.compactionManager = new CompactionManager({
1054
- enabled: true,
1055
- maxMessages: COMPACTION_MAX_MESSAGES,
1056
- maxTokens: Math.floor(ctx * COMPACTION_MAX_TOKENS_RATIO),
1057
- keepRecentMessages: COMPACTION_KEEP_RECENT,
1058
- memoryFlushEnabled: true,
1059
- softThresholdTokens: Math.floor(ctx * COMPACTION_SOFT_THRESHOLD_RATIO)
1060
- });
1061
- } catch {
1062
- this.compactionManager = new CompactionManager(DEFAULT_COMPACTION_CONFIG);
1063
- }
1064
- }
1065
- setHookRunner(runner) {
1066
- this.hookRunner = runner;
1067
- }
1068
- setUserHookEvaluator(evaluator) {
1069
- this.userHookEvaluator = evaluator;
1070
- }
1071
- initializeContextBuilder(embedder, vectorEnabled) {
1072
- this.embedder = embedder;
1073
- const db = getDatabase().getDb();
1074
- this.contextBuilder = new ContextBuilder(db, embedder, vectorEnabled);
1075
- }
1076
- getToolRegistry() {
1077
- return this.toolRegistry;
1078
- }
1079
- async processMessage(opts) {
1080
- const {
1081
- chatId,
1082
- userMessage,
1083
- userName,
1084
- timestamp,
1085
- isGroup,
1086
- pendingContext,
1087
- toolContext,
1088
- senderUsername,
1089
- senderRank,
1090
- hasMedia,
1091
- mediaType,
1092
- messageId,
1093
- replyContext
1094
- } = opts;
1095
- const effectiveIsGroup = isGroup ?? false;
1096
- const processStartTime = Date.now();
1097
- try {
1098
- let userHookContext = "";
1099
- if (this.userHookEvaluator) {
1100
- const hookResult = this.userHookEvaluator.evaluate(userMessage);
1101
- if (hookResult.blocked) {
1102
- log3.info("Message blocked by keyword filter");
1103
- return { content: hookResult.blockMessage ?? "", toolCalls: [] };
1104
- }
1105
- if (hookResult.additionalContext) {
1106
- userHookContext = sanitizeForContext(hookResult.additionalContext);
1107
- }
1108
- }
1109
- let effectiveMessage = userMessage;
1110
- let hookMessageContext = "";
1111
- if (this.hookRunner) {
1112
- const msgEvent = {
1113
- chatId,
1114
- senderId: toolContext?.senderId ? String(toolContext.senderId) : chatId,
1115
- senderName: userName ?? "",
1116
- isGroup: effectiveIsGroup,
1117
- isReply: !!replyContext,
1118
- replyToMessageId: replyContext ? messageId : void 0,
1119
- messageId: messageId ?? 0,
1120
- timestamp: timestamp ?? Date.now(),
1121
- text: userMessage,
1122
- block: false,
1123
- blockReason: "",
1124
- additionalContext: ""
1125
- };
1126
- await this.hookRunner.runModifyingHook("message:receive", msgEvent);
1127
- if (msgEvent.block) {
1128
- log3.info(`\u{1F6AB} Message blocked by hook: ${msgEvent.blockReason || "no reason"}`);
1129
- return { content: "", toolCalls: [] };
1130
- }
1131
- effectiveMessage = sanitizeForContext(msgEvent.text);
1132
- if (msgEvent.additionalContext) {
1133
- hookMessageContext = sanitizeForContext(msgEvent.additionalContext);
1134
- }
1135
- }
1136
- let session = getOrCreateSession(chatId);
1137
- const now = timestamp ?? Date.now();
1138
- const resetPolicy = this.config.agent.session_reset_policy;
1139
- if (shouldResetSession(session, resetPolicy)) {
1140
- log3.info(`\u{1F504} Auto-resetting session based on policy`);
1141
- if (this.hookRunner) {
1142
- await this.hookRunner.runObservingHook("session:end", {
1143
- sessionId: session.sessionId,
1144
- chatId,
1145
- messageCount: session.messageCount
1146
- });
1147
- }
1148
- if (transcriptExists(session.sessionId)) {
1149
- try {
1150
- log3.info(`\u{1F4BE} Saving memory before daily reset...`);
1151
- const oldContext = loadContextFromTranscript(session.sessionId);
1152
- await saveSessionMemory({
1153
- oldSessionId: session.sessionId,
1154
- newSessionId: "pending",
1155
- context: oldContext,
1156
- chatId,
1157
- apiKey: getEffectiveApiKey(this.config.agent.provider, this.config.agent.api_key),
1158
- provider: this.config.agent.provider,
1159
- utilityModel: this.config.agent.utility_model
1160
- });
1161
- log3.info(`\u2705 Memory saved before reset`);
1162
- } catch (error) {
1163
- log3.warn({ err: error }, `\u26A0\uFE0F Failed to save memory before reset`);
1164
- }
1165
- }
1166
- session = resetSessionWithPolicy(chatId, resetPolicy);
1167
- }
1168
- let context = loadContextFromTranscript(session.sessionId);
1169
- const isNewSession = context.messages.length === 0;
1170
- if (!isNewSession) {
1171
- log3.info(`\u{1F4D6} Loading existing session: ${session.sessionId}`);
1172
- } else {
1173
- log3.info(`\u{1F195} Starting new session: ${session.sessionId}`);
1174
- }
1175
- if (this.hookRunner) {
1176
- await this.hookRunner.runObservingHook("session:start", {
1177
- sessionId: session.sessionId,
1178
- chatId,
1179
- isResume: !isNewSession
1180
- });
1181
- }
1182
- const previousTimestamp = session.updatedAt;
1183
- let formattedMessage = formatMessageEnvelope({
1184
- channel: "Telegram",
1185
- senderId: toolContext?.senderId ? String(toolContext.senderId) : chatId,
1186
- senderName: userName,
1187
- senderUsername,
1188
- senderRank,
1189
- timestamp: now,
1190
- previousTimestamp,
1191
- body: effectiveMessage,
1192
- isGroup: effectiveIsGroup,
1193
- hasMedia,
1194
- mediaType,
1195
- messageId,
1196
- replyContext
1197
- });
1198
- if (pendingContext) {
1199
- formattedMessage = `${pendingContext}
1200
-
1201
- ${formattedMessage}`;
1202
- log3.debug(`\u{1F4CB} Including ${pendingContext.split("\n").length - 1} pending messages`);
1203
- }
1204
- log3.debug(`\u{1F4E8} Formatted message: ${formattedMessage.substring(0, 100)}...`);
1205
- const preview = formattedMessage.slice(0, 50).replace(/\n/g, " ");
1206
- const who = senderUsername ? `@${senderUsername}` : userName;
1207
- const msgType = isGroup ? `Group ${chatId} ${who}` : `DM ${who}`;
1208
- log3.info(`\u{1F4E8} ${msgType}: "${preview}${formattedMessage.length > 50 ? "..." : ""}"`);
1209
- let relevantContext = "";
1210
- let queryEmbedding;
1211
- const isNonTrivial = !isTrivialMessage(effectiveMessage);
1212
- if (this.embedder && isNonTrivial) {
1213
- try {
1214
- let searchQuery = effectiveMessage;
1215
- const recentUserMsgs = context.messages.filter((m) => m.role === "user" && typeof m.content === "string").slice(-3).map((m) => {
1216
- const text = m.content;
1217
- const bodyMatch = text.match(/\] (.+)/s);
1218
- return (bodyMatch ? bodyMatch[1] : text).trim();
1219
- }).filter((t) => t.length > 0);
1220
- if (recentUserMsgs.length > 0) {
1221
- searchQuery = recentUserMsgs.join(" ") + " " + effectiveMessage;
1222
- }
1223
- queryEmbedding = await this.embedder.embedQuery(
1224
- searchQuery.slice(0, EMBEDDING_QUERY_MAX_CHARS)
1225
- );
1226
- } catch (error) {
1227
- log3.warn({ err: error }, "Embedding computation failed");
1228
- }
1229
- }
1230
- if (this.contextBuilder && isNonTrivial) {
1231
- try {
1232
- const dbContext = await this.contextBuilder.buildContext({
1233
- query: effectiveMessage,
1234
- chatId,
1235
- includeAgentMemory: true,
1236
- includeFeedHistory: true,
1237
- searchAllChats: !isGroup,
1238
- maxRecentMessages: CONTEXT_MAX_RECENT_MESSAGES,
1239
- maxRelevantChunks: CONTEXT_MAX_RELEVANT_CHUNKS,
1240
- queryEmbedding
1241
- });
1242
- const contextParts = [];
1243
- if (dbContext.relevantKnowledge.length > 0) {
1244
- const sanitizedKnowledge = dbContext.relevantKnowledge.map(
1245
- (chunk) => sanitizeForContext(chunk)
1246
- );
1247
- contextParts.push(
1248
- `[Relevant knowledge from memory]
1249
- ${sanitizedKnowledge.join("\n---\n")}`
1250
- );
1251
- }
1252
- if (dbContext.relevantFeed.length > 0) {
1253
- const sanitizedFeed = dbContext.relevantFeed.map((msg) => sanitizeForContext(msg));
1254
- contextParts.push(
1255
- `[Relevant messages from Telegram feed]
1256
- ${sanitizedFeed.join("\n")}`
1257
- );
1258
- }
1259
- if (contextParts.length > 0) {
1260
- relevantContext = contextParts.join("\n\n");
1261
- log3.debug(
1262
- `\u{1F50D} Found ${dbContext.relevantKnowledge.length} knowledge chunks, ${dbContext.relevantFeed.length} feed messages`
1263
- );
1264
- }
1265
- } catch (error) {
1266
- log3.warn({ err: error }, "Context building failed");
1267
- }
1268
- }
1269
- const memoryStats = this.getMemoryStats();
1270
- const statsContext = `[Memory Status: ${memoryStats.totalMessages} messages across ${memoryStats.totalChats} chats, ${memoryStats.knowledgeChunks} knowledge chunks]`;
1271
- const additionalContext = relevantContext ? `You are in a Telegram conversation with chat ID: ${chatId}. Maintain conversation continuity.
1272
-
1273
- ${statsContext}
1274
-
1275
- ${relevantContext}` : `You are in a Telegram conversation with chat ID: ${chatId}. Maintain conversation continuity.
1276
-
1277
- ${statsContext}`;
1278
- let hookAdditionalContext = "";
1279
- if (this.hookRunner) {
1280
- const promptEvent = {
1281
- chatId,
1282
- sessionId: session.sessionId,
1283
- isGroup: effectiveIsGroup,
1284
- additionalContext: ""
1285
- };
1286
- await this.hookRunner.runModifyingHook("prompt:before", promptEvent);
1287
- hookAdditionalContext = sanitizeForContext(promptEvent.additionalContext);
1288
- }
1289
- const compactionConfig = this.compactionManager.getConfig();
1290
- const needsMemoryFlush = compactionConfig.enabled && compactionConfig.memoryFlushEnabled && context.messages.length > Math.floor((compactionConfig.maxMessages ?? 200) * 0.75);
1291
- const allHookContext = [userHookContext, hookAdditionalContext, hookMessageContext].filter(Boolean).join("\n\n");
1292
- const finalContext = additionalContext + (allHookContext ? `
1293
-
1294
- ${allHookContext}` : "");
1295
- const systemPrompt = buildSystemPrompt({
1296
- soul: this.soul,
1297
- userName,
1298
- senderUsername,
1299
- senderId: toolContext?.senderId,
1300
- ownerName: this.config.telegram.owner_name,
1301
- ownerUsername: this.config.telegram.owner_username,
1302
- context: finalContext,
1303
- includeMemory: !effectiveIsGroup,
1304
- includeStrategy: !effectiveIsGroup,
1305
- memoryFlushWarning: needsMemoryFlush
1306
- });
1307
- if (this.hookRunner) {
1308
- const promptAfterEvent = {
1309
- chatId,
1310
- sessionId: session.sessionId,
1311
- isGroup: effectiveIsGroup,
1312
- promptLength: systemPrompt.length,
1313
- sectionCount: (systemPrompt.match(/^#{1,3} /gm) || []).length,
1314
- ragContextLength: relevantContext.length,
1315
- hookContextLength: allHookContext.length
1316
- };
1317
- await this.hookRunner.runObservingHook("prompt:after", promptAfterEvent);
1318
- }
1319
- const userMsg = {
1320
- role: "user",
1321
- content: formattedMessage,
1322
- timestamp: now
1323
- };
1324
- context.messages.push(userMsg);
1325
- const preemptiveCompaction = await this.compactionManager.checkAndCompact(
1326
- session.sessionId,
1327
- context,
1328
- getEffectiveApiKey(this.config.agent.provider, this.config.agent.api_key),
1329
- chatId,
1330
- this.config.agent.provider,
1331
- this.config.agent.utility_model
1332
- );
1333
- if (preemptiveCompaction) {
1334
- log3.info(`\u{1F5DC}\uFE0F Preemptive compaction triggered, reloading session...`);
1335
- session = getSession(chatId);
1336
- context = loadContextFromTranscript(session.sessionId);
1337
- context.messages.push(userMsg);
1338
- }
1339
- appendToTranscript(session.sessionId, userMsg);
1340
- const provider = this.config.agent.provider || "anthropic";
1341
- const providerMeta = getProviderMetadata(provider);
1342
- const isAdmin = toolContext?.config?.telegram.admin_ids.includes(toolContext.senderId) ?? false;
1343
- let tools;
1344
- {
1345
- const toolIndex = this.toolRegistry?.getToolIndex();
1346
- const useRAG = toolIndex?.isIndexed && this.config.tool_rag?.enabled !== false && !isTrivialMessage(effectiveMessage) && !(providerMeta.toolLimit === null && this.config.tool_rag?.skip_unlimited_providers !== false);
1347
- if (useRAG && this.toolRegistry && queryEmbedding) {
1348
- tools = await this.toolRegistry.getForContextWithRAG(
1349
- effectiveMessage,
1350
- queryEmbedding,
1351
- effectiveIsGroup,
1352
- providerMeta.toolLimit,
1353
- chatId,
1354
- isAdmin
1355
- );
1356
- log3.info(`\u{1F50D} Tool RAG: ${tools.length}/${this.toolRegistry.count} tools selected`);
1357
- } else {
1358
- tools = this.toolRegistry?.getForContext(
1359
- effectiveIsGroup,
1360
- providerMeta.toolLimit,
1361
- chatId,
1362
- isAdmin
1363
- );
1364
- }
1365
- }
1366
- const maxIterations = this.config.agent.max_agentic_iterations || 5;
1367
- let iteration = 0;
1368
- let overflowResets = 0;
1369
- let rateLimitRetries = 0;
1370
- let serverErrorRetries = 0;
1371
- let finalResponse = null;
1372
- const totalToolCalls = [];
1373
- const accumulatedTexts = [];
1374
- const accumulatedUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalCost: 0 };
1375
- const seenToolSignatures = /* @__PURE__ */ new Set();
1376
- while (iteration < maxIterations) {
1377
- iteration++;
1378
- log3.debug(`\u{1F504} Agentic iteration ${iteration}/${maxIterations}`);
1379
- const iterationStartIndex = context.messages.length;
1380
- const maskedMessages = maskOldToolResults(context.messages, {
1381
- toolRegistry: this.toolRegistry ?? void 0,
1382
- currentIterationStartIndex: iterationStartIndex
1383
- });
1384
- const maskedContext = { ...context, messages: maskedMessages };
1385
- const response2 = await chatWithContext(this.config.agent, {
1386
- systemPrompt,
1387
- context: maskedContext,
1388
- sessionId: session.sessionId,
1389
- persistTranscript: true,
1390
- tools
1391
- });
1392
- const assistantMsg = response2.message;
1393
- if (assistantMsg.stopReason === "error") {
1394
- const errorMsg = assistantMsg.errorMessage || "";
1395
- if (this.hookRunner) {
1396
- 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";
1397
- const responseErrorEvent = {
1398
- chatId,
1399
- sessionId: session.sessionId,
1400
- isGroup: effectiveIsGroup,
1401
- error: errorMsg,
1402
- errorCode,
1403
- provider,
1404
- model: this.config.agent.model,
1405
- retryCount: rateLimitRetries + serverErrorRetries,
1406
- durationMs: Date.now() - processStartTime
1407
- };
1408
- await this.hookRunner.runObservingHook("response:error", responseErrorEvent);
1409
- }
1410
- if (isContextOverflowError(errorMsg)) {
1411
- overflowResets++;
1412
- if (overflowResets > 1) {
1413
- throw new Error(
1414
- "Context overflow persists after session reset. Message may be too large for the model's context window."
1415
- );
1416
- }
1417
- log3.error(`\u{1F6A8} Context overflow detected: ${errorMsg}`);
1418
- log3.info(`\u{1F4BE} Saving session memory before reset...`);
1419
- const summary = extractContextSummary(context, CONTEXT_OVERFLOW_SUMMARY_MESSAGES);
1420
- appendToDailyLog(summary);
1421
- log3.info(`\u2705 Memory saved to daily log`);
1422
- const archived = archiveTranscript(session.sessionId);
1423
- if (!archived) {
1424
- log3.error(
1425
- `\u26A0\uFE0F Failed to archive transcript ${session.sessionId}, proceeding with reset anyway`
1426
- );
1427
- }
1428
- log3.info(`\u{1F504} Resetting session due to context overflow...`);
1429
- session = resetSession(chatId);
1430
- context = { messages: [userMsg] };
1431
- appendToTranscript(session.sessionId, userMsg);
1432
- log3.info(`\u{1F504} Retrying with fresh context...`);
1433
- continue;
1434
- } else if (errorMsg.toLowerCase().includes("rate") || errorMsg.includes("429")) {
1435
- rateLimitRetries++;
1436
- if (rateLimitRetries <= RATE_LIMIT_MAX_RETRIES) {
1437
- const delay = 1e3 * Math.pow(2, rateLimitRetries - 1);
1438
- log3.warn(
1439
- `\u{1F6AB} Rate limited, retrying in ${delay}ms (attempt ${rateLimitRetries}/${RATE_LIMIT_MAX_RETRIES})...`
1440
- );
1441
- await new Promise((r3) => setTimeout(r3, delay));
1442
- iteration--;
1443
- continue;
1444
- }
1445
- log3.error(`\u{1F6AB} Rate limited after ${RATE_LIMIT_MAX_RETRIES} retries: ${errorMsg}`);
1446
- throw new Error(
1447
- `API rate limited after ${RATE_LIMIT_MAX_RETRIES} retries. Please try again later.`
1448
- );
1449
- } else if (errorMsg.includes("500") || errorMsg.includes("502") || errorMsg.includes("503") || errorMsg.includes("529")) {
1450
- serverErrorRetries++;
1451
- if (serverErrorRetries <= SERVER_ERROR_MAX_RETRIES) {
1452
- const delay = 2e3 * Math.pow(2, serverErrorRetries - 1);
1453
- log3.warn(
1454
- `\u{1F504} Server error, retrying in ${delay}ms (attempt ${serverErrorRetries}/${SERVER_ERROR_MAX_RETRIES})...`
1455
- );
1456
- await new Promise((r3) => setTimeout(r3, delay));
1457
- iteration--;
1458
- continue;
1459
- }
1460
- log3.error(`\u{1F6A8} Server error after ${SERVER_ERROR_MAX_RETRIES} retries: ${errorMsg}`);
1461
- throw new Error(
1462
- `API server error after ${SERVER_ERROR_MAX_RETRIES} retries. The provider may be experiencing issues.`
1463
- );
1464
- } else {
1465
- log3.error(`\u{1F6A8} API error: ${errorMsg}`);
1466
- throw new Error(`API error: ${errorMsg || "Unknown error"}`);
1467
- }
1468
- }
1469
- const iterUsage = response2.message.usage;
1470
- if (iterUsage) {
1471
- accumulatedUsage.input += iterUsage.input;
1472
- accumulatedUsage.output += iterUsage.output;
1473
- accumulatedUsage.cacheRead += iterUsage.cacheRead ?? 0;
1474
- accumulatedUsage.cacheWrite += iterUsage.cacheWrite ?? 0;
1475
- accumulatedUsage.totalCost += iterUsage.cost?.total ?? 0;
1476
- }
1477
- if (response2.text) {
1478
- accumulatedTexts.push(response2.text);
1479
- }
1480
- const toolCalls = response2.message.content.filter((block) => block.type === "toolCall");
1481
- if (toolCalls.length === 0) {
1482
- log3.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 done`);
1483
- finalResponse = response2;
1484
- break;
1485
- }
1486
- if (!this.toolRegistry || !toolContext) {
1487
- log3.error("\u26A0\uFE0F Cannot execute tools: registry or context missing");
1488
- break;
1489
- }
1490
- log3.debug(`\u{1F527} Executing ${toolCalls.length} tool call(s)`);
1491
- context.messages.push(response2.message);
1492
- const iterationToolNames = [];
1493
- const fullContext = {
1494
- ...toolContext,
1495
- chatId,
1496
- isGroup: effectiveIsGroup
1497
- };
1498
- const toolPlans = [];
1499
- for (const block of toolCalls) {
1500
- if (block.type !== "toolCall") continue;
1501
- let toolParams = block.arguments ?? {};
1502
- let blocked = false;
1503
- let blockReason = "";
1504
- if (this.hookRunner) {
1505
- const beforeEvent = {
1506
- toolName: block.name,
1507
- params: structuredClone(toolParams),
1508
- chatId,
1509
- isGroup: effectiveIsGroup,
1510
- block: false,
1511
- blockReason: ""
1512
- };
1513
- await this.hookRunner.runModifyingHook("tool:before", beforeEvent);
1514
- if (beforeEvent.block) {
1515
- blocked = true;
1516
- blockReason = beforeEvent.blockReason || "Blocked by plugin hook";
1517
- } else {
1518
- toolParams = structuredClone(beforeEvent.params);
1519
- }
1520
- }
1521
- toolPlans.push({ block, blocked, blockReason, params: toolParams });
1522
- }
1523
- const execResults = new Array(toolPlans.length);
1524
- {
1525
- let cursor = 0;
1526
- const runWorker = async () => {
1527
- while (cursor < toolPlans.length) {
1528
- const idx = cursor++;
1529
- const plan = toolPlans[idx];
1530
- if (plan.blocked) {
1531
- execResults[idx] = {
1532
- result: { success: false, error: plan.blockReason },
1533
- durationMs: 0
1534
- };
1535
- continue;
1536
- }
1537
- const startTime = Date.now();
1538
- try {
1539
- const result = await this.toolRegistry.execute(
1540
- { ...plan.block, arguments: plan.params },
1541
- fullContext
1542
- );
1543
- execResults[idx] = { result, durationMs: Date.now() - startTime };
1544
- } catch (execErr) {
1545
- const errMsg = execErr instanceof Error ? execErr.message : String(execErr);
1546
- const errStack = execErr instanceof Error ? execErr.stack : void 0;
1547
- execResults[idx] = {
1548
- result: { success: false, error: errMsg },
1549
- durationMs: Date.now() - startTime,
1550
- execError: { message: errMsg, stack: errStack }
1551
- };
1552
- }
1553
- }
1554
- };
1555
- const workers = Math.min(TOOL_CONCURRENCY_LIMIT, toolPlans.length);
1556
- await Promise.all(Array.from({ length: workers }, () => runWorker()));
1557
- }
1558
- for (let i = 0; i < toolPlans.length; i++) {
1559
- const plan = toolPlans[i];
1560
- const { block } = plan;
1561
- const exec = execResults[i];
1562
- if (exec.execError && this.hookRunner) {
1563
- const errorEvent = {
1564
- toolName: block.name,
1565
- params: structuredClone(plan.params),
1566
- error: exec.execError.message,
1567
- stack: exec.execError.stack,
1568
- chatId,
1569
- isGroup: effectiveIsGroup,
1570
- durationMs: exec.durationMs
1571
- };
1572
- await this.hookRunner.runObservingHook("tool:error", errorEvent);
1573
- }
1574
- if (this.hookRunner) {
1575
- const afterEvent = {
1576
- toolName: block.name,
1577
- params: structuredClone(plan.params),
1578
- result: {
1579
- success: exec.result.success,
1580
- data: exec.result.data,
1581
- error: exec.result.error
1582
- },
1583
- durationMs: exec.durationMs,
1584
- chatId,
1585
- isGroup: effectiveIsGroup,
1586
- ...plan.blocked ? { blocked: true, blockReason: plan.blockReason } : {}
1587
- };
1588
- await this.hookRunner.runObservingHook("tool:after", afterEvent);
1589
- }
1590
- log3.debug(`${block.name}: ${exec.result.success ? "\u2713" : "\u2717"} ${exec.result.error || ""}`);
1591
- iterationToolNames.push(`${block.name} ${exec.result.success ? "\u2713" : "\u2717"}`);
1592
- totalToolCalls.push({
1593
- name: block.name,
1594
- input: block.arguments
1595
- });
1596
- let resultText = JSON.stringify(exec.result);
1597
- if (resultText.length > MAX_TOOL_RESULT_SIZE) {
1598
- log3.warn(`\u26A0\uFE0F Tool result too large (${resultText.length} chars), truncating...`);
1599
- const data = exec.result.data;
1600
- if (data?.summary || data?.message) {
1601
- resultText = JSON.stringify({
1602
- success: exec.result.success,
1603
- data: {
1604
- summary: data.summary || data.message,
1605
- _truncated: true,
1606
- _originalSize: resultText.length,
1607
- _message: "Full data truncated. Use limit parameter for smaller results."
1608
- }
1609
- });
1610
- } else {
1611
- const summarized = {
1612
- _truncated: true,
1613
- _originalSize: resultText.length,
1614
- _message: "Full data truncated. Use limit parameter for smaller results."
1615
- };
1616
- if (data && typeof data === "object") {
1617
- for (const [key, value] of Object.entries(data)) {
1618
- if (Array.isArray(value)) {
1619
- summarized[key] = `[${value.length} items]`;
1620
- } else if (typeof value === "string" && value.length > 500) {
1621
- summarized[key] = value.slice(0, 500) + "...[truncated]";
1622
- } else {
1623
- summarized[key] = value;
1624
- }
1625
- }
1626
- }
1627
- resultText = JSON.stringify({ success: exec.result.success, data: summarized });
1628
- }
1629
- }
1630
- if (provider === "cocoon") {
1631
- const { wrapToolResult } = await import("./tool-adapter-IVX2XQJE.js");
1632
- const cocoonResultMsg = {
1633
- role: "user",
1634
- content: [
1635
- {
1636
- type: "text",
1637
- text: wrapToolResult(resultText)
1638
- }
1639
- ],
1640
- timestamp: Date.now()
1641
- };
1642
- context.messages.push(cocoonResultMsg);
1643
- appendToTranscript(session.sessionId, cocoonResultMsg);
1644
- } else {
1645
- const toolResultMsg = {
1646
- role: "toolResult",
1647
- toolCallId: block.id,
1648
- toolName: block.name,
1649
- content: [
1650
- {
1651
- type: "text",
1652
- text: resultText
1653
- }
1654
- ],
1655
- isError: !exec.result.success,
1656
- timestamp: Date.now()
1657
- };
1658
- context.messages.push(toolResultMsg);
1659
- appendToTranscript(session.sessionId, toolResultMsg);
1660
- }
1661
- }
1662
- log3.info(`\u{1F504} ${iteration}/${maxIterations} \u2192 ${iterationToolNames.join(", ")}`);
1663
- const iterSignatures = toolPlans.map(
1664
- (p2) => `${p2.block.name}:${JSON.stringify(p2.params, Object.keys(p2.params).sort())}`
1665
- );
1666
- const allDuplicates = iterSignatures.length > 0 && iterSignatures.every((sig) => seenToolSignatures.has(sig));
1667
- for (const sig of iterSignatures) seenToolSignatures.add(sig);
1668
- if (allDuplicates) {
1669
- log3.warn(
1670
- `\u{1F501} Loop stall detected: all ${iterSignatures.length} tool call(s) are repeats \u2014 breaking early`
1671
- );
1672
- finalResponse = response2;
1673
- break;
1674
- }
1675
- if (iteration === maxIterations) {
1676
- log3.info(`\u26A0\uFE0F Max iterations reached (${maxIterations})`);
1677
- finalResponse = response2;
1678
- }
1679
- }
1680
- if (!finalResponse) {
1681
- log3.error("\u26A0\uFE0F Agentic loop exited early without final response");
1682
- return {
1683
- content: "Internal error: Agent loop failed to produce a response.",
1684
- toolCalls: []
1685
- };
1686
- }
1687
- const response = finalResponse;
1688
- const lastMsg = context.messages[context.messages.length - 1];
1689
- if (lastMsg?.role !== "assistant") {
1690
- context.messages.push(response.message);
1691
- }
1692
- const sessionUpdate = {
1693
- updatedAt: Date.now(),
1694
- messageCount: session.messageCount + 1,
1695
- model: this.config.agent.model,
1696
- provider: this.config.agent.provider,
1697
- inputTokens: (session.inputTokens ?? 0) + accumulatedUsage.input + accumulatedUsage.cacheRead + accumulatedUsage.cacheWrite,
1698
- outputTokens: (session.outputTokens ?? 0) + accumulatedUsage.output
1699
- };
1700
- updateSession(chatId, sessionUpdate);
1701
- if (accumulatedUsage.input > 0 || accumulatedUsage.output > 0) {
1702
- const u = accumulatedUsage;
1703
- const totalInput = u.input + u.cacheRead + u.cacheWrite;
1704
- const inK = (totalInput / 1e3).toFixed(1);
1705
- const cacheParts = [];
1706
- if (u.cacheRead) cacheParts.push(`${(u.cacheRead / 1e3).toFixed(1)}K cached`);
1707
- if (u.cacheWrite) cacheParts.push(`${(u.cacheWrite / 1e3).toFixed(1)}K new`);
1708
- const cacheInfo = cacheParts.length > 0 ? ` (${cacheParts.join(", ")})` : "";
1709
- log3.info(`\u{1F4B0} ${inK}K in${cacheInfo}, ${u.output} out | $${u.totalCost.toFixed(3)}`);
1710
- globalTokenUsage.totalTokens += u.input + u.output + u.cacheRead + u.cacheWrite;
1711
- globalTokenUsage.totalCost += u.totalCost;
1712
- }
1713
- let content = accumulatedTexts.join("\n").trim() || response.text;
1714
- const usedTelegramSendTool = totalToolCalls.some((tc) => TELEGRAM_SEND_TOOLS.has(tc.name));
1715
- if (!content && totalToolCalls.length > 0 && !usedTelegramSendTool) {
1716
- log3.warn("\u26A0\uFE0F Empty response after tool calls - generating fallback");
1717
- content = "I executed the requested action but couldn't generate a response. Please try again.";
1718
- } else if (!content && usedTelegramSendTool) {
1719
- log3.info("\u2705 Response sent via Telegram tool - no additional text needed");
1720
- content = "";
1721
- } else if (!content && accumulatedUsage.input === 0 && accumulatedUsage.output === 0) {
1722
- log3.warn("\u26A0\uFE0F Empty response with zero tokens - possible API issue");
1723
- content = "I couldn't process your request. Please try again.";
1724
- }
1725
- let responseMetadata = {};
1726
- if (this.hookRunner) {
1727
- const responseBeforeEvent = {
1728
- chatId,
1729
- sessionId: session.sessionId,
1730
- isGroup: effectiveIsGroup,
1731
- originalText: content,
1732
- text: content,
1733
- block: false,
1734
- blockReason: "",
1735
- metadata: {}
1736
- };
1737
- await this.hookRunner.runModifyingHook("response:before", responseBeforeEvent);
1738
- if (responseBeforeEvent.block) {
1739
- log3.info(
1740
- `\u{1F6AB} Response blocked by hook: ${responseBeforeEvent.blockReason || "no reason"}`
1741
- );
1742
- content = "";
1743
- } else {
1744
- content = responseBeforeEvent.text;
1745
- }
1746
- responseMetadata = responseBeforeEvent.metadata;
1747
- }
1748
- if (this.hookRunner) {
1749
- const responseAfterEvent = {
1750
- chatId,
1751
- sessionId: session.sessionId,
1752
- isGroup: effectiveIsGroup,
1753
- text: content,
1754
- durationMs: Date.now() - processStartTime,
1755
- toolsUsed: totalToolCalls.map((tc) => tc.name),
1756
- tokenUsage: accumulatedUsage.input > 0 || accumulatedUsage.output > 0 ? { input: accumulatedUsage.input, output: accumulatedUsage.output } : void 0,
1757
- metadata: responseMetadata
1758
- };
1759
- await this.hookRunner.runObservingHook("response:after", responseAfterEvent);
1760
- }
1761
- return {
1762
- content,
1763
- toolCalls: totalToolCalls
1764
- };
1765
- } catch (error) {
1766
- log3.error({ err: error }, "Agent error");
1767
- throw error;
1768
- }
1769
- }
1770
- clearHistory(chatId) {
1771
- const db = getDatabase().getDb();
1772
- db.prepare(
1773
- `DELETE FROM tg_messages_vec WHERE id IN (
1774
- SELECT id FROM tg_messages WHERE chat_id = ?
1775
- )`
1776
- ).run(chatId);
1777
- db.prepare(`DELETE FROM tg_messages WHERE chat_id = ?`).run(chatId);
1778
- resetSession(chatId);
1779
- log3.info(`\u{1F5D1}\uFE0F Cleared history for chat ${chatId}`);
1780
- }
1781
- getConfig() {
1782
- return this.config;
1783
- }
1784
- getActiveChatIds() {
1785
- const db = getDatabase().getDb();
1786
- const rows = db.prepare(
1787
- `
1788
- SELECT DISTINCT chat_id
1789
- FROM tg_messages
1790
- ORDER BY timestamp DESC
1791
- `
1792
- ).all();
1793
- return rows.map((r3) => r3.chat_id);
1794
- }
1795
- setSoul(soul) {
1796
- this.soul = soul;
1797
- }
1798
- configureCompaction(config) {
1799
- this.compactionManager.updateConfig(config);
1800
- log3.info({ config: this.compactionManager.getConfig() }, `\u{1F5DC}\uFE0F Compaction config updated`);
1801
- }
1802
- getCompactionConfig() {
1803
- return this.compactionManager.getConfig();
1804
- }
1805
- _memoryStatsCache = null;
1806
- getMemoryStats() {
1807
- const now = Date.now();
1808
- if (this._memoryStatsCache && now < this._memoryStatsCache.expiry) {
1809
- return this._memoryStatsCache.data;
1810
- }
1811
- const db = getDatabase().getDb();
1812
- const msgCount = db.prepare(`SELECT COUNT(*) as count FROM tg_messages`).get();
1813
- const chatCount = db.prepare(`SELECT COUNT(DISTINCT chat_id) as count FROM tg_messages`).get();
1814
- const knowledgeCount = db.prepare(`SELECT COUNT(*) as count FROM knowledge`).get();
1815
- const data = {
1816
- totalMessages: msgCount.count,
1817
- totalChats: chatCount.count,
1818
- knowledgeChunks: knowledgeCount.count
1819
- };
1820
- this._memoryStatsCache = { data, expiry: now + 5 * 60 * 1e3 };
1821
- return data;
1822
- }
1823
- };
1824
539
 
1825
540
  // src/bot/services/styled-keyboard.ts
1826
541
  import { Api } from "telegram";
@@ -2007,7 +722,7 @@ function unescapeHtml(text) {
2007
722
  }
2008
723
 
2009
724
  // src/bot/inline-router.ts
2010
- var log4 = createLogger("InlineRouter");
725
+ var log2 = createLogger("InlineRouter");
2011
726
  var INLINE_TIMEOUT_MS = 5e3;
2012
727
  var CALLBACK_TIMEOUT_MS = 15e3;
2013
728
  function compileGlob(pattern) {
@@ -2028,11 +743,11 @@ var InlineRouter = class {
2028
743
  }
2029
744
  registerPlugin(name, handlers) {
2030
745
  this.plugins.set(name, handlers);
2031
- log4.info(`Registered plugin "${name}" for inline routing`);
746
+ log2.info(`Registered plugin "${name}" for inline routing`);
2032
747
  }
2033
748
  unregisterPlugin(name) {
2034
749
  this.plugins.delete(name);
2035
- log4.info(`Unregistered plugin "${name}" from inline routing`);
750
+ log2.info(`Unregistered plugin "${name}" from inline routing`);
2036
751
  }
2037
752
  hasPlugin(name) {
2038
753
  return this.plugins.has(name);
@@ -2104,7 +819,7 @@ var InlineRouter = class {
2104
819
  is_personal: true
2105
820
  });
2106
821
  } catch (error) {
2107
- log4.error({ err: error }, `Plugin "${pluginName}" inline query handler failed`);
822
+ log2.error({ err: error }, `Plugin "${pluginName}" inline query handler failed`);
2108
823
  try {
2109
824
  await ctx.answerInlineQuery([], { cache_time: 0, is_personal: true });
2110
825
  } catch {
@@ -2164,7 +879,7 @@ var InlineRouter = class {
2164
879
  } catch (error) {
2165
880
  const errMsg = error?.errorMessage;
2166
881
  if (errMsg === "MESSAGE_NOT_MODIFIED") return;
2167
- log4.debug(`GramJS edit failed, falling back to Grammy: ${errMsg || error}`);
882
+ log2.debug(`GramJS edit failed, falling back to Grammy: ${errMsg || error}`);
2168
883
  }
2169
884
  }
2170
885
  const replyMarkup = styledButtons ? toGrammyKeyboard(styledButtons) : void 0;
@@ -2184,7 +899,7 @@ var InlineRouter = class {
2184
899
  await ctx.answerCallbackQuery();
2185
900
  }
2186
901
  } catch (error) {
2187
- log4.error({ err: error }, `Plugin "${pluginName}" callback handler failed`);
902
+ log2.error({ err: error }, `Plugin "${pluginName}" callback handler failed`);
2188
903
  if (!answered) {
2189
904
  try {
2190
905
  await ctx.answerCallbackQuery({ text: "Error processing action" });
@@ -2207,7 +922,7 @@ var InlineRouter = class {
2207
922
  };
2208
923
  await plugin.onChosenResult(crCtx);
2209
924
  } catch (error) {
2210
- log4.error({ err: error }, `Plugin "${pluginName}" chosen result handler failed`);
925
+ log2.error({ err: error }, `Plugin "${pluginName}" chosen result handler failed`);
2211
926
  }
2212
927
  }
2213
928
  /**
@@ -2317,7 +1032,7 @@ import { promisify } from "util";
2317
1032
 
2318
1033
  // src/agent/tools/plugin-validator.ts
2319
1034
  import { z } from "zod";
2320
- var log5 = createLogger("PluginValidator");
1035
+ var log3 = createLogger("PluginValidator");
2321
1036
  var ManifestSchema = z.object({
2322
1037
  name: z.string().min(1).max(64).regex(
2323
1038
  /^[a-z0-9][a-z0-9-]*$/,
@@ -2360,20 +1075,20 @@ function validateToolDefs(defs, pluginName) {
2360
1075
  const valid = [];
2361
1076
  for (const def of defs) {
2362
1077
  if (!def || typeof def !== "object") {
2363
- log5.warn(`[${pluginName}] tool is not an object, skipping`);
1078
+ log3.warn(`[${pluginName}] tool is not an object, skipping`);
2364
1079
  continue;
2365
1080
  }
2366
1081
  const t = def;
2367
1082
  if (!t.name || typeof t.name !== "string") {
2368
- log5.warn(`[${pluginName}] tool missing 'name', skipping`);
1083
+ log3.warn(`[${pluginName}] tool missing 'name', skipping`);
2369
1084
  continue;
2370
1085
  }
2371
1086
  if (!t.description || typeof t.description !== "string") {
2372
- log5.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
1087
+ log3.warn(`[${pluginName}] tool "${t.name}" missing 'description', skipping`);
2373
1088
  continue;
2374
1089
  }
2375
1090
  if (!t.execute || typeof t.execute !== "function") {
2376
- log5.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
1091
+ log3.warn(`[${pluginName}] tool "${t.name}" missing 'execute' function, skipping`);
2377
1092
  continue;
2378
1093
  }
2379
1094
  valid.push(t);
@@ -2433,25 +1148,25 @@ function withTxLock(fn) {
2433
1148
  }
2434
1149
 
2435
1150
  // src/ton/transfer.ts
2436
- var log6 = createLogger("TON");
1151
+ var log4 = createLogger("TON");
2437
1152
  async function sendTon(params) {
2438
1153
  return withTxLock(async () => {
2439
1154
  try {
2440
1155
  const { toAddress: toAddress2, amount, comment = "", bounce = false } = params;
2441
1156
  if (!Number.isFinite(amount) || amount <= 0) {
2442
- log6.error({ amount }, "Invalid transfer amount");
1157
+ log4.error({ amount }, "Invalid transfer amount");
2443
1158
  return null;
2444
1159
  }
2445
1160
  let recipientAddress;
2446
1161
  try {
2447
1162
  recipientAddress = Address.parse(toAddress2);
2448
1163
  } catch (e) {
2449
- log6.error({ err: e }, `Invalid recipient address: ${toAddress2}`);
1164
+ log4.error({ err: e }, `Invalid recipient address: ${toAddress2}`);
2450
1165
  return null;
2451
1166
  }
2452
1167
  const keyPair = await getKeyPair();
2453
1168
  if (!keyPair) {
2454
- log6.error("Wallet not initialized");
1169
+ log4.error("Wallet not initialized");
2455
1170
  return null;
2456
1171
  }
2457
1172
  const wallet = WalletContractV5R1.create({
@@ -2475,7 +1190,7 @@ async function sendTon(params) {
2475
1190
  ]
2476
1191
  });
2477
1192
  const pseudoHash = `${seqno}_${Date.now()}_${amount.toFixed(2)}`;
2478
- 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}`);
2479
1194
  return pseudoHash;
2480
1195
  } catch (error) {
2481
1196
  const err = error;
@@ -2483,14 +1198,14 @@ async function sendTon(params) {
2483
1198
  if (status === 429 || status !== void 0 && status >= 500) {
2484
1199
  invalidateTonClientCache();
2485
1200
  }
2486
- log6.error({ err: error }, "Error sending TON");
1201
+ log4.error({ err: error }, "Error sending TON");
2487
1202
  throw error;
2488
1203
  }
2489
1204
  });
2490
1205
  }
2491
1206
 
2492
1207
  // src/utils/retry.ts
2493
- var log7 = createLogger("Utils");
1208
+ var log5 = createLogger("Utils");
2494
1209
  var DEFAULT_OPTIONS = {
2495
1210
  maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
2496
1211
  baseDelayMs: RETRY_DEFAULT_BASE_DELAY_MS,
@@ -2514,7 +1229,7 @@ async function withRetry(fn, options = {}) {
2514
1229
  return result;
2515
1230
  } catch (error) {
2516
1231
  lastError = error instanceof Error ? error : new Error(String(error));
2517
- log7.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
1232
+ log5.warn(`Retry attempt ${attempt}/${opts.maxAttempts} failed: ${lastError.message}`);
2518
1233
  if (attempt < opts.maxAttempts) {
2519
1234
  const delay = Math.min(opts.baseDelayMs * Math.pow(2, attempt - 1), opts.maxDelayMs);
2520
1235
  await sleep(delay);
@@ -5992,7 +4707,7 @@ var DEDUST_GAS = {
5992
4707
  var NATIVE_TON_ADDRESS = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
5993
4708
 
5994
4709
  // src/agent/tools/dedust/asset-cache.ts
5995
- var log8 = createLogger("Tools");
4710
+ var log6 = createLogger("Tools");
5996
4711
  var ASSET_LIST_URL = "https://assets.dedust.io/list.json";
5997
4712
  var CACHE_TTL_MS = 10 * 60 * 1e3;
5998
4713
  var cachedAssets = [];
@@ -6011,7 +4726,7 @@ async function getAssetList() {
6011
4726
  return cachedAssets;
6012
4727
  } catch (error) {
6013
4728
  if (cachedAssets.length > 0) {
6014
- log8.warn({ err: error }, "Asset list fetch failed, using stale cache");
4729
+ log6.warn({ err: error }, "Asset list fetch failed, using stale cache");
6015
4730
  return cachedAssets;
6016
4731
  }
6017
4732
  throw error;
@@ -6064,7 +4779,7 @@ var stonApiClient = new StonApiClient();
6064
4779
  function isTon(asset) {
6065
4780
  return asset.toLowerCase() === "ton";
6066
4781
  }
6067
- async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log12) {
4782
+ async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log10) {
6068
4783
  try {
6069
4784
  const isTonInput = isTon(fromAsset);
6070
4785
  const isTonOutput = isTon(toAsset);
@@ -6095,11 +4810,11 @@ async function getStonfiQuote(fromAsset, toAsset, amount, slippage, log12) {
6095
4810
  fee: feeAmount.toFixed(6)
6096
4811
  };
6097
4812
  } catch (err) {
6098
- log12.debug("dex.quoteSTONfi() failed:", err);
4813
+ log10.debug("dex.quoteSTONfi() failed:", err);
6099
4814
  return null;
6100
4815
  }
6101
4816
  }
6102
- async function getDedustQuote(fromAsset, toAsset, amount, slippage, log12) {
4817
+ async function getDedustQuote(fromAsset, toAsset, amount, slippage, log10) {
6103
4818
  try {
6104
4819
  const isTonInput = isTon(fromAsset);
6105
4820
  const isTonOutput = isTon(toAsset);
@@ -6133,7 +4848,7 @@ async function getDedustQuote(fromAsset, toAsset, amount, slippage, log12) {
6133
4848
  poolType
6134
4849
  };
6135
4850
  } catch (err) {
6136
- log12.debug("dex.quoteDeDust() failed:", err);
4851
+ log10.debug("dex.quoteDeDust() failed:", err);
6137
4852
  return null;
6138
4853
  }
6139
4854
  }
@@ -6305,14 +5020,14 @@ function validateDexParams(amount, slippage) {
6305
5020
  throw new PluginSDKError("Slippage must be between 0 and 1", "OPERATION_FAILED");
6306
5021
  }
6307
5022
  }
6308
- function createDexSDK(log12) {
5023
+ function createDexSDK(log10) {
6309
5024
  return {
6310
5025
  async quote(params) {
6311
5026
  validateDexParams(params.amount, params.slippage);
6312
5027
  const slippage = params.slippage ?? 0.01;
6313
5028
  const [stonfi, dedust] = await Promise.all([
6314
- getStonfiQuote(params.fromAsset, params.toAsset, params.amount, slippage, log12),
6315
- 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)
6316
5031
  ]);
6317
5032
  if (!stonfi && !dedust) {
6318
5033
  throw new PluginSDKError("No DEX has liquidity for this pair", "OPERATION_FAILED");
@@ -6326,7 +5041,14 @@ function createDexSDK(log12) {
6326
5041
  } else {
6327
5042
  const stonfiOut = parseFloat(stonfi.expectedOutput);
6328
5043
  const dedustOut = parseFloat(dedust.expectedOutput);
6329
- 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) {
6330
5052
  recommended = "stonfi";
6331
5053
  if (dedustOut > 0) {
6332
5054
  savings = `${((stonfiOut - dedustOut) / dedustOut * 100).toFixed(2)}%`;
@@ -6346,7 +5068,7 @@ function createDexSDK(log12) {
6346
5068
  params.toAsset,
6347
5069
  params.amount,
6348
5070
  params.slippage ?? 0.01,
6349
- log12
5071
+ log10
6350
5072
  );
6351
5073
  },
6352
5074
  async quoteDeDust(params) {
@@ -6355,25 +5077,25 @@ function createDexSDK(log12) {
6355
5077
  params.toAsset,
6356
5078
  params.amount,
6357
5079
  params.slippage ?? 0.01,
6358
- log12
5080
+ log10
6359
5081
  );
6360
5082
  },
6361
5083
  async swap(params) {
6362
5084
  validateDexParams(params.amount, params.slippage);
6363
5085
  if (params.dex === "stonfi") {
6364
- return executeSTONfiSwap(params, log12);
5086
+ return executeSTONfiSwap(params, log10);
6365
5087
  }
6366
5088
  if (params.dex === "dedust") {
6367
- return executeDedustSwap(params, log12);
5089
+ return executeDedustSwap(params, log10);
6368
5090
  }
6369
5091
  const quoteResult = await this.quote(params);
6370
- return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log12) : executeDedustSwap(params, log12);
5092
+ return quoteResult.recommended === "stonfi" ? executeSTONfiSwap(params, log10) : executeDedustSwap(params, log10);
6371
5093
  },
6372
5094
  async swapSTONfi(params) {
6373
- return executeSTONfiSwap(params, log12);
5095
+ return executeSTONfiSwap(params, log10);
6374
5096
  },
6375
5097
  async swapDeDust(params) {
6376
- return executeDedustSwap(params, log12);
5098
+ return executeDedustSwap(params, log10);
6377
5099
  }
6378
5100
  };
6379
5101
  }
@@ -6410,7 +5132,7 @@ function normalizeDomain(domain) {
6410
5132
  if (!d.endsWith(".ton")) d += ".ton";
6411
5133
  return d;
6412
5134
  }
6413
- function createDnsSDK(log12) {
5135
+ function createDnsSDK(log10) {
6414
5136
  return {
6415
5137
  async check(domain) {
6416
5138
  const normalized = normalizeDomain(domain);
@@ -6433,7 +5155,7 @@ function createDnsSDK(log12) {
6433
5155
  };
6434
5156
  } catch (err) {
6435
5157
  if (err instanceof PluginSDKError) throw err;
6436
- log12.debug("dns.check() failed:", err);
5158
+ log10.debug("dns.check() failed:", err);
6437
5159
  throw new PluginSDKError(
6438
5160
  `Failed to check domain: ${err instanceof Error ? err.message : String(err)}`,
6439
5161
  "OPERATION_FAILED"
@@ -6446,7 +5168,7 @@ function createDnsSDK(log12) {
6446
5168
  const response = await tonapiFetch(`/dns/${encodeURIComponent(normalized)}`);
6447
5169
  if (response.status === 404) return null;
6448
5170
  if (!response.ok) {
6449
- log12.debug(`dns.resolve() TonAPI error: ${response.status}`);
5171
+ log10.debug(`dns.resolve() TonAPI error: ${response.status}`);
6450
5172
  return null;
6451
5173
  }
6452
5174
  const data = await response.json();
@@ -6458,7 +5180,7 @@ function createDnsSDK(log12) {
6458
5180
  expirationDate: data.expiring_at || void 0
6459
5181
  };
6460
5182
  } catch (err) {
6461
- log12.debug("dns.resolve() failed:", err);
5183
+ log10.debug("dns.resolve() failed:", err);
6462
5184
  return null;
6463
5185
  }
6464
5186
  },
@@ -6468,7 +5190,7 @@ function createDnsSDK(log12) {
6468
5190
  `/dns/auctions?tld=ton&limit=${Math.min(limit ?? 20, 100)}`
6469
5191
  );
6470
5192
  if (!response.ok) {
6471
- log12.debug(`dns.getAuctions() TonAPI error: ${response.status}`);
5193
+ log10.debug(`dns.getAuctions() TonAPI error: ${response.status}`);
6472
5194
  return [];
6473
5195
  }
6474
5196
  const data = await response.json();
@@ -6481,7 +5203,7 @@ function createDnsSDK(log12) {
6481
5203
  bids: a.bids || 0
6482
5204
  }));
6483
5205
  } catch (err) {
6484
- log12.debug("dns.getAuctions() failed:", err);
5206
+ log10.debug("dns.getAuctions() failed:", err);
6485
5207
  return [];
6486
5208
  }
6487
5209
  },
@@ -6795,25 +5517,25 @@ function findJettonBalance(balances, jettonAddress) {
6795
5517
  }
6796
5518
  });
6797
5519
  }
6798
- function cleanupOldTransactions(db, retentionDays, log12) {
5520
+ function cleanupOldTransactions(db, retentionDays, log10) {
6799
5521
  if (Math.random() > CLEANUP_PROBABILITY) return;
6800
5522
  try {
6801
5523
  const cutoff = Math.floor(Date.now() / 1e3) - retentionDays * 24 * 60 * 60;
6802
5524
  const result = db.prepare("DELETE FROM used_transactions WHERE used_at < ?").run(cutoff);
6803
5525
  if (result.changes > 0) {
6804
- log12.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
5526
+ log10.debug(`Cleaned up ${result.changes} old transaction records (>${retentionDays}d)`);
6805
5527
  }
6806
5528
  } catch (err) {
6807
- log12.error("Transaction cleanup failed:", err);
5529
+ log10.error("Transaction cleanup failed:", err);
6808
5530
  }
6809
5531
  }
6810
- function createTonSDK(log12, db) {
5532
+ function createTonSDK(log10, db) {
6811
5533
  return {
6812
5534
  getAddress() {
6813
5535
  try {
6814
5536
  return getWalletAddress();
6815
5537
  } catch (err) {
6816
- log12.error("ton.getAddress() failed:", err);
5538
+ log10.error("ton.getAddress() failed:", err);
6817
5539
  return null;
6818
5540
  }
6819
5541
  },
@@ -6823,7 +5545,7 @@ function createTonSDK(log12, db) {
6823
5545
  if (!addr) return null;
6824
5546
  return await getWalletBalance(addr);
6825
5547
  } catch (err) {
6826
- log12.error("ton.getBalance() failed:", err);
5548
+ log10.error("ton.getBalance() failed:", err);
6827
5549
  return null;
6828
5550
  }
6829
5551
  },
@@ -6831,7 +5553,7 @@ function createTonSDK(log12, db) {
6831
5553
  try {
6832
5554
  return await getTonPrice();
6833
5555
  } catch (err) {
6834
- log12.error("ton.getPrice() failed:", err);
5556
+ log10.error("ton.getPrice() failed:", err);
6835
5557
  return null;
6836
5558
  }
6837
5559
  },
@@ -6882,7 +5604,7 @@ function createTonSDK(log12, db) {
6882
5604
  );
6883
5605
  return formatTransactions(transactions);
6884
5606
  } catch (err) {
6885
- log12.error("ton.getTransactions() failed:", err);
5607
+ log10.error("ton.getTransactions() failed:", err);
6886
5608
  return [];
6887
5609
  }
6888
5610
  },
@@ -6905,7 +5627,7 @@ function createTonSDK(log12, db) {
6905
5627
  throw new PluginSDKError("Wallet not initialized", "WALLET_NOT_INITIALIZED");
6906
5628
  }
6907
5629
  const maxAgeMinutes = params.maxAgeMinutes ?? DEFAULT_MAX_AGE_MINUTES;
6908
- cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log12);
5630
+ cleanupOldTransactions(db, DEFAULT_TX_RETENTION_DAYS, log10);
6909
5631
  try {
6910
5632
  const txs = await this.getTransactions(address4, 20);
6911
5633
  for (const tx of txs) {
@@ -6939,7 +5661,7 @@ function createTonSDK(log12, db) {
6939
5661
  };
6940
5662
  } catch (err) {
6941
5663
  if (err instanceof PluginSDKError) throw err;
6942
- log12.error("ton.verifyPayment() failed:", err);
5664
+ log10.error("ton.verifyPayment() failed:", err);
6943
5665
  return {
6944
5666
  verified: false,
6945
5667
  error: `Verification failed: ${err instanceof Error ? err.message : String(err)}`
@@ -6953,7 +5675,7 @@ function createTonSDK(log12, db) {
6953
5675
  if (!addr) return [];
6954
5676
  const response = await tonapiFetch(`/accounts/${encodeURIComponent(addr)}/jettons`);
6955
5677
  if (!response.ok) {
6956
- log12.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
5678
+ log10.error(`ton.getJettonBalances() TonAPI error: ${response.status}`);
6957
5679
  return [];
6958
5680
  }
6959
5681
  const data = await response.json();
@@ -6977,7 +5699,7 @@ function createTonSDK(log12, db) {
6977
5699
  }
6978
5700
  return balances;
6979
5701
  } catch (err) {
6980
- log12.error("ton.getJettonBalances() failed:", err);
5702
+ log10.error("ton.getJettonBalances() failed:", err);
6981
5703
  return [];
6982
5704
  }
6983
5705
  },
@@ -6986,7 +5708,7 @@ function createTonSDK(log12, db) {
6986
5708
  const response = await tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`);
6987
5709
  if (response.status === 404) return null;
6988
5710
  if (!response.ok) {
6989
- log12.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
5711
+ log10.error(`ton.getJettonInfo() TonAPI error: ${response.status}`);
6990
5712
  return null;
6991
5713
  }
6992
5714
  const data = await response.json();
@@ -7004,7 +5726,7 @@ function createTonSDK(log12, db) {
7004
5726
  image: data.preview || metadata.image || void 0
7005
5727
  };
7006
5728
  } catch (err) {
7007
- log12.error("ton.getJettonInfo() failed:", err);
5729
+ log10.error("ton.getJettonInfo() failed:", err);
7008
5730
  return null;
7009
5731
  }
7010
5732
  },
@@ -7097,7 +5819,7 @@ function createTonSDK(log12, db) {
7097
5819
  if (status === 429 || status && status >= 500) {
7098
5820
  invalidateTonClientCache();
7099
5821
  if (attempt < MAX_SEND_ATTEMPTS) {
7100
- log12.warn(
5822
+ log10.warn(
7101
5823
  `sendJetton attempt ${attempt} failed (${status}): ${JSON.stringify(respData ?? err.message)}, retrying...`
7102
5824
  );
7103
5825
  await new Promise((r3) => setTimeout(r3, 1e3 * attempt));
@@ -7127,14 +5849,14 @@ function createTonSDK(log12, db) {
7127
5849
  try {
7128
5850
  const response = await tonapiFetch(`/accounts/${encodeURIComponent(ownerAddress)}/jettons`);
7129
5851
  if (!response.ok) {
7130
- log12.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
5852
+ log10.error(`ton.getJettonWalletAddress() TonAPI error: ${response.status}`);
7131
5853
  return null;
7132
5854
  }
7133
5855
  const data = await response.json();
7134
5856
  const match = findJettonBalance(data.balances ?? [], jettonAddress);
7135
5857
  return match ? match.wallet_address.address : null;
7136
5858
  } catch (err) {
7137
- log12.error("ton.getJettonWalletAddress() failed:", err);
5859
+ log10.error("ton.getJettonWalletAddress() failed:", err);
7138
5860
  return null;
7139
5861
  }
7140
5862
  },
@@ -7157,7 +5879,7 @@ function createTonSDK(log12, db) {
7157
5879
  if (!keyPair) {
7158
5880
  throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
7159
5881
  }
7160
- const boc = await withTxLock(async () => {
5882
+ const txResult = await withTxLock(async () => {
7161
5883
  const wallet = WalletContractV5R14.create({
7162
5884
  workchain: 0,
7163
5885
  publicKey: keyPair.publicKey
@@ -7165,8 +5887,10 @@ function createTonSDK(log12, db) {
7165
5887
  const client = await getCachedTonClient();
7166
5888
  const contract = client.open(wallet);
7167
5889
  const seqno = await contract.getSeqno();
5890
+ const validUntil = Math.floor(Date.now() / 1e3) + 120;
7168
5891
  const transferCell = wallet.createTransfer({
7169
5892
  seqno,
5893
+ timeout: validUntil,
7170
5894
  secretKey: keyPair.secretKey,
7171
5895
  sendMode: SendMode4.PAY_GAS_SEPARATELY,
7172
5896
  messages: [
@@ -7189,10 +5913,18 @@ function createTonSDK(log12, db) {
7189
5913
  body: transferCell
7190
5914
  })
7191
5915
  ).endCell();
7192
- return extMsg.toBoc().toString("base64");
5916
+ const boc = extMsg.toBoc().toString("base64");
5917
+ return { boc, seqno, validUntil, walletAddress: wallet.address.toRawString() };
7193
5918
  });
7194
5919
  return {
7195
- 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,
7196
5928
  publicKey: walletData.publicKey,
7197
5929
  walletVersion: "v5r1"
7198
5930
  };
@@ -7259,7 +5991,7 @@ function createTonSDK(log12, db) {
7259
5991
  if (!keyPair) {
7260
5992
  throw new PluginSDKError("Wallet key derivation failed", "OPERATION_FAILED");
7261
5993
  }
7262
- const boc = await withTxLock(async () => {
5994
+ const txResult = await withTxLock(async () => {
7263
5995
  const wallet = WalletContractV5R14.create({
7264
5996
  workchain: 0,
7265
5997
  publicKey: keyPair.publicKey
@@ -7267,8 +5999,10 @@ function createTonSDK(log12, db) {
7267
5999
  const client = await getCachedTonClient();
7268
6000
  const walletContract = client.open(wallet);
7269
6001
  const seqno = await walletContract.getSeqno();
6002
+ const validUntil = Math.floor(Date.now() / 1e3) + 120;
7270
6003
  const transferCell = wallet.createTransfer({
7271
6004
  seqno,
6005
+ timeout: validUntil,
7272
6006
  secretKey: keyPair.secretKey,
7273
6007
  sendMode: SendMode4.PAY_GAS_SEPARATELY,
7274
6008
  messages: [
@@ -7291,10 +6025,18 @@ function createTonSDK(log12, db) {
7291
6025
  body: transferCell
7292
6026
  })
7293
6027
  ).endCell();
7294
- return extMsg.toBoc().toString("base64");
6028
+ const boc = extMsg.toBoc().toString("base64");
6029
+ return { boc, seqno, validUntil, walletAddress: wallet.address.toRawString() };
7295
6030
  });
7296
6031
  return {
7297
- 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,
7298
6040
  publicKey: walletData.publicKey,
7299
6041
  walletVersion: "v5r1"
7300
6042
  };
@@ -7311,7 +6053,7 @@ function createTonSDK(log12, db) {
7311
6053
  const wallet = loadWallet();
7312
6054
  return wallet?.publicKey ?? null;
7313
6055
  } catch (err) {
7314
- log12.error("ton.getPublicKey() failed:", err);
6056
+ log10.error("ton.getPublicKey() failed:", err);
7315
6057
  return null;
7316
6058
  }
7317
6059
  },
@@ -7327,14 +6069,14 @@ function createTonSDK(log12, db) {
7327
6069
  `/accounts/${encodeURIComponent(addr)}/nfts?limit=100&indirect_ownership=true`
7328
6070
  );
7329
6071
  if (!response.ok) {
7330
- log12.error(`ton.getNftItems() TonAPI error: ${response.status}`);
6072
+ log10.error(`ton.getNftItems() TonAPI error: ${response.status}`);
7331
6073
  return [];
7332
6074
  }
7333
6075
  const data = await response.json();
7334
6076
  if (!Array.isArray(data.nft_items)) return [];
7335
6077
  return data.nft_items.filter((item) => item.trust !== "blacklist").map((item) => mapNftItem(item));
7336
6078
  } catch (err) {
7337
- log12.error("ton.getNftItems() failed:", err);
6079
+ log10.error("ton.getNftItems() failed:", err);
7338
6080
  return [];
7339
6081
  }
7340
6082
  },
@@ -7343,13 +6085,13 @@ function createTonSDK(log12, db) {
7343
6085
  const response = await tonapiFetch(`/nfts/${encodeURIComponent(nftAddress)}`);
7344
6086
  if (response.status === 404) return null;
7345
6087
  if (!response.ok) {
7346
- log12.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
6088
+ log10.error(`ton.getNftInfo() TonAPI error: ${response.status}`);
7347
6089
  return null;
7348
6090
  }
7349
6091
  const item = await response.json();
7350
6092
  return mapNftItem(item);
7351
6093
  } catch (err) {
7352
- log12.error("ton.getNftInfo() failed:", err);
6094
+ log10.error("ton.getNftInfo() failed:", err);
7353
6095
  return null;
7354
6096
  }
7355
6097
  },
@@ -7382,7 +6124,7 @@ function createTonSDK(log12, db) {
7382
6124
  `/rates?tokens=${encodeURIComponent(jettonAddress)}&currencies=usd,ton`
7383
6125
  );
7384
6126
  if (!response.ok) {
7385
- log12.debug(`ton.getJettonPrice() TonAPI error: ${response.status}`);
6127
+ log10.debug(`ton.getJettonPrice() TonAPI error: ${response.status}`);
7386
6128
  return null;
7387
6129
  }
7388
6130
  const data = await response.json();
@@ -7396,7 +6138,7 @@ function createTonSDK(log12, db) {
7396
6138
  change30d: rateData.diff_30d?.USD ?? null
7397
6139
  };
7398
6140
  } catch (err) {
7399
- log12.debug("ton.getJettonPrice() failed:", err);
6141
+ log10.debug("ton.getJettonPrice() failed:", err);
7400
6142
  return null;
7401
6143
  }
7402
6144
  },
@@ -7410,7 +6152,7 @@ function createTonSDK(log12, db) {
7410
6152
  tonapiFetch(`/jettons/${encodeURIComponent(jettonAddress)}`)
7411
6153
  ]);
7412
6154
  if (!holdersResponse.ok) {
7413
- log12.debug(`ton.getJettonHolders() TonAPI error: ${holdersResponse.status}`);
6155
+ log10.debug(`ton.getJettonHolders() TonAPI error: ${holdersResponse.status}`);
7414
6156
  return [];
7415
6157
  }
7416
6158
  const data = await holdersResponse.json();
@@ -7430,7 +6172,7 @@ function createTonSDK(log12, db) {
7430
6172
  };
7431
6173
  });
7432
6174
  } catch (err) {
7433
- log12.debug("ton.getJettonHolders() failed:", err);
6175
+ log10.debug("ton.getJettonHolders() failed:", err);
7434
6176
  return [];
7435
6177
  }
7436
6178
  },
@@ -7475,15 +6217,13 @@ function createTonSDK(log12, db) {
7475
6217
  const geckoData = await geckoResponse.json();
7476
6218
  const attrs = geckoData.data?.attributes;
7477
6219
  if (attrs) {
7478
- if (attrs.volume_usd?.h24) {
7479
- volume24h = `$${parseFloat(attrs.volume_usd.h24).toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
7480
- }
7481
- if (attrs.fdv_usd) {
7482
- fdv = `$${parseFloat(attrs.fdv_usd).toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
7483
- }
7484
- if (attrs.market_cap_usd) {
7485
- marketCap = `$${parseFloat(attrs.market_cap_usd).toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
7486
- }
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);
7487
6227
  }
7488
6228
  }
7489
6229
  return {
@@ -7502,13 +6242,251 @@ function createTonSDK(log12, db) {
7502
6242
  holders: holdersCount
7503
6243
  };
7504
6244
  } catch (err) {
7505
- log12.debug("ton.getJettonHistory() failed:", err);
6245
+ log10.debug("ton.getJettonHistory() failed:", err);
7506
6246
  return null;
7507
6247
  }
7508
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
+ },
7509
6487
  // ─── Sub-namespaces ───────────────────────────────────────────
7510
- dex: Object.freeze(createDexSDK(log12)),
7511
- dns: Object.freeze(createDnsSDK(log12))
6488
+ dex: Object.freeze(createDexSDK(log10)),
6489
+ dns: Object.freeze(createDnsSDK(log10))
7512
6490
  };
7513
6491
  }
7514
6492
  function mapNftItem(item) {
@@ -7561,7 +6539,7 @@ async function getApi() {
7561
6539
  }
7562
6540
 
7563
6541
  // src/sdk/telegram-messages.ts
7564
- function createTelegramMessagesSDK(bridge, log12) {
6542
+ function createTelegramMessagesSDK(bridge, log10) {
7565
6543
  function requireBridge2() {
7566
6544
  requireBridge(bridge);
7567
6545
  }
@@ -7668,7 +6646,7 @@ function createTelegramMessagesSDK(bridge, log12) {
7668
6646
  return (resultData.messages ?? []).map(toSimpleMessage);
7669
6647
  } catch (err) {
7670
6648
  if (err instanceof PluginSDKError) throw err;
7671
- log12.error("telegram.searchMessages() failed:", err);
6649
+ log10.error("telegram.searchMessages() failed:", err);
7672
6650
  return [];
7673
6651
  }
7674
6652
  },
@@ -7911,7 +6889,7 @@ function createTelegramMessagesSDK(bridge, log12) {
7911
6889
  return messages;
7912
6890
  } catch (err) {
7913
6891
  if (err instanceof PluginSDKError) throw err;
7914
- log12.error("telegram.getScheduledMessages() failed:", err);
6892
+ log10.error("telegram.getScheduledMessages() failed:", err);
7915
6893
  return [];
7916
6894
  }
7917
6895
  },
@@ -7972,7 +6950,7 @@ function createTelegramMessagesSDK(bridge, log12) {
7972
6950
  }
7973
6951
 
7974
6952
  // src/sdk/telegram-social.ts
7975
- function createTelegramSocialSDK(bridge, log12) {
6953
+ function createTelegramSocialSDK(bridge, log10) {
7976
6954
  function requireBridge2() {
7977
6955
  requireBridge(bridge);
7978
6956
  }
@@ -8047,7 +7025,7 @@ function createTelegramSocialSDK(bridge, log12) {
8047
7025
  return null;
8048
7026
  } catch (err) {
8049
7027
  if (err instanceof PluginSDKError) throw err;
8050
- log12.error("telegram.getChatInfo() failed:", err);
7028
+ log10.error("telegram.getChatInfo() failed:", err);
8051
7029
  return null;
8052
7030
  }
8053
7031
  },
@@ -8161,7 +7139,7 @@ function createTelegramSocialSDK(bridge, log12) {
8161
7139
  });
8162
7140
  } catch (err) {
8163
7141
  if (err instanceof PluginSDKError) throw err;
8164
- log12.error("telegram.getParticipants() failed:", err);
7142
+ log10.error("telegram.getParticipants() failed:", err);
8165
7143
  return [];
8166
7144
  }
8167
7145
  },
@@ -8523,7 +7501,7 @@ function createTelegramSocialSDK(bridge, log12) {
8523
7501
  }));
8524
7502
  } catch (err) {
8525
7503
  if (err instanceof PluginSDKError) throw err;
8526
- log12.error("telegram.getDialogs() failed:", err);
7504
+ log10.error("telegram.getDialogs() failed:", err);
8527
7505
  return [];
8528
7506
  }
8529
7507
  },
@@ -8537,7 +7515,7 @@ function createTelegramSocialSDK(bridge, log12) {
8537
7515
  return messages.map(toSimpleMessage);
8538
7516
  } catch (err) {
8539
7517
  if (err instanceof PluginSDKError) throw err;
8540
- log12.error("telegram.getHistory() failed:", err);
7518
+ log10.error("telegram.getHistory() failed:", err);
8541
7519
  return [];
8542
7520
  }
8543
7521
  },
@@ -8568,7 +7546,7 @@ function createTelegramSocialSDK(bridge, log12) {
8568
7546
  }));
8569
7547
  } catch (err) {
8570
7548
  if (err instanceof PluginSDKError) throw err;
8571
- log12.error("telegram.getStarsTransactions() failed:", err);
7549
+ log10.error("telegram.getStarsTransactions() failed:", err);
8572
7550
  return [];
8573
7551
  }
8574
7552
  },
@@ -8673,7 +7651,7 @@ function createTelegramSocialSDK(bridge, log12) {
8673
7651
  };
8674
7652
  } catch (err) {
8675
7653
  if (err instanceof PluginSDKError) throw err;
8676
- log12.error("telegram.getCollectibleInfo() failed:", err);
7654
+ log10.error("telegram.getCollectibleInfo() failed:", err);
8677
7655
  return null;
8678
7656
  }
8679
7657
  },
@@ -8711,7 +7689,7 @@ function createTelegramSocialSDK(bridge, log12) {
8711
7689
  } catch (err) {
8712
7690
  if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
8713
7691
  if (err instanceof PluginSDKError) throw err;
8714
- log12.error("telegram.getUniqueGift() failed:", err);
7692
+ log10.error("telegram.getUniqueGift() failed:", err);
8715
7693
  return null;
8716
7694
  }
8717
7695
  },
@@ -8737,7 +7715,7 @@ function createTelegramSocialSDK(bridge, log12) {
8737
7715
  } catch (err) {
8738
7716
  if (err.errorMessage === "STARGIFT_SLUG_INVALID") return null;
8739
7717
  if (err instanceof PluginSDKError) throw err;
8740
- log12.error("telegram.getUniqueGiftValue() failed:", err);
7718
+ log10.error("telegram.getUniqueGiftValue() failed:", err);
8741
7719
  return null;
8742
7720
  }
8743
7721
  },
@@ -8848,7 +7826,7 @@ function createTelegramSocialSDK(bridge, log12) {
8848
7826
  }
8849
7827
 
8850
7828
  // src/sdk/telegram.ts
8851
- function createTelegramSDK(bridge, log12) {
7829
+ function createTelegramSDK(bridge, log10) {
8852
7830
  function requireBridge2() {
8853
7831
  requireBridge(bridge);
8854
7832
  }
@@ -8952,7 +7930,7 @@ function createTelegramSDK(bridge, log12) {
8952
7930
  timestamp: m.timestamp
8953
7931
  }));
8954
7932
  } catch (err) {
8955
- log12.error("telegram.getMessages() failed:", err);
7933
+ log10.error("telegram.getMessages() failed:", err);
8956
7934
  return [];
8957
7935
  }
8958
7936
  },
@@ -8974,7 +7952,7 @@ function createTelegramSDK(bridge, log12) {
8974
7952
  return bridge.isAvailable();
8975
7953
  },
8976
7954
  getRawClient() {
8977
- log12.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
7955
+ log10.warn("getRawClient() called \u2014 this bypasses SDK sandbox guarantees");
8978
7956
  if (!bridge.isAvailable()) return null;
8979
7957
  try {
8980
7958
  return bridge.getClient().getClient();
@@ -8983,8 +7961,8 @@ function createTelegramSDK(bridge, log12) {
8983
7961
  }
8984
7962
  },
8985
7963
  // Spread extended methods from sub-modules
8986
- ...createTelegramMessagesSDK(bridge, log12),
8987
- ...createTelegramSocialSDK(bridge, log12)
7964
+ ...createTelegramMessagesSDK(bridge, log10),
7965
+ ...createTelegramSocialSDK(bridge, log10)
8988
7966
  };
8989
7967
  }
8990
7968
 
@@ -9025,23 +8003,23 @@ function deletePluginSecret(pluginName, key) {
9025
8003
  function listPluginSecretKeys(pluginName) {
9026
8004
  return Object.keys(readSecretsFile(pluginName));
9027
8005
  }
9028
- function createSecretsSDK(pluginName, pluginConfig, log12) {
8006
+ function createSecretsSDK(pluginName, pluginConfig, log10) {
9029
8007
  const envPrefix = pluginName.replace(/-/g, "_").toUpperCase();
9030
8008
  function get(key) {
9031
8009
  const envKey = `${envPrefix}_${key.toUpperCase()}`;
9032
8010
  const envValue = process.env[envKey];
9033
8011
  if (envValue) {
9034
- log12.debug(`Secret "${key}" resolved from env var ${envKey}`);
8012
+ log10.debug(`Secret "${key}" resolved from env var ${envKey}`);
9035
8013
  return envValue;
9036
8014
  }
9037
8015
  const stored = readSecretsFile(pluginName);
9038
8016
  if (key in stored && stored[key]) {
9039
- log12.debug(`Secret "${key}" resolved from secrets store`);
8017
+ log10.debug(`Secret "${key}" resolved from secrets store`);
9040
8018
  return stored[key];
9041
8019
  }
9042
8020
  const configValue = pluginConfig[key];
9043
8021
  if (configValue !== void 0 && configValue !== null) {
9044
- log12.debug(`Secret "${key}" resolved from pluginConfig`);
8022
+ log10.debug(`Secret "${key}" resolved from pluginConfig`);
9045
8023
  return String(configValue);
9046
8024
  }
9047
8025
  return void 0;
@@ -9143,7 +8121,7 @@ function createStorageSDK(db) {
9143
8121
  }
9144
8122
 
9145
8123
  // src/sdk/bot.ts
9146
- function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter, log12) {
8124
+ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLimiter, log10) {
9147
8125
  if (!router || !manifest || !manifest.inline && !manifest.callbacks) {
9148
8126
  return null;
9149
8127
  }
@@ -9166,7 +8144,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
9166
8144
  },
9167
8145
  onInlineQuery(handler) {
9168
8146
  if (handlers.onInlineQuery) {
9169
- log12.warn("onInlineQuery called again \u2014 overwriting previous handler");
8147
+ log10.warn("onInlineQuery called again \u2014 overwriting previous handler");
9170
8148
  }
9171
8149
  handlers.onInlineQuery = async (ctx) => {
9172
8150
  if (rateLimiter) {
@@ -9212,7 +8190,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
9212
8190
  return;
9213
8191
  } catch (error) {
9214
8192
  if (error?.errorMessage === "MESSAGE_NOT_MODIFIED") return;
9215
- 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}`);
9216
8194
  }
9217
8195
  }
9218
8196
  if (grammyBot) {
@@ -9225,7 +8203,7 @@ function createBotSDK(router, gramjsBot, grammyBot, pluginName, manifest, rateLi
9225
8203
  });
9226
8204
  } catch (error) {
9227
8205
  if (error?.description?.includes("message is not modified")) return;
9228
- log12.error(`Failed to edit inline message: ${error?.description || error}`);
8206
+ log10.error(`Failed to edit inline message: ${error?.description || error}`);
9229
8207
  }
9230
8208
  }
9231
8209
  },
@@ -9284,13 +8262,13 @@ function createSafeDb(db) {
9284
8262
  });
9285
8263
  }
9286
8264
  function createPluginSDK(deps, opts) {
9287
- const log12 = createLogger2(opts.pluginName);
8265
+ const log10 = createLogger2(opts.pluginName);
9288
8266
  const safeDb = opts.db ? createSafeDb(opts.db) : null;
9289
- const ton = Object.freeze(createTonSDK(log12, safeDb));
9290
- const telegram = Object.freeze(createTelegramSDK(deps.bridge, log12));
9291
- 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));
9292
8270
  const storage = safeDb ? Object.freeze(createStorageSDK(safeDb)) : null;
9293
- const frozenLog = Object.freeze(log12);
8271
+ const frozenLog = Object.freeze(log10);
9294
8272
  const frozenConfig = Object.freeze(JSON.parse(JSON.stringify(opts.sanitizedConfig ?? {})));
9295
8273
  const frozenPluginConfig = Object.freeze(JSON.parse(JSON.stringify(opts.pluginConfig ?? {})));
9296
8274
  let cachedBot;
@@ -9321,20 +8299,20 @@ function createPluginSDK(deps, opts) {
9321
8299
  },
9322
8300
  on(hookName, handler, onOpts) {
9323
8301
  if (!opts.hookRegistry) {
9324
- log12.warn(`Hook registration unavailable \u2014 sdk.on() ignored`);
8302
+ log10.warn(`Hook registration unavailable \u2014 sdk.on() ignored`);
9325
8303
  return;
9326
8304
  }
9327
8305
  if (opts.declaredHooks) {
9328
8306
  const declared = opts.declaredHooks.some((h2) => h2.name === hookName);
9329
8307
  if (!declared) {
9330
- log12.warn(`Hook "${hookName}" not declared in manifest \u2014 registration rejected`);
8308
+ log10.warn(`Hook "${hookName}" not declared in manifest \u2014 registration rejected`);
9331
8309
  return;
9332
8310
  }
9333
8311
  }
9334
8312
  const rawPriority = Number(onOpts?.priority) || 0;
9335
8313
  const clampedPriority = Math.max(-1e3, Math.min(1e3, rawPriority));
9336
8314
  if (rawPriority !== clampedPriority) {
9337
- log12.debug(`Hook "${hookName}" priority ${rawPriority} clamped to ${clampedPriority}`);
8315
+ log10.debug(`Hook "${hookName}" priority ${rawPriority} clamped to ${clampedPriority}`);
9338
8316
  }
9339
8317
  const registered = opts.hookRegistry.register({
9340
8318
  pluginId: opts.pluginName,
@@ -9344,7 +8322,7 @@ function createPluginSDK(deps, opts) {
9344
8322
  globalPriority: opts.globalPriority ?? 0
9345
8323
  });
9346
8324
  if (!registered) {
9347
- log12.warn(
8325
+ log10.warn(
9348
8326
  `Hook registration limit reached for plugin "${opts.pluginName}" \u2014 "${hookName}" rejected`
9349
8327
  );
9350
8328
  }
@@ -9463,7 +8441,7 @@ var HookRegistry = class {
9463
8441
 
9464
8442
  // src/agent/tools/plugin-loader.ts
9465
8443
  var execFileAsync = promisify(execFile);
9466
- var log9 = createLogger("PluginLoader");
8444
+ var log7 = createLogger("PluginLoader");
9467
8445
  var PLUGIN_DATA_DIR = join3(TELETON_ROOT, "plugins", "data");
9468
8446
  function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookRegistry, pluginPriorities) {
9469
8447
  let manifest = null;
@@ -9471,7 +8449,7 @@ function adaptPlugin(raw, entryName, config, loadedModuleNames, sdkDeps, hookReg
9471
8449
  try {
9472
8450
  manifest = validateManifest(raw.manifest);
9473
8451
  } catch (err) {
9474
- log9.warn(
8452
+ log7.warn(
9475
8453
  `[${entryName}] invalid manifest, ignoring: ${err instanceof Error ? err.message : err}`
9476
8454
  );
9477
8455
  }
@@ -9665,7 +8643,7 @@ async function ensurePluginDeps(pluginDir, pluginEntry) {
9665
8643
  const nodeModules = join3(pluginDir, "node_modules");
9666
8644
  if (!existsSync5(pkgJson)) return;
9667
8645
  if (!existsSync5(lockfile)) {
9668
- log9.warn(
8646
+ log7.warn(
9669
8647
  `[${pluginEntry}] package.json without package-lock.json \u2014 skipping (lockfile required)`
9670
8648
  );
9671
8649
  return;
@@ -9674,16 +8652,16 @@ async function ensurePluginDeps(pluginDir, pluginEntry) {
9674
8652
  const marker = join3(nodeModules, ".package-lock.json");
9675
8653
  if (existsSync5(marker) && statSync(marker).mtimeMs >= statSync(lockfile).mtimeMs) return;
9676
8654
  }
9677
- log9.info(`[${pluginEntry}] Installing dependencies...`);
8655
+ log7.info(`[${pluginEntry}] Installing dependencies...`);
9678
8656
  try {
9679
8657
  await execFileAsync("npm", ["ci", "--ignore-scripts", "--no-audit", "--no-fund"], {
9680
8658
  cwd: pluginDir,
9681
8659
  timeout: 6e4,
9682
8660
  env: { ...process.env, NODE_ENV: "production" }
9683
8661
  });
9684
- log9.info(`[${pluginEntry}] Dependencies installed`);
8662
+ log7.info(`[${pluginEntry}] Dependencies installed`);
9685
8663
  } catch (err) {
9686
- 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)}`);
9687
8665
  }
9688
8666
  }
9689
8667
  async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
@@ -9736,7 +8714,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
9736
8714
  );
9737
8715
  for (const result of loadResults) {
9738
8716
  if (result.status === "rejected") {
9739
- log9.error(
8717
+ log7.error(
9740
8718
  `Plugin failed to load: ${result.reason instanceof Error ? result.reason.message : result.reason}`
9741
8719
  );
9742
8720
  continue;
@@ -9744,7 +8722,7 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
9744
8722
  const { entry, mod } = result.value;
9745
8723
  try {
9746
8724
  if (!mod.tools || typeof mod.tools !== "function" && !Array.isArray(mod.tools)) {
9747
- log9.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
8725
+ log7.warn(`Plugin "${entry}": no 'tools' array or function exported, skipping`);
9748
8726
  continue;
9749
8727
  }
9750
8728
  const adapted = adaptPlugin(
@@ -9757,18 +8735,28 @@ async function loadEnhancedPlugins(config, loadedModuleNames, sdkDeps, db) {
9757
8735
  pluginPriorities
9758
8736
  );
9759
8737
  if (loadedNames.has(adapted.name)) {
9760
- log9.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
8738
+ log7.warn(`Plugin "${adapted.name}" already loaded, skipping duplicate from "${entry}"`);
9761
8739
  continue;
9762
8740
  }
9763
8741
  loadedNames.add(adapted.name);
9764
8742
  modules.push(adapted);
9765
8743
  } catch (err) {
9766
- 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}`);
9767
8745
  }
9768
8746
  }
9769
8747
  return { modules, hookRegistry };
9770
8748
  }
9771
8749
 
8750
+ // src/agent/token-usage.ts
8751
+ var globalTokenUsage = { totalTokens: 0, totalCost: 0 };
8752
+ function getTokenUsage() {
8753
+ return { ...globalTokenUsage };
8754
+ }
8755
+ function accumulateTokenUsage(usage) {
8756
+ globalTokenUsage.totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
8757
+ globalTokenUsage.totalCost += usage.totalCost;
8758
+ }
8759
+
9772
8760
  // src/ton-proxy/manager.ts
9773
8761
  import { spawn, execSync } from "child_process";
9774
8762
  import {
@@ -9782,7 +8770,7 @@ import {
9782
8770
  import { mkdir } from "fs/promises";
9783
8771
  import { join as join4 } from "path";
9784
8772
  import { pipeline } from "stream/promises";
9785
- var log10 = createLogger("TonProxy");
8773
+ var log8 = createLogger("TonProxy");
9786
8774
  var GITHUB_REPO = "xssnick/Tonutils-Proxy";
9787
8775
  var BINARY_DIR = join4(TELETON_ROOT, "bin");
9788
8776
  var PID_FILE = join4(TELETON_ROOT, "ton-proxy.pid");
@@ -9817,7 +8805,7 @@ var TonProxyManager = class {
9817
8805
  */
9818
8806
  async install() {
9819
8807
  const binaryName = getBinaryName();
9820
- log10.info(`Downloading TON Proxy binary (${binaryName})...`);
8808
+ log8.info(`Downloading TON Proxy binary (${binaryName})...`);
9821
8809
  await mkdir(BINARY_DIR, { recursive: true });
9822
8810
  const releaseUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
9823
8811
  const releaseRes = await fetch(releaseUrl, {
@@ -9829,7 +8817,7 @@ var TonProxyManager = class {
9829
8817
  const release = await releaseRes.json();
9830
8818
  const tag = release.tag_name;
9831
8819
  const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${tag}/${binaryName}`;
9832
- log10.info(`Downloading ${downloadUrl}`);
8820
+ log8.info(`Downloading ${downloadUrl}`);
9833
8821
  const res = await fetch(downloadUrl);
9834
8822
  if (!res.ok || !res.body) {
9835
8823
  throw new Error(`Download failed: ${res.status} ${res.statusText}`);
@@ -9838,7 +8826,7 @@ var TonProxyManager = class {
9838
8826
  const fileStream = createWriteStream(dest);
9839
8827
  await pipeline(res.body, fileStream);
9840
8828
  chmodSync(dest, 493);
9841
- log10.info(`TON Proxy installed: ${dest} (${tag})`);
8829
+ log8.info(`TON Proxy installed: ${dest} (${tag})`);
9842
8830
  }
9843
8831
  /** Kill any orphan proxy process from a previous session */
9844
8832
  killOrphan() {
@@ -9848,7 +8836,7 @@ var TonProxyManager = class {
9848
8836
  if (pid && !isNaN(pid)) {
9849
8837
  try {
9850
8838
  process.kill(pid, 0);
9851
- log10.warn(`Killing orphan TON Proxy (PID ${pid}) from previous session`);
8839
+ log8.warn(`Killing orphan TON Proxy (PID ${pid}) from previous session`);
9852
8840
  process.kill(pid, "SIGTERM");
9853
8841
  } catch {
9854
8842
  }
@@ -9865,7 +8853,7 @@ var TonProxyManager = class {
9865
8853
  const pidMatch = out.match(/pid=(\d+)/);
9866
8854
  if (pidMatch) {
9867
8855
  const pid = parseInt(pidMatch[1], 10);
9868
- 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`);
9869
8857
  try {
9870
8858
  process.kill(pid, "SIGTERM");
9871
8859
  } catch {
@@ -9880,7 +8868,7 @@ var TonProxyManager = class {
9880
8868
  try {
9881
8869
  writeFileSync2(PID_FILE, String(pid), { mode: 384 });
9882
8870
  } catch {
9883
- log10.warn("Failed to write TON Proxy PID file");
8871
+ log8.warn("Failed to write TON Proxy PID file");
9884
8872
  }
9885
8873
  }
9886
8874
  /** Remove PID file */
@@ -9893,7 +8881,7 @@ var TonProxyManager = class {
9893
8881
  /** Start the proxy process */
9894
8882
  async start() {
9895
8883
  if (this.isRunning()) {
9896
- log10.warn("TON Proxy is already running");
8884
+ log8.warn("TON Proxy is already running");
9897
8885
  return;
9898
8886
  }
9899
8887
  this.restartCount = 0;
@@ -9904,7 +8892,7 @@ var TonProxyManager = class {
9904
8892
  }
9905
8893
  const binaryPath = this.getBinaryPath();
9906
8894
  const port = String(this.config.port);
9907
- log10.info(`Starting TON Proxy on 127.0.0.1:${port}`);
8895
+ log8.info(`Starting TON Proxy on 127.0.0.1:${port}`);
9908
8896
  this.process = spawn(binaryPath, ["-addr", `127.0.0.1:${port}`], {
9909
8897
  cwd: BINARY_DIR,
9910
8898
  stdio: ["ignore", "pipe", "pipe"],
@@ -9912,24 +8900,24 @@ var TonProxyManager = class {
9912
8900
  });
9913
8901
  this.process.stdout?.on("data", (chunk) => {
9914
8902
  const line = chunk.toString().trim();
9915
- if (line) log10.debug(`[proxy] ${line}`);
8903
+ if (line) log8.debug(`[proxy] ${line}`);
9916
8904
  });
9917
8905
  this.process.stderr?.on("data", (chunk) => {
9918
8906
  const line = chunk.toString().trim();
9919
- if (line) log10.warn(`[proxy:err] ${line}`);
8907
+ if (line) log8.warn(`[proxy:err] ${line}`);
9920
8908
  });
9921
8909
  this.process.on("exit", (code, signal) => {
9922
- log10.info(`TON Proxy exited (code=${code}, signal=${signal})`);
8910
+ log8.info(`TON Proxy exited (code=${code}, signal=${signal})`);
9923
8911
  this.process = null;
9924
8912
  this.removePidFile();
9925
8913
  if (code !== 0 && code !== null && this.restartCount < this.maxRestarts) {
9926
8914
  this.restartCount++;
9927
- log10.warn(`Auto-restarting TON Proxy (attempt ${this.restartCount}/${this.maxRestarts})`);
9928
- 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"));
9929
8917
  }
9930
8918
  });
9931
8919
  this.process.on("error", (err) => {
9932
- log10.error({ err }, "TON Proxy process error");
8920
+ log8.error({ err }, "TON Proxy process error");
9933
8921
  this.process = null;
9934
8922
  });
9935
8923
  this.startHealthCheck();
@@ -9947,14 +8935,14 @@ var TonProxyManager = class {
9947
8935
  });
9948
8936
  });
9949
8937
  if (this.process?.pid) this.writePidFile(this.process.pid);
9950
- 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})`);
9951
8939
  }
9952
8940
  /** Stop the proxy process gracefully */
9953
8941
  async stop() {
9954
8942
  this.stopHealthCheck();
9955
8943
  if (!this.process) return;
9956
8944
  this.maxRestarts = 0;
9957
- log10.info("Stopping TON Proxy...");
8945
+ log8.info("Stopping TON Proxy...");
9958
8946
  return new Promise((resolve2) => {
9959
8947
  if (!this.process) {
9960
8948
  resolve2();
@@ -9962,7 +8950,7 @@ var TonProxyManager = class {
9962
8950
  }
9963
8951
  const forceKill = setTimeout(() => {
9964
8952
  if (this.process) {
9965
- log10.warn("TON Proxy did not exit gracefully, sending SIGKILL");
8953
+ log8.warn("TON Proxy did not exit gracefully, sending SIGKILL");
9966
8954
  this.process.kill("SIGKILL");
9967
8955
  }
9968
8956
  }, KILL_GRACE_MS);
@@ -9984,7 +8972,7 @@ var TonProxyManager = class {
9984
8972
  if (existsSync6(binaryPath)) {
9985
8973
  const { unlink } = await import("fs/promises");
9986
8974
  await unlink(binaryPath);
9987
- log10.info(`TON Proxy binary removed: ${binaryPath}`);
8975
+ log8.info(`TON Proxy binary removed: ${binaryPath}`);
9988
8976
  }
9989
8977
  }
9990
8978
  /** Get proxy status for WebUI / tools */
@@ -10018,7 +9006,7 @@ var TonProxyManager = class {
10018
9006
  }).catch(() => null);
10019
9007
  clearTimeout(timeout);
10020
9008
  if (!res) {
10021
- log10.warn("TON Proxy health check failed (no response)");
9009
+ log8.warn("TON Proxy health check failed (no response)");
10022
9010
  }
10023
9011
  } catch {
10024
9012
  }
@@ -10075,7 +9063,7 @@ var tonProxyStatusExecutor = async () => {
10075
9063
  };
10076
9064
 
10077
9065
  // src/ton-proxy/module.ts
10078
- var log11 = createLogger("TonProxyModule");
9066
+ var log9 = createLogger("TonProxyModule");
10079
9067
  var manager = null;
10080
9068
  function getTonProxyManager() {
10081
9069
  return manager;
@@ -10102,9 +9090,9 @@ var tonProxyModule = {
10102
9090
  setProxyManager(manager);
10103
9091
  try {
10104
9092
  await manager.start();
10105
- log11.info(`TON Proxy started on port ${proxyConfig.port}`);
9093
+ log9.info(`TON Proxy started on port ${proxyConfig.port}`);
10106
9094
  } catch (err) {
10107
- log11.error({ err }, "Failed to start TON Proxy");
9095
+ log9.error({ err }, "Failed to start TON Proxy");
10108
9096
  manager = null;
10109
9097
  }
10110
9098
  },
@@ -10161,13 +9149,15 @@ export {
10161
9149
  validateReadPath,
10162
9150
  validateWritePath,
10163
9151
  validateDirectory,
9152
+ appendToDailyLog,
9153
+ writeSummaryToDailyLog,
10164
9154
  sanitizeForPrompt,
10165
9155
  sanitizeForContext,
10166
9156
  clearPromptCache,
10167
9157
  loadSoul,
10168
- TELEGRAM_SEND_TOOLS,
9158
+ buildSystemPrompt,
10169
9159
  getTokenUsage,
10170
- AgentRuntime,
9160
+ accumulateTokenUsage,
10171
9161
  writePluginSecret,
10172
9162
  deletePluginSecret,
10173
9163
  listPluginSecretKeys,