teleton 0.2.4 → 0.3.0

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.
@@ -1,3 +1,13 @@
1
+ import {
2
+ Mi,
3
+ Mn,
4
+ br,
5
+ dt,
6
+ le,
7
+ qn,
8
+ ut,
9
+ ye
10
+ } from "./chunk-U7FQYCBQ.js";
1
11
  import {
2
12
  initScraperDb
3
13
  } from "./chunk-DUW5VBAZ.js";
@@ -20,7 +30,7 @@ import {
20
30
  initializeMemory,
21
31
  migrateFromMainDb,
22
32
  openModuleDb
23
- } from "./chunk-TBIMVWQZ.js";
33
+ } from "./chunk-QQW6KE7Q.js";
24
34
  import {
25
35
  COINGECKO_API_URL,
26
36
  ELEVENLABS_TTS_URL,
@@ -30,7 +40,7 @@ import {
30
40
  fetchWithTimeout,
31
41
  setTonapiKey,
32
42
  tonapiFetch
33
- } from "./chunk-WMIN6AGX.js";
43
+ } from "./chunk-DAMFGHXV.js";
34
44
  import {
35
45
  COMPACTION_KEEP_RECENT,
36
46
  COMPACTION_MAX_MESSAGES,
@@ -43,8 +53,10 @@ import {
43
53
  DEBOUNCE_MAX_BUFFER_SIZE,
44
54
  DEBOUNCE_MAX_MULTIPLIER,
45
55
  DEFAULT_GIFTS_QUERY_LIMIT,
56
+ MAX_DEPENDENTS_PER_TASK,
46
57
  MAX_POLL_QUESTION_LENGTH,
47
58
  MAX_TOOL_RESULT_SIZE,
59
+ MAX_WRITE_SIZE,
48
60
  PAYMENT_TOLERANCE_RATIO,
49
61
  PENDING_HISTORY_MAX_AGE_MS,
50
62
  PENDING_HISTORY_MAX_PER_CHAT,
@@ -53,7 +65,14 @@ import {
53
65
  TELEGRAM_CONNECTION_RETRIES,
54
66
  TELEGRAM_FLOOD_SLEEP_THRESHOLD,
55
67
  TELEGRAM_MAX_MESSAGE_LENGTH
56
- } from "./chunk-QMN6ZOA5.js";
68
+ } from "./chunk-UYF4TT44.js";
69
+ import {
70
+ ALLOWED_EXTENSIONS,
71
+ MAX_FILE_SIZES,
72
+ TELETON_ROOT,
73
+ WORKSPACE_PATHS,
74
+ WORKSPACE_ROOT
75
+ } from "./chunk-EYWNOHMJ.js";
57
76
  import {
58
77
  GRAMJS_RETRY_DELAY_MS,
59
78
  MESSAGE_HANDLER_LOCK_TIMEOUT_MS,
@@ -64,29 +83,13 @@ import {
64
83
  RETRY_DEFAULT_MAX_ATTEMPTS,
65
84
  RETRY_DEFAULT_MAX_DELAY_MS,
66
85
  RETRY_DEFAULT_TIMEOUT_MS,
86
+ TOOL_EXECUTION_TIMEOUT_MS,
67
87
  TTS_TIMEOUT_MS
68
- } from "./chunk-LJXYESJJ.js";
69
- import {
70
- ALLOWED_EXTENSIONS,
71
- MAX_FILE_SIZES,
72
- TELETON_ROOT,
73
- WORKSPACE_PATHS,
74
- WORKSPACE_ROOT
75
- } from "./chunk-EYWNOHMJ.js";
88
+ } from "./chunk-LCMHAUNK.js";
76
89
  import {
77
90
  telegramGetMyGiftsExecutor,
78
91
  telegramGetMyGiftsTool
79
92
  } from "./chunk-B2PRMXOH.js";
80
- import {
81
- Mi,
82
- Mn,
83
- br,
84
- dt,
85
- le,
86
- qn,
87
- ut,
88
- ye
89
- } from "./chunk-U7FQYCBQ.js";
90
93
  import {
91
94
  __commonJS,
92
95
  __toESM
@@ -1521,7 +1524,7 @@ var PROVIDER_REGISTRY = {
1521
1524
  keyHint: "sk-or-v1-...",
1522
1525
  consoleUrl: "https://openrouter.ai/keys",
1523
1526
  defaultModel: "anthropic/claude-opus-4.5",
1524
- utilityModel: "google/gemini-2.0-flash-001:free",
1527
+ utilityModel: "google/gemini-2.5-flash-lite",
1525
1528
  toolLimit: 128,
1526
1529
  piAiProvider: "openrouter"
1527
1530
  }
@@ -2146,6 +2149,9 @@ function getProviderModel(provider, modelId) {
2146
2149
  const meta = getProviderMetadata(provider);
2147
2150
  try {
2148
2151
  const model = getModel(meta.piAiProvider, modelId);
2152
+ if (!model) {
2153
+ throw new Error(`getModel returned undefined for ${provider}/${modelId}`);
2154
+ }
2149
2155
  modelCache.set(cacheKey, model);
2150
2156
  return model;
2151
2157
  } catch (e) {
@@ -2157,6 +2163,11 @@ function getProviderModel(provider, modelId) {
2157
2163
  if (fallbackCached) return fallbackCached;
2158
2164
  try {
2159
2165
  const model = getModel(meta.piAiProvider, meta.defaultModel);
2166
+ if (!model) {
2167
+ throw new Error(
2168
+ `Fallback model ${meta.defaultModel} also returned undefined for ${provider}`
2169
+ );
2170
+ }
2160
2171
  modelCache.set(fallbackKey, model);
2161
2172
  return model;
2162
2173
  } catch {
@@ -3409,7 +3420,13 @@ ${statsContext}`;
3409
3420
  const providerMeta = getProviderMetadata(
3410
3421
  this.config.agent.provider || "anthropic"
3411
3422
  );
3412
- const tools23 = this.toolRegistry?.getForContext(isGroup ?? false, providerMeta.toolLimit);
3423
+ const isAdmin = toolContext?.config?.telegram.admin_ids.includes(toolContext.senderId) ?? false;
3424
+ const tools23 = this.toolRegistry?.getForContext(
3425
+ isGroup ?? false,
3426
+ providerMeta.toolLimit,
3427
+ chatId,
3428
+ isAdmin
3429
+ );
3413
3430
  const maxIterations = this.config.agent.max_agentic_iterations || 5;
3414
3431
  let iteration = 0;
3415
3432
  let overflowResets = 0;
@@ -3625,20 +3642,9 @@ ${statsContext}`;
3625
3642
  clearHistory(chatId) {
3626
3643
  const db3 = getDatabase().getDb();
3627
3644
  db3.prepare(
3628
- `
3629
- DELETE FROM tg_messages_fts
3630
- WHERE rowid IN (
3631
- SELECT rowid FROM tg_messages WHERE chat_id = ?
3632
- )
3633
- `
3634
- ).run(chatId);
3635
- db3.prepare(
3636
- `
3637
- DELETE FROM tg_messages_vec
3638
- WHERE id IN (
3645
+ `DELETE FROM tg_messages_vec WHERE id IN (
3639
3646
  SELECT id FROM tg_messages WHERE chat_id = ?
3640
- )
3641
- `
3647
+ )`
3642
3648
  ).run(chatId);
3643
3649
  db3.prepare(`DELETE FROM tg_messages WHERE chat_id = ?`).run(chatId);
3644
3650
  resetSession(chatId);
@@ -4598,7 +4604,7 @@ var MessageHandler = class {
4598
4604
  reason: "Already processed"
4599
4605
  };
4600
4606
  }
4601
- if (message.isGroup && message.isBot) {
4607
+ if (message.isBot) {
4602
4608
  return {
4603
4609
  message,
4604
4610
  isAdmin,
@@ -4840,7 +4846,21 @@ import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto
4840
4846
  import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
4841
4847
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync6, chmodSync as chmodSync2 } from "fs";
4842
4848
  import { join as join6, dirname as dirname5 } from "path";
4849
+
4850
+ // src/ton/endpoint.ts
4843
4851
  import { getHttpEndpoint } from "@orbs-network/ton-access";
4852
+ var ENDPOINT_CACHE_TTL_MS = 6e4;
4853
+ var _cache = null;
4854
+ async function getCachedHttpEndpoint() {
4855
+ if (_cache && Date.now() - _cache.ts < ENDPOINT_CACHE_TTL_MS) {
4856
+ return _cache.url;
4857
+ }
4858
+ const url = await getHttpEndpoint({ network: "mainnet" });
4859
+ _cache = { url, ts: Date.now() };
4860
+ return url;
4861
+ }
4862
+
4863
+ // src/ton/wallet-service.ts
4844
4864
  var WALLET_FILE = join6(TELETON_ROOT, "wallet.json");
4845
4865
  async function generateWallet() {
4846
4866
  const mnemonic = await mnemonicNew(24);
@@ -4906,7 +4926,7 @@ function getWalletAddress() {
4906
4926
  }
4907
4927
  async function getWalletBalance(address4) {
4908
4928
  try {
4909
- const endpoint = await getHttpEndpoint({ network: "mainnet" });
4929
+ const endpoint = await getCachedHttpEndpoint();
4910
4930
  const client = new TonClient({ endpoint });
4911
4931
  const { Address: Address19 } = await import("@ton/core");
4912
4932
  const addressObj = Address19.parse(address4);
@@ -5000,15 +5020,20 @@ function initDealsConfig(yaml) {
5000
5020
  // src/telegram/admin.ts
5001
5021
  var VALID_DM_POLICIES = ["open", "allowlist", "pairing", "disabled"];
5002
5022
  var VALID_GROUP_POLICIES = ["open", "allowlist", "disabled"];
5023
+ var VALID_MODULE_LEVELS = ["open", "admin", "disabled"];
5003
5024
  var AdminHandler = class {
5004
5025
  bridge;
5005
5026
  config;
5006
5027
  agent;
5007
5028
  paused = false;
5008
- constructor(bridge, config, agent) {
5029
+ permissions;
5030
+ registry;
5031
+ constructor(bridge, config, agent, permissions, registry) {
5009
5032
  this.bridge = bridge;
5010
5033
  this.config = config;
5011
5034
  this.agent = agent;
5035
+ this.permissions = permissions ?? null;
5036
+ this.registry = registry ?? null;
5012
5037
  }
5013
5038
  /**
5014
5039
  * Check if user is admin
@@ -5043,7 +5068,7 @@ var AdminHandler = class {
5043
5068
  /**
5044
5069
  * Handle admin command
5045
5070
  */
5046
- async handleCommand(command, chatId, senderId) {
5071
+ async handleCommand(command, chatId, senderId, isGroup) {
5047
5072
  if (!this.isAdmin(senderId)) {
5048
5073
  return "\u26D4 Admin access required";
5049
5074
  }
@@ -5074,6 +5099,8 @@ var AdminHandler = class {
5074
5099
  return await this.handleStopCommand();
5075
5100
  case "verbose":
5076
5101
  return this.handleVerboseCommand();
5102
+ case "modules":
5103
+ return this.handleModulesCommand(command, isGroup ?? false);
5077
5104
  case "help":
5078
5105
  return this.handleHelpCommand();
5079
5106
  case "ping":
@@ -5286,6 +5313,124 @@ Usage:
5286
5313
  setVerbose(next);
5287
5314
  return next ? "\u{1F50A} Verbose logging **ON**" : "\u{1F507} Verbose logging **OFF**";
5288
5315
  }
5316
+ /**
5317
+ * /modules - Manage per-group module permissions
5318
+ */
5319
+ handleModulesCommand(command, isGroup) {
5320
+ if (!this.permissions || !this.registry) {
5321
+ return "\u274C Module permissions non disponible";
5322
+ }
5323
+ if (!isGroup) {
5324
+ return "\u274C /modules est uniquement disponible dans les groupes";
5325
+ }
5326
+ const chatId = command.chatId;
5327
+ const sub = command.args[0]?.toLowerCase();
5328
+ if (!sub) {
5329
+ return this.listModules(chatId);
5330
+ }
5331
+ switch (sub) {
5332
+ case "set":
5333
+ return this.setModuleLevel(chatId, command.args[1], command.args[2], command.senderId);
5334
+ case "info":
5335
+ return this.showModuleInfo(command.args[1], chatId);
5336
+ case "reset":
5337
+ return this.resetModules(chatId, command.args[1]);
5338
+ default:
5339
+ return `\u274C Sous-commande inconnue: "${sub}"
5340
+
5341
+ Usage: /modules | /modules set <module> <level> | /modules info <module> | /modules reset [module]`;
5342
+ }
5343
+ }
5344
+ listModules(chatId) {
5345
+ const modules = this.registry.getAvailableModules();
5346
+ const overrides = this.permissions.getOverrides(chatId);
5347
+ const lines = ["\u{1F9E9} **Modules** (ce groupe)\n"];
5348
+ for (const mod of modules) {
5349
+ const count = this.registry.getModuleToolCount(mod);
5350
+ const level = overrides.get(mod) ?? "open";
5351
+ const isProtected = this.permissions.isProtected(mod);
5352
+ let icon;
5353
+ switch (level) {
5354
+ case "open":
5355
+ icon = "\u2705";
5356
+ break;
5357
+ case "admin":
5358
+ icon = "\u{1F510}";
5359
+ break;
5360
+ case "disabled":
5361
+ icon = "\u274C";
5362
+ break;
5363
+ }
5364
+ const toolWord = count === 1 ? "tool" : "tools";
5365
+ const protectedMark = isProtected ? " \u{1F512}" : "";
5366
+ lines.push(` ${icon} **${mod}** ${count} ${toolWord} ${level}${protectedMark}`);
5367
+ }
5368
+ lines.push("");
5369
+ lines.push("Niveaux: `open` | `admin` | `disabled`");
5370
+ lines.push("Usage: `/modules set <module> <level>`");
5371
+ return lines.join("\n");
5372
+ }
5373
+ setModuleLevel(chatId, module, level, senderId) {
5374
+ if (!module || !level) {
5375
+ return "\u274C Usage: /modules set <module> <level>";
5376
+ }
5377
+ module = module.toLowerCase();
5378
+ level = level.toLowerCase();
5379
+ const available = this.registry.getAvailableModules();
5380
+ if (!available.includes(module)) {
5381
+ return `\u274C Module inconnu: "${module}"`;
5382
+ }
5383
+ if (this.permissions.isProtected(module)) {
5384
+ return `\u26D4 Module "${module}" est prot\xE9g\xE9`;
5385
+ }
5386
+ if (!VALID_MODULE_LEVELS.includes(level)) {
5387
+ return `\u274C Niveau invalide: "${level}". Valide: ${VALID_MODULE_LEVELS.join(", ")}`;
5388
+ }
5389
+ const oldLevel = this.permissions.getLevel(chatId, module);
5390
+ this.permissions.setLevel(chatId, module, level, senderId);
5391
+ const icons = { open: "\u2705", admin: "\u{1F510}", disabled: "\u274C" };
5392
+ return `${icons[level]} **${module}**: ${oldLevel} \u2192 ${level}`;
5393
+ }
5394
+ showModuleInfo(module, chatId) {
5395
+ if (!module) {
5396
+ return "\u274C Usage: /modules info <module>";
5397
+ }
5398
+ module = module.toLowerCase();
5399
+ const available = this.registry.getAvailableModules();
5400
+ if (!available.includes(module)) {
5401
+ return `\u274C Module inconnu: "${module}"`;
5402
+ }
5403
+ const tools23 = this.registry.getModuleTools(module);
5404
+ const count = tools23.length;
5405
+ const toolWord = count === 1 ? "tool" : "tools";
5406
+ const level = this.permissions.getLevel(chatId, module);
5407
+ const isProtected = this.permissions.isProtected(module);
5408
+ const protectedMark = isProtected ? " \u{1F512}" : "";
5409
+ const lines = [
5410
+ `\u{1F4E6} Module "**${module}**" \u2014 ${level}${protectedMark} (${count} ${toolWord})
5411
+ `
5412
+ ];
5413
+ for (const t of tools23) {
5414
+ lines.push(` ${t.name} ${t.scope}`);
5415
+ }
5416
+ return lines.join("\n");
5417
+ }
5418
+ resetModules(chatId, module) {
5419
+ if (module) {
5420
+ module = module.toLowerCase();
5421
+ const available = this.registry.getAvailableModules();
5422
+ if (!available.includes(module)) {
5423
+ return `\u274C Module inconnu: "${module}"`;
5424
+ }
5425
+ if (this.permissions.isProtected(module)) {
5426
+ return `\u26D4 Module "${module}" est prot\xE9g\xE9 (d\xE9j\xE0 open)`;
5427
+ }
5428
+ this.permissions.resetModule(chatId, module);
5429
+ return `\u2705 **${module}** \u2192 open`;
5430
+ }
5431
+ this.permissions.resetAll(chatId);
5432
+ return "\u2705 Tous les modules remis \xE0 **open**";
5433
+ }
5289
5434
  /**
5290
5435
  * /help - Show available commands
5291
5436
  */
@@ -5307,6 +5452,9 @@ Change access policy
5307
5452
  **/strategy** [buy|sell <percent>]
5308
5453
  View or change trading thresholds
5309
5454
 
5455
+ **/modules** [set|info|reset]
5456
+ Manage per-group module permissions
5457
+
5310
5458
  **/wallet**
5311
5459
  Check TON wallet balance
5312
5460
 
@@ -5458,6 +5606,8 @@ import { validateToolCall } from "@mariozechner/pi-ai";
5458
5606
  var ToolRegistry = class {
5459
5607
  tools = /* @__PURE__ */ new Map();
5460
5608
  scopes = /* @__PURE__ */ new Map();
5609
+ toolModules = /* @__PURE__ */ new Map();
5610
+ permissions = null;
5461
5611
  /**
5462
5612
  * Register a new tool with optional scope
5463
5613
  */
@@ -5469,6 +5619,42 @@ var ToolRegistry = class {
5469
5619
  if (scope && scope !== "always") {
5470
5620
  this.scopes.set(tool.name, scope);
5471
5621
  }
5622
+ this.toolModules.set(tool.name, tool.name.split("_")[0]);
5623
+ }
5624
+ /**
5625
+ * Set the module permissions manager
5626
+ */
5627
+ setPermissions(mp) {
5628
+ this.permissions = mp;
5629
+ }
5630
+ /**
5631
+ * Get sorted unique module names derived from registered tools
5632
+ */
5633
+ getAvailableModules() {
5634
+ const modules = new Set(this.toolModules.values());
5635
+ return Array.from(modules).sort();
5636
+ }
5637
+ /**
5638
+ * Get the number of tools in a module
5639
+ */
5640
+ getModuleToolCount(module) {
5641
+ let count = 0;
5642
+ for (const mod of this.toolModules.values()) {
5643
+ if (mod === module) count++;
5644
+ }
5645
+ return count;
5646
+ }
5647
+ /**
5648
+ * Get tools belonging to a module with their scope
5649
+ */
5650
+ getModuleTools(module) {
5651
+ const result = [];
5652
+ for (const [name, mod] of this.toolModules) {
5653
+ if (mod === module) {
5654
+ result.push({ name, scope: this.scopes.get(name) ?? "always" });
5655
+ }
5656
+ }
5657
+ return result.sort((a, b) => a.name.localeCompare(b.name));
5472
5658
  }
5473
5659
  /**
5474
5660
  * Get all registered tools for pi-ai
@@ -5500,9 +5686,43 @@ var ToolRegistry = class {
5500
5686
  error: `Tool "${toolCall.name}" is only available in group chats`
5501
5687
  };
5502
5688
  }
5689
+ if (context.isGroup && this.permissions) {
5690
+ const module = this.toolModules.get(toolCall.name);
5691
+ if (module) {
5692
+ const level = this.permissions.getLevel(context.chatId, module);
5693
+ if (level === "disabled") {
5694
+ return {
5695
+ success: false,
5696
+ error: `Module "${module}" is disabled in this group`
5697
+ };
5698
+ }
5699
+ if (level === "admin") {
5700
+ const isAdmin = context.config?.telegram.admin_ids.includes(context.senderId) ?? false;
5701
+ if (!isAdmin) {
5702
+ return {
5703
+ success: false,
5704
+ error: `Module "${module}" is restricted to admins in this group`
5705
+ };
5706
+ }
5707
+ }
5708
+ }
5709
+ }
5503
5710
  try {
5504
5711
  const validatedArgs = validateToolCall(this.getAll(), toolCall);
5505
- const result = await registered.executor(validatedArgs, context);
5712
+ let timeoutHandle;
5713
+ const result = await Promise.race([
5714
+ registered.executor(validatedArgs, context),
5715
+ new Promise((_, reject) => {
5716
+ timeoutHandle = setTimeout(
5717
+ () => reject(
5718
+ new Error(
5719
+ `Tool "${toolCall.name}" timed out after ${TOOL_EXECUTION_TIMEOUT_MS / 1e3}s`
5720
+ )
5721
+ ),
5722
+ TOOL_EXECUTION_TIMEOUT_MS
5723
+ );
5724
+ })
5725
+ ]).finally(() => clearTimeout(timeoutHandle));
5506
5726
  return result;
5507
5727
  } catch (error) {
5508
5728
  console.error(`Error executing tool ${toolCall.name}:`, error);
@@ -5526,13 +5746,25 @@ var ToolRegistry = class {
5526
5746
  return all.slice(0, toolLimit);
5527
5747
  }
5528
5748
  /**
5529
- * Get tools filtered by chat context (DM vs group) and provider limit.
5749
+ * Get tools filtered by chat context (DM vs group), module permissions, and provider limit.
5530
5750
  * - In groups: excludes "dm-only" tools (financial, private)
5531
5751
  * - In DMs: excludes "group-only" tools (moderation)
5752
+ * - In groups with permissions: excludes disabled modules, admin-only modules for non-admins
5532
5753
  */
5533
- getForContext(isGroup, toolLimit) {
5754
+ getForContext(isGroup, toolLimit, chatId, isAdmin) {
5534
5755
  const excluded = isGroup ? "dm-only" : "group-only";
5535
- const filtered = Array.from(this.tools.values()).filter((rt) => this.scopes.get(rt.tool.name) !== excluded).map((rt) => rt.tool);
5756
+ const filtered = Array.from(this.tools.values()).filter((rt) => {
5757
+ if (this.scopes.get(rt.tool.name) === excluded) return false;
5758
+ if (isGroup && chatId && this.permissions) {
5759
+ const module = this.toolModules.get(rt.tool.name);
5760
+ if (module) {
5761
+ const level = this.permissions.getLevel(chatId, module);
5762
+ if (level === "disabled") return false;
5763
+ if (level === "admin" && !isAdmin) return false;
5764
+ }
5765
+ }
5766
+ return true;
5767
+ }).map((rt) => rt.tool);
5536
5768
  if (toolLimit !== null && filtered.length > toolLimit) {
5537
5769
  console.warn(
5538
5770
  `\u26A0\uFE0F Provider tool limit: ${toolLimit}, after scope filter: ${filtered.length}. Truncating to ${toolLimit} tools.`
@@ -10526,19 +10758,6 @@ var GramJSBotClient = class {
10526
10758
  import { TonClient as TonClient2, fromNano as fromNano2 } from "@ton/ton";
10527
10759
  import { Address } from "@ton/core";
10528
10760
 
10529
- // src/ton/endpoint.ts
10530
- import { getHttpEndpoint as getHttpEndpoint2 } from "@orbs-network/ton-access";
10531
- var ENDPOINT_CACHE_TTL_MS = 6e4;
10532
- var _cache = null;
10533
- async function getCachedHttpEndpoint() {
10534
- if (_cache && Date.now() - _cache.ts < ENDPOINT_CACHE_TTL_MS) {
10535
- return _cache.url;
10536
- }
10537
- const url = await getHttpEndpoint2({ network: "mainnet" });
10538
- _cache = { url, ts: Date.now() };
10539
- return url;
10540
- }
10541
-
10542
10761
  // src/casino/retry.ts
10543
10762
  var DEFAULT_OPTIONS = {
10544
10763
  maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
@@ -10773,7 +10992,6 @@ If you already sent, wait a moment and try again.`
10773
10992
  import { mnemonicToPrivateKey as mnemonicToPrivateKey2 } from "@ton/crypto";
10774
10993
  import { WalletContractV5R1 as WalletContractV5R12, TonClient as TonClient3, toNano, internal } from "@ton/ton";
10775
10994
  import { Address as Address2, SendMode } from "@ton/core";
10776
- import { getHttpEndpoint as getHttpEndpoint3 } from "@orbs-network/ton-access";
10777
10995
  async function sendTon(params) {
10778
10996
  try {
10779
10997
  const { toAddress: toAddress2, amount, comment = "", bounce = false } = params;
@@ -10794,7 +11012,7 @@ async function sendTon(params) {
10794
11012
  workchain: 0,
10795
11013
  publicKey: keyPair.publicKey
10796
11014
  });
10797
- const endpoint = await getHttpEndpoint3({ network: "mainnet" });
11015
+ const endpoint = await getCachedHttpEndpoint();
10798
11016
  const client = new TonClient3({ endpoint });
10799
11017
  const contract = client.open(wallet);
10800
11018
  const seqno = await contract.getSeqno();
@@ -11954,6 +12172,7 @@ function openDealsDb() {
11954
12172
  CREATE INDEX IF NOT EXISTS idx_deals_chat ON deals(chat_id);
11955
12173
  CREATE INDEX IF NOT EXISTS idx_deals_inline_msg ON deals(inline_message_id) WHERE inline_message_id IS NOT NULL;
11956
12174
  CREATE INDEX IF NOT EXISTS idx_deals_payment_claimed ON deals(payment_claimed_at) WHERE payment_claimed_at IS NOT NULL;
12175
+ CREATE INDEX IF NOT EXISTS idx_deals_expires ON deals(expires_at) WHERE status IN ('proposed', 'accepted');
11957
12176
 
11958
12177
  CREATE TABLE IF NOT EXISTS user_trade_stats (
11959
12178
  telegram_id INTEGER PRIMARY KEY,
@@ -14374,7 +14593,6 @@ var telegramCreateScheduledTaskExecutor = async (params, context) => {
14374
14593
  }
14375
14594
  const { getTaskStore } = await import("./tasks-M3QDPTGY.js");
14376
14595
  const taskStore = getTaskStore(context.db);
14377
- const MAX_DEPENDENTS_PER_TASK = 10;
14378
14596
  if (dependsOn && dependsOn.length > 0) {
14379
14597
  for (const parentId of dependsOn) {
14380
14598
  const existingDependents = taskStore.getDependents(parentId);
@@ -14877,7 +15095,6 @@ import { Type as Type75 } from "@sinclair/typebox";
14877
15095
  import { mnemonicToPrivateKey as mnemonicToPrivateKey3 } from "@ton/crypto";
14878
15096
  import { WalletContractV5R1 as WalletContractV5R13, TonClient as TonClient4, toNano as toNano2, internal as internal2 } from "@ton/ton";
14879
15097
  import { Address as Address3, SendMode as SendMode2 } from "@ton/core";
14880
- import { getHttpEndpoint as getHttpEndpoint4 } from "@orbs-network/ton-access";
14881
15098
  var tonSendTool = {
14882
15099
  name: "ton_send",
14883
15100
  description: "Send TON cryptocurrency to an address. Requires wallet to be initialized. Amount is in TON (not nanoTON). Example: amount 1.5 = 1.5 TON. Always confirm the transaction details before sending.",
@@ -14919,7 +15136,7 @@ var tonSendExecutor = async (params, context) => {
14919
15136
  workchain: 0,
14920
15137
  publicKey: keyPair.publicKey
14921
15138
  });
14922
- const endpoint = await getHttpEndpoint4({ network: "mainnet" });
15139
+ const endpoint = await getCachedHttpEndpoint();
14923
15140
  const client = new TonClient4({ endpoint });
14924
15141
  const contract = client.open(wallet);
14925
15142
  const seqno = await contract.getSeqno();
@@ -14959,7 +15176,6 @@ var tonSendExecutor = async (params, context) => {
14959
15176
  import { Type as Type76 } from "@sinclair/typebox";
14960
15177
  import { TonClient as TonClient5, fromNano as fromNano3 } from "@ton/ton";
14961
15178
  import { Address as Address4 } from "@ton/core";
14962
- import { getHttpEndpoint as getHttpEndpoint5 } from "@orbs-network/ton-access";
14963
15179
  var OP_CODES = {
14964
15180
  COMMENT: 0,
14965
15181
  JETTON_TRANSFER: 260734629,
@@ -15035,7 +15251,7 @@ var tonGetTransactionsExecutor = async (params, context) => {
15035
15251
  error: `Invalid address: ${address4}`
15036
15252
  };
15037
15253
  }
15038
- const endpoint = await getHttpEndpoint5({ network: "mainnet" });
15254
+ const endpoint = await getCachedHttpEndpoint();
15039
15255
  const client = new TonClient5({ endpoint });
15040
15256
  const transactions = await client.getTransactions(addressObj, {
15041
15257
  limit: Math.min(limit, 50)
@@ -15179,7 +15395,6 @@ var tonGetTransactionsExecutor = async (params, context) => {
15179
15395
  import { Type as Type77 } from "@sinclair/typebox";
15180
15396
  import { TonClient as TonClient6, fromNano as fromNano4 } from "@ton/ton";
15181
15397
  import { Address as Address5 } from "@ton/core";
15182
- import { getHttpEndpoint as getHttpEndpoint6 } from "@orbs-network/ton-access";
15183
15398
  var OP_CODES2 = {
15184
15399
  COMMENT: 0,
15185
15400
  JETTON_TRANSFER: 260734629,
@@ -15251,7 +15466,7 @@ var tonMyTransactionsExecutor = async (params, context) => {
15251
15466
  };
15252
15467
  }
15253
15468
  const addressObj = Address5.parse(walletData.address);
15254
- const endpoint = await getHttpEndpoint6({ network: "mainnet" });
15469
+ const endpoint = await getCachedHttpEndpoint();
15255
15470
  const client = new TonClient6({ endpoint });
15256
15471
  const transactions = await client.getTransactions(addressObj, {
15257
15472
  limit: Math.min(limit, 50)
@@ -15391,6 +15606,223 @@ var tonMyTransactionsExecutor = async (params, context) => {
15391
15606
  }
15392
15607
  };
15393
15608
 
15609
+ // src/agent/tools/ton/chart.ts
15610
+ import { Type as Type78 } from "@sinclair/typebox";
15611
+ var tonChartTool = {
15612
+ name: "ton_chart",
15613
+ description: "Get price history chart for TON or any jetton. Returns price points over a time period with stats (min, max, change %). Use token param for jettons (master contract address).",
15614
+ parameters: Type78.Object({
15615
+ token: Type78.Optional(
15616
+ Type78.String({
15617
+ description: 'Token identifier: "ton" for TON, or a jetton master contract address. Defaults to "ton".'
15618
+ })
15619
+ ),
15620
+ period: Type78.Optional(
15621
+ Type78.String({
15622
+ description: 'Time period: "1h", "24h", "7d", "30d", "90d", "1y". Defaults to "7d".'
15623
+ })
15624
+ )
15625
+ }),
15626
+ category: "data-bearing"
15627
+ };
15628
+ var PERIOD_CONFIG = {
15629
+ "1h": { seconds: 3600, points: 60 },
15630
+ "24h": { seconds: 86400, points: 96 },
15631
+ "7d": { seconds: 604800, points: 168 },
15632
+ "30d": { seconds: 2592e3, points: 120 },
15633
+ "90d": { seconds: 7776e3, points: 180 },
15634
+ "1y": { seconds: 31536e3, points: 200 }
15635
+ };
15636
+ var tonChartExecutor = async (params, context) => {
15637
+ try {
15638
+ const token = params.token || "ton";
15639
+ const period = params.period || "7d";
15640
+ const config = PERIOD_CONFIG[period];
15641
+ if (!config) {
15642
+ return {
15643
+ success: false,
15644
+ error: `Invalid period "${period}". Use one of: ${Object.keys(PERIOD_CONFIG).join(", ")}`
15645
+ };
15646
+ }
15647
+ const endDate = Math.floor(Date.now() / 1e3);
15648
+ const startDate = endDate - config.seconds;
15649
+ const url = `/rates/chart?token=${encodeURIComponent(token)}&currency=usd&start_date=${startDate}&end_date=${endDate}&points_count=${config.points}`;
15650
+ const res = await tonapiFetch(url);
15651
+ if (!res.ok) {
15652
+ const text = await res.text().catch(() => "");
15653
+ return {
15654
+ success: false,
15655
+ error: `TonAPI returned ${res.status}: ${text || res.statusText}`
15656
+ };
15657
+ }
15658
+ const data = await res.json();
15659
+ if (!Array.isArray(data.points) || data.points.length === 0) {
15660
+ return {
15661
+ success: false,
15662
+ error: `No price data available for token "${token}" over period "${period}".`
15663
+ };
15664
+ }
15665
+ const rawPoints = data.points;
15666
+ const sorted = [...rawPoints].sort((a, b) => a[0] - b[0]);
15667
+ const points = sorted.map(([ts, price]) => ({
15668
+ timestamp: ts,
15669
+ date: new Date(ts * 1e3).toISOString(),
15670
+ price
15671
+ }));
15672
+ const prices = points.map((p2) => p2.price);
15673
+ const startPrice = prices[0];
15674
+ const currentPrice = prices[prices.length - 1];
15675
+ const minPrice = Math.min(...prices);
15676
+ const maxPrice = Math.max(...prices);
15677
+ const changeAbsolute = currentPrice - startPrice;
15678
+ const changePercent = startPrice !== 0 ? changeAbsolute / startPrice * 100 : 0;
15679
+ const minIdx = prices.indexOf(minPrice);
15680
+ const maxIdx = prices.indexOf(maxPrice);
15681
+ const stats = {
15682
+ currentPrice,
15683
+ startPrice,
15684
+ minPrice,
15685
+ maxPrice,
15686
+ changePercent: Math.round(changePercent * 100) / 100,
15687
+ changeAbsolute: Math.round(changeAbsolute * 1e6) / 1e6,
15688
+ high: { price: maxPrice, date: points[maxIdx].date },
15689
+ low: { price: minPrice, date: points[minIdx].date }
15690
+ };
15691
+ const direction = changePercent >= 0 ? "+" : "";
15692
+ const tokenLabel = token === "ton" ? "TON" : token;
15693
+ const message = `${tokenLabel} price over ${period}: $${currentPrice.toFixed(4)} (${direction}${stats.changePercent}%). Low: $${minPrice.toFixed(4)}, High: $${maxPrice.toFixed(4)}. ${points.length} data points.`;
15694
+ return {
15695
+ success: true,
15696
+ data: {
15697
+ token: tokenLabel,
15698
+ period,
15699
+ points,
15700
+ stats,
15701
+ message
15702
+ }
15703
+ };
15704
+ } catch (error) {
15705
+ console.error("Error in ton_chart:", error);
15706
+ return {
15707
+ success: false,
15708
+ error: error instanceof Error ? error.message : String(error)
15709
+ };
15710
+ }
15711
+ };
15712
+
15713
+ // src/agent/tools/ton/nft-list.ts
15714
+ import { Type as Type79 } from "@sinclair/typebox";
15715
+ var nftListTool = {
15716
+ name: "nft_list",
15717
+ description: "List NFTs owned by a TON wallet. Defaults to the agent's own wallet. Can filter by collection address.",
15718
+ parameters: Type79.Object({
15719
+ address: Type79.Optional(
15720
+ Type79.String({
15721
+ description: "TON wallet address to query. Defaults to the agent's wallet."
15722
+ })
15723
+ ),
15724
+ collection: Type79.Optional(
15725
+ Type79.String({
15726
+ description: "Filter by collection contract address."
15727
+ })
15728
+ ),
15729
+ limit: Type79.Optional(
15730
+ Type79.Number({
15731
+ description: "Max NFTs to return (1-100). Defaults to 50.",
15732
+ minimum: 1,
15733
+ maximum: 100
15734
+ })
15735
+ )
15736
+ }),
15737
+ category: "data-bearing"
15738
+ };
15739
+ var nftListExecutor = async (params, context) => {
15740
+ try {
15741
+ const address4 = params.address || getWalletAddress();
15742
+ if (!address4) {
15743
+ return {
15744
+ success: false,
15745
+ error: "No address provided and agent wallet is not initialized."
15746
+ };
15747
+ }
15748
+ const limit = params.limit || 50;
15749
+ const queryParts = [`limit=${limit}`, "indirect_ownership=true"];
15750
+ if (params.collection) {
15751
+ queryParts.push(`collection=${encodeURIComponent(params.collection)}`);
15752
+ }
15753
+ const url = `/accounts/${encodeURIComponent(address4)}/nfts?${queryParts.join("&")}`;
15754
+ const res = await tonapiFetch(url);
15755
+ if (!res.ok) {
15756
+ const text = await res.text().catch(() => "");
15757
+ return {
15758
+ success: false,
15759
+ error: `TonAPI returned ${res.status}: ${text || res.statusText}`
15760
+ };
15761
+ }
15762
+ const data = await res.json();
15763
+ if (!Array.isArray(data.nft_items)) {
15764
+ return {
15765
+ success: false,
15766
+ error: "Invalid API response: missing nft_items array"
15767
+ };
15768
+ }
15769
+ const rawItems = data.nft_items;
15770
+ const filtered = rawItems.filter((item) => item.trust !== "blacklist");
15771
+ const nfts = filtered.map((item) => {
15772
+ const meta = item.metadata || {};
15773
+ const coll = item.collection || {};
15774
+ const sale = item.sale;
15775
+ const previews = item.previews || [];
15776
+ const preview = previews.length > 1 && previews[1].url || previews.length > 0 && previews[0].url || null;
15777
+ let salePrice = null;
15778
+ if (sale?.price?.value) {
15779
+ const raw = Number(sale.price.value);
15780
+ if (!isNaN(raw) && raw > 0) {
15781
+ const amount = raw / 1e9;
15782
+ const currency = sale.price.token_name || "TON";
15783
+ salePrice = `${amount} ${currency}`;
15784
+ }
15785
+ }
15786
+ return {
15787
+ address: item.address,
15788
+ name: meta.name || "Unnamed NFT",
15789
+ description: (meta.description || "").slice(0, 100),
15790
+ collection: coll.name || null,
15791
+ collectionAddress: coll.address || null,
15792
+ preview,
15793
+ onSale: !!sale,
15794
+ salePrice,
15795
+ marketplace: sale?.marketplace || null,
15796
+ dns: item.dns || null,
15797
+ trust: item.trust || "none",
15798
+ explorer: `https://tonviewer.com/${item.address}`
15799
+ };
15800
+ });
15801
+ const hasMore = rawItems.length >= limit;
15802
+ const summary = `Found ${nfts.length} NFT(s) for ${address4}${params.collection ? ` in collection ${params.collection}` : ""}${hasMore ? ` (limit ${limit} reached, there may be more)` : ""}.`;
15803
+ const onSaleCount = nfts.filter((n2) => n2.onSale).length;
15804
+ const collections = [...new Set(nfts.map((n2) => n2.collection).filter(Boolean))];
15805
+ const message = `${summary}${onSaleCount > 0 ? ` ${onSaleCount} on sale.` : ""} Collections: ${collections.length > 0 ? collections.join(", ") : "none"}.`;
15806
+ return {
15807
+ success: true,
15808
+ data: {
15809
+ address: address4,
15810
+ totalNfts: nfts.length,
15811
+ hasMore,
15812
+ nfts,
15813
+ message,
15814
+ summary
15815
+ }
15816
+ };
15817
+ } catch (error) {
15818
+ console.error("Error in nft_list:", error);
15819
+ return {
15820
+ success: false,
15821
+ error: error instanceof Error ? error.message : String(error)
15822
+ };
15823
+ }
15824
+ };
15825
+
15394
15826
  // src/agent/tools/ton/index.ts
15395
15827
  var tools16 = [
15396
15828
  { tool: tonSendTool, executor: tonSendExecutor, scope: "dm-only" },
@@ -15398,16 +15830,18 @@ var tools16 = [
15398
15830
  { tool: tonGetBalanceTool, executor: tonGetBalanceExecutor },
15399
15831
  { tool: tonPriceTool, executor: tonPriceExecutor },
15400
15832
  { tool: tonGetTransactionsTool, executor: tonGetTransactionsExecutor },
15401
- { tool: tonMyTransactionsTool, executor: tonMyTransactionsExecutor }
15833
+ { tool: tonMyTransactionsTool, executor: tonMyTransactionsExecutor },
15834
+ { tool: tonChartTool, executor: tonChartExecutor },
15835
+ { tool: nftListTool, executor: nftListExecutor }
15402
15836
  ];
15403
15837
 
15404
15838
  // src/agent/tools/dns/check.ts
15405
- import { Type as Type78 } from "@sinclair/typebox";
15839
+ import { Type as Type80 } from "@sinclair/typebox";
15406
15840
  var dnsCheckTool = {
15407
15841
  name: "dns_check",
15408
15842
  description: "Check if a .ton domain is available, in auction, or already owned. Returns status with relevant details (price estimates, current bids, owner info).",
15409
- parameters: Type78.Object({
15410
- domain: Type78.String({
15843
+ parameters: Type80.Object({
15844
+ domain: Type80.String({
15411
15845
  description: "Domain name to check (with or without .ton extension)"
15412
15846
  })
15413
15847
  })
@@ -15521,13 +15955,13 @@ var dnsCheckExecutor = async (params, context) => {
15521
15955
  };
15522
15956
 
15523
15957
  // src/agent/tools/dns/auctions.ts
15524
- import { Type as Type79 } from "@sinclair/typebox";
15958
+ import { Type as Type81 } from "@sinclair/typebox";
15525
15959
  var dnsAuctionsTool = {
15526
15960
  name: "dns_auctions",
15527
15961
  description: "List all active .ton domain auctions. Returns domains currently in auction with current bid prices, number of bids, and end times.",
15528
- parameters: Type79.Object({
15529
- limit: Type79.Optional(
15530
- Type79.Number({
15962
+ parameters: Type81.Object({
15963
+ limit: Type81.Optional(
15964
+ Type81.Number({
15531
15965
  description: "Maximum number of auctions to return (default: 20, max: 100)",
15532
15966
  minimum: 1,
15533
15967
  maximum: 100
@@ -15592,12 +16026,12 @@ ${summary}`
15592
16026
  };
15593
16027
 
15594
16028
  // src/agent/tools/dns/resolve.ts
15595
- import { Type as Type80 } from "@sinclair/typebox";
16029
+ import { Type as Type82 } from "@sinclair/typebox";
15596
16030
  var dnsResolveTool = {
15597
16031
  name: "dns_resolve",
15598
16032
  description: "Resolve a .ton domain to its associated wallet address. Only works for domains that are already owned (not available or in auction).",
15599
- parameters: Type80.Object({
15600
- domain: Type80.String({
16033
+ parameters: Type82.Object({
16034
+ domain: Type82.String({
15601
16035
  description: "Domain name to resolve (with or without .ton extension)"
15602
16036
  })
15603
16037
  })
@@ -15653,20 +16087,19 @@ var dnsResolveExecutor = async (params, context) => {
15653
16087
  };
15654
16088
 
15655
16089
  // src/agent/tools/dns/start-auction.ts
15656
- import { Type as Type81 } from "@sinclair/typebox";
16090
+ import { Type as Type83 } from "@sinclair/typebox";
15657
16091
  import { mnemonicToPrivateKey as mnemonicToPrivateKey4 } from "@ton/crypto";
15658
16092
  import { WalletContractV5R1 as WalletContractV5R14, TonClient as TonClient7, toNano as toNano3, internal as internal3, beginCell } from "@ton/ton";
15659
16093
  import { Address as Address6, SendMode as SendMode3 } from "@ton/core";
15660
- import { getHttpEndpoint as getHttpEndpoint7 } from "@orbs-network/ton-access";
15661
16094
  var DNS_COLLECTION = "EQC3dNlesgVD8YbAazcauIrXBPfiVhMMr5YYk2in0Mtsz0Bz";
15662
16095
  var dnsStartAuctionTool = {
15663
16096
  name: "dns_start_auction",
15664
16097
  description: "Start an auction for an unminted .ton domain. Sends TON to the DNS collection contract to mint a new domain NFT. Domain must be 4-126 characters, available (not minted), and amount must meet minimum price.",
15665
- parameters: Type81.Object({
15666
- domain: Type81.String({
16098
+ parameters: Type83.Object({
16099
+ domain: Type83.String({
15667
16100
  description: "Domain name to mint (without .ton extension, 4-126 chars)"
15668
16101
  }),
15669
- amount: Type81.Number({
16102
+ amount: Type83.Number({
15670
16103
  description: "Bid amount in TON (must meet minimum: ~100 TON for 4 chars, ~1 TON for 11+ chars)",
15671
16104
  minimum: 1
15672
16105
  })
@@ -15700,7 +16133,7 @@ var dnsStartAuctionExecutor = async (params, context) => {
15700
16133
  workchain: 0,
15701
16134
  publicKey: keyPair.publicKey
15702
16135
  });
15703
- const endpoint = await getHttpEndpoint7({ network: "mainnet" });
16136
+ const endpoint = await getCachedHttpEndpoint();
15704
16137
  const client = new TonClient7({ endpoint });
15705
16138
  const contract = client.open(wallet);
15706
16139
  const seqno = await contract.getSeqno();
@@ -15741,19 +16174,18 @@ var dnsStartAuctionExecutor = async (params, context) => {
15741
16174
  };
15742
16175
 
15743
16176
  // src/agent/tools/dns/bid.ts
15744
- import { Type as Type82 } from "@sinclair/typebox";
16177
+ import { Type as Type84 } from "@sinclair/typebox";
15745
16178
  import { mnemonicToPrivateKey as mnemonicToPrivateKey5 } from "@ton/crypto";
15746
16179
  import { WalletContractV5R1 as WalletContractV5R15, TonClient as TonClient8, toNano as toNano4, internal as internal4 } from "@ton/ton";
15747
16180
  import { Address as Address7, SendMode as SendMode4 } from "@ton/core";
15748
- import { getHttpEndpoint as getHttpEndpoint8 } from "@orbs-network/ton-access";
15749
16181
  var dnsBidTool = {
15750
16182
  name: "dns_bid",
15751
16183
  description: "Place a bid on an existing .ton domain auction. Bid must be at least 5% higher than current bid. The domain must already be in auction (use dns_check first to verify status and get current bid).",
15752
- parameters: Type82.Object({
15753
- domain: Type82.String({
16184
+ parameters: Type84.Object({
16185
+ domain: Type84.String({
15754
16186
  description: "Domain name (with or without .ton extension)"
15755
16187
  }),
15756
- amount: Type82.Number({
16188
+ amount: Type84.Number({
15757
16189
  description: "Bid amount in TON (must be >= 105% of current bid)",
15758
16190
  minimum: 1
15759
16191
  })
@@ -15818,7 +16250,7 @@ var dnsBidExecutor = async (params, context) => {
15818
16250
  workchain: 0,
15819
16251
  publicKey: keyPair.publicKey
15820
16252
  });
15821
- const endpoint = await getHttpEndpoint8({ network: "mainnet" });
16253
+ const endpoint = await getCachedHttpEndpoint();
15822
16254
  const client = new TonClient8({ endpoint });
15823
16255
  const contract = client.open(wallet);
15824
16256
  const seqno = await contract.getSeqno();
@@ -15859,11 +16291,10 @@ var dnsBidExecutor = async (params, context) => {
15859
16291
  };
15860
16292
 
15861
16293
  // src/agent/tools/dns/link.ts
15862
- import { Type as Type83 } from "@sinclair/typebox";
16294
+ import { Type as Type85 } from "@sinclair/typebox";
15863
16295
  import { mnemonicToPrivateKey as mnemonicToPrivateKey6 } from "@ton/crypto";
15864
16296
  import { WalletContractV5R1 as WalletContractV5R16, TonClient as TonClient9, toNano as toNano5, internal as internal5, beginCell as beginCell2 } from "@ton/ton";
15865
16297
  import { Address as Address8, SendMode as SendMode5 } from "@ton/core";
15866
- import { getHttpEndpoint as getHttpEndpoint9 } from "@orbs-network/ton-access";
15867
16298
  var DNS_CHANGE_RECORD_OP = 1320284409;
15868
16299
  var DNS_SMC_ADDRESS_PREFIX = 40915;
15869
16300
  var WALLET_RECORD_KEY = BigInt(
@@ -15872,12 +16303,12 @@ var WALLET_RECORD_KEY = BigInt(
15872
16303
  var dnsLinkTool = {
15873
16304
  name: "dns_link",
15874
16305
  description: "Link a wallet address to a .ton domain you own. This sets the wallet record so the domain resolves to the specified address. If no wallet_address is provided, links to your own wallet.",
15875
- parameters: Type83.Object({
15876
- domain: Type83.String({
16306
+ parameters: Type85.Object({
16307
+ domain: Type85.String({
15877
16308
  description: "Domain name (with or without .ton extension)"
15878
16309
  }),
15879
- wallet_address: Type83.Optional(
15880
- Type83.String({
16310
+ wallet_address: Type85.Optional(
16311
+ Type85.String({
15881
16312
  description: "Wallet address to link (defaults to your wallet if not specified)"
15882
16313
  })
15883
16314
  )
@@ -15945,7 +16376,7 @@ var dnsLinkExecutor = async (params, context) => {
15945
16376
  workchain: 0,
15946
16377
  publicKey: keyPair.publicKey
15947
16378
  });
15948
- const endpoint = await getHttpEndpoint9({ network: "mainnet" });
16379
+ const endpoint = await getCachedHttpEndpoint();
15949
16380
  const client = new TonClient9({ endpoint });
15950
16381
  const contract = client.open(wallet);
15951
16382
  const seqno = await contract.getSeqno();
@@ -15987,11 +16418,10 @@ var dnsLinkExecutor = async (params, context) => {
15987
16418
  };
15988
16419
 
15989
16420
  // src/agent/tools/dns/unlink.ts
15990
- import { Type as Type84 } from "@sinclair/typebox";
16421
+ import { Type as Type86 } from "@sinclair/typebox";
15991
16422
  import { mnemonicToPrivateKey as mnemonicToPrivateKey7 } from "@ton/crypto";
15992
16423
  import { WalletContractV5R1 as WalletContractV5R17, TonClient as TonClient10, toNano as toNano6, internal as internal6, beginCell as beginCell3 } from "@ton/ton";
15993
16424
  import { Address as Address9, SendMode as SendMode6 } from "@ton/core";
15994
- import { getHttpEndpoint as getHttpEndpoint10 } from "@orbs-network/ton-access";
15995
16425
  var DNS_CHANGE_RECORD_OP2 = 1320284409;
15996
16426
  var WALLET_RECORD_KEY2 = BigInt(
15997
16427
  "0xe8d44050873dba865aa7c170ab4cce64d90839a34dcfd6cf71d14e0205443b1b"
@@ -15999,8 +16429,8 @@ var WALLET_RECORD_KEY2 = BigInt(
15999
16429
  var dnsUnlinkTool = {
16000
16430
  name: "dns_unlink",
16001
16431
  description: "Remove the wallet link from a .ton domain you own. This deletes the wallet record so the domain no longer resolves to any address.",
16002
- parameters: Type84.Object({
16003
- domain: Type84.String({
16432
+ parameters: Type86.Object({
16433
+ domain: Type86.String({
16004
16434
  description: "Domain name (with or without .ton extension)"
16005
16435
  })
16006
16436
  })
@@ -16058,7 +16488,7 @@ var dnsUnlinkExecutor = async (params, context) => {
16058
16488
  workchain: 0,
16059
16489
  publicKey: keyPair.publicKey
16060
16490
  });
16061
- const endpoint = await getHttpEndpoint10({ network: "mainnet" });
16491
+ const endpoint = await getCachedHttpEndpoint();
16062
16492
  const client = new TonClient10({ endpoint });
16063
16493
  const contract = client.open(wallet);
16064
16494
  const seqno = await contract.getSeqno();
@@ -16109,11 +16539,11 @@ var tools17 = [
16109
16539
  ];
16110
16540
 
16111
16541
  // src/agent/tools/jetton/balances.ts
16112
- import { Type as Type85 } from "@sinclair/typebox";
16542
+ import { Type as Type87 } from "@sinclair/typebox";
16113
16543
  var jettonBalancesTool = {
16114
16544
  name: "jetton_balances",
16115
16545
  description: "Get all Jetton (token) balances owned by the agent. Returns a list of all tokens with their balances, names, symbols, and verification status. Useful to check what tokens you currently hold.",
16116
- parameters: Type85.Object({}),
16546
+ parameters: Type87.Object({}),
16117
16547
  category: "data-bearing"
16118
16548
  };
16119
16549
  var jettonBalancesExecutor = async (params, context) => {
@@ -16214,11 +16644,10 @@ var jettonBalancesExecutor = async (params, context) => {
16214
16644
  };
16215
16645
 
16216
16646
  // src/agent/tools/jetton/swap.ts
16217
- import { Type as Type86 } from "@sinclair/typebox";
16647
+ import { Type as Type88 } from "@sinclair/typebox";
16218
16648
  import { mnemonicToPrivateKey as mnemonicToPrivateKey8 } from "@ton/crypto";
16219
16649
  import { WalletContractV5R1 as WalletContractV5R18, TonClient as TonClient11, toNano as toNano19, internal as internal7 } from "@ton/ton";
16220
16650
  import { SendMode as SendMode7 } from "@ton/core";
16221
- import { getHttpEndpoint as getHttpEndpoint11 } from "@orbs-network/ton-access";
16222
16651
 
16223
16652
  // node_modules/@ston-fi/sdk/dist/chunk-HNMPFVZW.js
16224
16653
  import { Address as Address10, address } from "@ton/ton";
@@ -19544,19 +19973,19 @@ var NATIVE_TON_ADDRESS = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
19544
19973
  var jettonSwapTool = {
19545
19974
  name: "jetton_swap",
19546
19975
  description: "Swap tokens on STON.fi DEX. Supports TON\u2194Jetton and Jetton\u2194Jetton swaps. Use 'ton' as from_asset to buy jettons with TON, or provide jetton master address. Amount is in human-readable units (will be converted based on decimals). Example: swap 10 TON for USDT, or swap USDT for SCALE.",
19547
- parameters: Type86.Object({
19548
- from_asset: Type86.String({
19976
+ parameters: Type88.Object({
19977
+ from_asset: Type88.String({
19549
19978
  description: "Source asset: 'ton' for TON, or jetton master address (EQ... format)"
19550
19979
  }),
19551
- to_asset: Type86.String({
19980
+ to_asset: Type88.String({
19552
19981
  description: "Destination jetton master address (EQ... format)"
19553
19982
  }),
19554
- amount: Type86.Number({
19983
+ amount: Type88.Number({
19555
19984
  description: "Amount to swap in human-readable units (e.g., 10 for 10 TON or 10 tokens)",
19556
19985
  minimum: 1e-3
19557
19986
  }),
19558
- slippage: Type86.Optional(
19559
- Type86.Number({
19987
+ slippage: Type88.Optional(
19988
+ Type88.Number({
19560
19989
  description: "Slippage tolerance (0.01 = 1%, default: 0.01)",
19561
19990
  minimum: 1e-3,
19562
19991
  maximum: 0.5
@@ -19589,7 +20018,7 @@ var jettonSwapExecutor = async (params, context) => {
19589
20018
  error: `Invalid to_asset address: ${toAddress2}`
19590
20019
  };
19591
20020
  }
19592
- const endpoint = await getHttpEndpoint11({ network: "mainnet" });
20021
+ const endpoint = await getCachedHttpEndpoint();
19593
20022
  const tonClient = new TonClient11({ endpoint });
19594
20023
  const stonApiClient = new StonApiClient();
19595
20024
  console.log(`Simulating swap: ${amount} ${fromAddress} \u2192 ${toAddress2}`);
@@ -19674,28 +20103,27 @@ var jettonSwapExecutor = async (params, context) => {
19674
20103
  };
19675
20104
 
19676
20105
  // src/agent/tools/jetton/send.ts
19677
- import { Type as Type87 } from "@sinclair/typebox";
20106
+ import { Type as Type89 } from "@sinclair/typebox";
19678
20107
  import { mnemonicToPrivateKey as mnemonicToPrivateKey9 } from "@ton/crypto";
19679
20108
  import { WalletContractV5R1 as WalletContractV5R19, TonClient as TonClient12, toNano as toNano20, internal as internal8 } from "@ton/ton";
19680
20109
  import { Address as Address13, SendMode as SendMode8, beginCell as beginCell15 } from "@ton/core";
19681
- import { getHttpEndpoint as getHttpEndpoint12 } from "@orbs-network/ton-access";
19682
20110
  var JETTON_TRANSFER_OP = 260734629;
19683
20111
  var jettonSendTool = {
19684
20112
  name: "jetton_send",
19685
20113
  description: "Send Jettons (tokens) to another address. Requires the jetton master address, recipient address, and amount. Amount is in human-readable units (e.g., 10 for 10 USDT). Use jetton_balances first to see what tokens you own and their addresses.",
19686
- parameters: Type87.Object({
19687
- jetton_address: Type87.String({
20114
+ parameters: Type89.Object({
20115
+ jetton_address: Type89.String({
19688
20116
  description: "Jetton master contract address (EQ... or 0:... format)"
19689
20117
  }),
19690
- to: Type87.String({
20118
+ to: Type89.String({
19691
20119
  description: "Recipient TON address (EQ... or UQ... format)"
19692
20120
  }),
19693
- amount: Type87.Number({
20121
+ amount: Type89.Number({
19694
20122
  description: "Amount to send in human-readable units (e.g., 10 for 10 tokens)",
19695
20123
  minimum: 0
19696
20124
  }),
19697
- comment: Type87.Optional(
19698
- Type87.String({
20125
+ comment: Type89.Optional(
20126
+ Type89.String({
19699
20127
  description: "Optional comment/memo to include with the transfer"
19700
20128
  })
19701
20129
  )
@@ -19758,7 +20186,7 @@ var jettonSendExecutor = async (params, context) => {
19758
20186
  workchain: 0,
19759
20187
  publicKey: keyPair.publicKey
19760
20188
  });
19761
- const endpoint = await getHttpEndpoint12({ network: "mainnet" });
20189
+ const endpoint = await getCachedHttpEndpoint();
19762
20190
  const client = new TonClient12({ endpoint });
19763
20191
  const walletContract = client.open(wallet);
19764
20192
  const seqno = await walletContract.getSeqno();
@@ -19799,12 +20227,12 @@ var jettonSendExecutor = async (params, context) => {
19799
20227
  };
19800
20228
 
19801
20229
  // src/agent/tools/jetton/info.ts
19802
- import { Type as Type88 } from "@sinclair/typebox";
20230
+ import { Type as Type90 } from "@sinclair/typebox";
19803
20231
  var jettonInfoTool = {
19804
20232
  name: "jetton_info",
19805
20233
  description: "Get detailed information about a Jetton (token) by its master contract address. Returns name, symbol, decimals, total supply, holders count, and verification status. Useful to research a token before buying or sending.",
19806
- parameters: Type88.Object({
19807
- jetton_address: Type88.String({
20234
+ parameters: Type90.Object({
20235
+ jetton_address: Type90.String({
19808
20236
  description: "Jetton master contract address (EQ... or 0:... format)"
19809
20237
  })
19810
20238
  })
@@ -19894,12 +20322,12 @@ function formatLargeNumber(num) {
19894
20322
  }
19895
20323
 
19896
20324
  // src/agent/tools/jetton/price.ts
19897
- import { Type as Type89 } from "@sinclair/typebox";
20325
+ import { Type as Type91 } from "@sinclair/typebox";
19898
20326
  var jettonPriceTool = {
19899
20327
  name: "jetton_price",
19900
20328
  description: "Get the current price of a Jetton (token) in USD and TON, along with 24h, 7d, and 30d price changes. Useful to check token value before swapping or to monitor investments.",
19901
- parameters: Type89.Object({
19902
- jetton_address: Type89.String({
20329
+ parameters: Type91.Object({
20330
+ jetton_address: Type91.String({
19903
20331
  description: "Jetton master contract address (EQ... or 0:... format)"
19904
20332
  })
19905
20333
  })
@@ -19992,17 +20420,17 @@ var jettonPriceExecutor = async (params, context) => {
19992
20420
  };
19993
20421
 
19994
20422
  // src/agent/tools/jetton/search.ts
19995
- import { Type as Type90 } from "@sinclair/typebox";
20423
+ import { Type as Type92 } from "@sinclair/typebox";
19996
20424
  var jettonSearchTool = {
19997
20425
  name: "jetton_search",
19998
20426
  description: "Search for Jettons (tokens) by name or symbol. Returns a list of matching tokens with their addresses, useful for finding a token's address before swapping or checking prices. Search is case-insensitive.",
19999
- parameters: Type90.Object({
20000
- query: Type90.String({
20427
+ parameters: Type92.Object({
20428
+ query: Type92.String({
20001
20429
  description: "Search query - token name or symbol (e.g., 'usdt', 'scale', 'not')",
20002
20430
  minLength: 1
20003
20431
  }),
20004
- limit: Type90.Optional(
20005
- Type90.Number({
20432
+ limit: Type92.Optional(
20433
+ Type92.Number({
20006
20434
  description: "Maximum number of results to return (default: 10, max: 50)",
20007
20435
  minimum: 1,
20008
20436
  maximum: 50
@@ -20111,25 +20539,25 @@ var jettonSearchExecutor = async (params, context) => {
20111
20539
  };
20112
20540
 
20113
20541
  // src/agent/tools/jetton/quote.ts
20114
- import { Type as Type91 } from "@sinclair/typebox";
20542
+ import { Type as Type93 } from "@sinclair/typebox";
20115
20543
  import { toNano as toNano21 } from "@ton/ton";
20116
20544
  var NATIVE_TON_ADDRESS2 = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
20117
20545
  var jettonQuoteTool = {
20118
20546
  name: "jetton_quote",
20119
20547
  description: "Get a price quote for a token swap WITHOUT executing it. Shows expected output, minimum output, price impact, and fees. Use this to preview a swap before committing. Use 'ton' as from_asset for TON, or jetton master address.",
20120
- parameters: Type91.Object({
20121
- from_asset: Type91.String({
20548
+ parameters: Type93.Object({
20549
+ from_asset: Type93.String({
20122
20550
  description: "Source asset: 'ton' for TON, or jetton master address (EQ... format)"
20123
20551
  }),
20124
- to_asset: Type91.String({
20552
+ to_asset: Type93.String({
20125
20553
  description: "Destination jetton master address (EQ... format)"
20126
20554
  }),
20127
- amount: Type91.Number({
20555
+ amount: Type93.Number({
20128
20556
  description: "Amount to swap in human-readable units",
20129
20557
  minimum: 1e-3
20130
20558
  }),
20131
- slippage: Type91.Optional(
20132
- Type91.Number({
20559
+ slippage: Type93.Optional(
20560
+ Type93.Number({
20133
20561
  description: "Slippage tolerance (0.01 = 1%, default: 0.01)",
20134
20562
  minimum: 1e-3,
20135
20563
  maximum: 0.5
@@ -20215,16 +20643,16 @@ var jettonQuoteExecutor = async (params, context) => {
20215
20643
  };
20216
20644
 
20217
20645
  // src/agent/tools/jetton/holders.ts
20218
- import { Type as Type92 } from "@sinclair/typebox";
20646
+ import { Type as Type94 } from "@sinclair/typebox";
20219
20647
  var jettonHoldersTool = {
20220
20648
  name: "jetton_holders",
20221
20649
  description: "Get the top holders of a Jetton (token). Shows wallet addresses and their balances. Useful to analyze token distribution and identify whale wallets.",
20222
- parameters: Type92.Object({
20223
- jetton_address: Type92.String({
20650
+ parameters: Type94.Object({
20651
+ jetton_address: Type94.String({
20224
20652
  description: "Jetton master contract address (EQ... or 0:... format)"
20225
20653
  }),
20226
- limit: Type92.Optional(
20227
- Type92.Number({
20654
+ limit: Type94.Optional(
20655
+ Type94.Number({
20228
20656
  description: "Number of top holders to return (default: 10, max: 100)",
20229
20657
  minimum: 1,
20230
20658
  maximum: 100
@@ -20309,12 +20737,12 @@ var jettonHoldersExecutor = async (params, context) => {
20309
20737
  };
20310
20738
 
20311
20739
  // src/agent/tools/jetton/history.ts
20312
- import { Type as Type93 } from "@sinclair/typebox";
20740
+ import { Type as Type95 } from "@sinclair/typebox";
20313
20741
  var jettonHistoryTool = {
20314
20742
  name: "jetton_history",
20315
20743
  description: "Get price history and performance data for a Jetton. Shows price changes over 24h, 7d, 30d periods, along with volume and market data. Useful for analyzing token trends.",
20316
- parameters: Type93.Object({
20317
- jetton_address: Type93.String({
20744
+ parameters: Type95.Object({
20745
+ jetton_address: Type95.String({
20318
20746
  description: "Jetton master contract address (EQ... format)"
20319
20747
  })
20320
20748
  })
@@ -20434,13 +20862,13 @@ var jettonHistoryExecutor = async (params, context) => {
20434
20862
  };
20435
20863
 
20436
20864
  // src/agent/tools/jetton/trending.ts
20437
- import { Type as Type94 } from "@sinclair/typebox";
20865
+ import { Type as Type96 } from "@sinclair/typebox";
20438
20866
  var jettonTrendingTool = {
20439
20867
  name: "jetton_trending",
20440
20868
  description: "Get trending/popular Jettons on the TON blockchain. Shows tokens ranked by trading volume and liquidity. Useful for discovering popular tokens.",
20441
- parameters: Type94.Object({
20442
- limit: Type94.Optional(
20443
- Type94.Number({
20869
+ parameters: Type96.Object({
20870
+ limit: Type96.Optional(
20871
+ Type96.Number({
20444
20872
  description: "Number of trending tokens to return (default: 10, max: 50)",
20445
20873
  minimum: 1,
20446
20874
  maximum: 50
@@ -20507,18 +20935,18 @@ var jettonTrendingExecutor = async (params, context) => {
20507
20935
  };
20508
20936
 
20509
20937
  // src/agent/tools/jetton/pools.ts
20510
- import { Type as Type95 } from "@sinclair/typebox";
20938
+ import { Type as Type97 } from "@sinclair/typebox";
20511
20939
  var jettonPoolsTool = {
20512
20940
  name: "jetton_pools",
20513
20941
  description: "Get liquidity pools for a Jetton or list top pools by volume. Shows pool addresses, liquidity, volume, APY, and trading pairs. Useful for finding where to trade a token or analyzing DeFi opportunities.",
20514
- parameters: Type95.Object({
20515
- jetton_address: Type95.Optional(
20516
- Type95.String({
20942
+ parameters: Type97.Object({
20943
+ jetton_address: Type97.Optional(
20944
+ Type97.String({
20517
20945
  description: "Jetton address to filter pools (optional - if not provided, returns top pools)"
20518
20946
  })
20519
20947
  ),
20520
- limit: Type95.Optional(
20521
- Type95.Number({
20948
+ limit: Type97.Optional(
20949
+ Type97.Number({
20522
20950
  description: "Number of pools to return (default: 10, max: 50)",
20523
20951
  minimum: 1,
20524
20952
  maximum: 50
@@ -20620,7 +21048,7 @@ var jettonPoolsExecutor = async (params, context) => {
20620
21048
 
20621
21049
  // src/agent/tools/jetton/index.ts
20622
21050
  var tools18 = [
20623
- { tool: jettonSwapTool, executor: jettonSwapExecutor, scope: "dm-only" },
21051
+ { tool: jettonSwapTool, executor: jettonSwapExecutor },
20624
21052
  { tool: jettonSendTool, executor: jettonSendExecutor, scope: "dm-only" },
20625
21053
  { tool: jettonBalancesTool, executor: jettonBalancesExecutor },
20626
21054
  { tool: jettonInfoTool, executor: jettonInfoExecutor },
@@ -20634,10 +21062,9 @@ var tools18 = [
20634
21062
  ];
20635
21063
 
20636
21064
  // src/agent/tools/dedust/quote.ts
20637
- import { Type as Type96 } from "@sinclair/typebox";
21065
+ import { Type as Type98 } from "@sinclair/typebox";
20638
21066
  import { TonClient as TonClient13, toNano as toNano22, fromNano as fromNano5 } from "@ton/ton";
20639
21067
  import { Address as Address14 } from "@ton/core";
20640
- import { getHttpEndpoint as getHttpEndpoint13 } from "@orbs-network/ton-access";
20641
21068
  import { Factory, Asset, PoolType, ReadinessStatus } from "@dedust/sdk";
20642
21069
 
20643
21070
  // src/agent/tools/dedust/constants.ts
@@ -20659,24 +21086,24 @@ var NATIVE_TON_ADDRESS3 = "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c";
20659
21086
  var dedustQuoteTool = {
20660
21087
  name: "dedust_quote",
20661
21088
  description: "Get a price quote for a token swap on DeDust DEX WITHOUT executing it. Shows expected output, minimum output, and pool info. Use 'ton' as from_asset for TON, or jetton master address. Pool types: 'volatile' (default) or 'stable' (for stablecoins).",
20662
- parameters: Type96.Object({
20663
- from_asset: Type96.String({
21089
+ parameters: Type98.Object({
21090
+ from_asset: Type98.String({
20664
21091
  description: "Source asset: 'ton' for TON, or jetton master address (EQ... format)"
20665
21092
  }),
20666
- to_asset: Type96.String({
21093
+ to_asset: Type98.String({
20667
21094
  description: "Destination asset: 'ton' for TON, or jetton master address (EQ... format)"
20668
21095
  }),
20669
- amount: Type96.Number({
21096
+ amount: Type98.Number({
20670
21097
  description: "Amount to swap in human-readable units",
20671
21098
  minimum: 1e-3
20672
21099
  }),
20673
- pool_type: Type96.Optional(
20674
- Type96.Union([Type96.Literal("volatile"), Type96.Literal("stable")], {
21100
+ pool_type: Type98.Optional(
21101
+ Type98.Union([Type98.Literal("volatile"), Type98.Literal("stable")], {
20675
21102
  description: "Pool type: 'volatile' (default) or 'stable' for stablecoin pairs"
20676
21103
  })
20677
21104
  ),
20678
- slippage: Type96.Optional(
20679
- Type96.Number({
21105
+ slippage: Type98.Optional(
21106
+ Type98.Number({
20680
21107
  description: "Slippage tolerance (0.01 = 1%, default: 0.01)",
20681
21108
  minimum: 1e-3,
20682
21109
  maximum: 0.5
@@ -20711,7 +21138,7 @@ var dedustQuoteExecutor = async (params, context) => {
20711
21138
  };
20712
21139
  }
20713
21140
  }
20714
- const endpoint = await getHttpEndpoint13({ network: "mainnet" });
21141
+ const endpoint = await getCachedHttpEndpoint();
20715
21142
  const tonClient = new TonClient13({ endpoint });
20716
21143
  const factory = tonClient.open(
20717
21144
  Factory.createFromAddress(Address14.parse(DEDUST_FACTORY_MAINNET))
@@ -20791,33 +21218,32 @@ var dedustQuoteExecutor = async (params, context) => {
20791
21218
  };
20792
21219
 
20793
21220
  // src/agent/tools/dedust/swap.ts
20794
- import { Type as Type97 } from "@sinclair/typebox";
21221
+ import { Type as Type99 } from "@sinclair/typebox";
20795
21222
  import { mnemonicToPrivateKey as mnemonicToPrivateKey10 } from "@ton/crypto";
20796
21223
  import { WalletContractV5R1 as WalletContractV5R110, TonClient as TonClient14, toNano as toNano23, fromNano as fromNano6 } from "@ton/ton";
20797
21224
  import { Address as Address15 } from "@ton/core";
20798
- import { getHttpEndpoint as getHttpEndpoint14 } from "@orbs-network/ton-access";
20799
21225
  import { Factory as Factory2, Asset as Asset2, PoolType as PoolType2, ReadinessStatus as ReadinessStatus2, JettonRoot, VaultJetton } from "@dedust/sdk";
20800
21226
  var dedustSwapTool = {
20801
21227
  name: "dedust_swap",
20802
21228
  description: "Execute a token swap on DeDust DEX. Supports TON->Jetton and Jetton->TON/Jetton swaps. Use 'ton' as from_asset or to_asset for TON. Pool types: 'volatile' (default) or 'stable' (for stablecoins like USDT/USDC). Use dedust_quote first to preview the swap.",
20803
- parameters: Type97.Object({
20804
- from_asset: Type97.String({
21229
+ parameters: Type99.Object({
21230
+ from_asset: Type99.String({
20805
21231
  description: "Source asset: 'ton' for TON, or jetton master address (EQ... format)"
20806
21232
  }),
20807
- to_asset: Type97.String({
21233
+ to_asset: Type99.String({
20808
21234
  description: "Destination asset: 'ton' for TON, or jetton master address (EQ... format)"
20809
21235
  }),
20810
- amount: Type97.Number({
21236
+ amount: Type99.Number({
20811
21237
  description: "Amount to swap in human-readable units (e.g., 10 for 10 TON or 10 tokens)",
20812
21238
  minimum: 1e-3
20813
21239
  }),
20814
- pool_type: Type97.Optional(
20815
- Type97.Union([Type97.Literal("volatile"), Type97.Literal("stable")], {
21240
+ pool_type: Type99.Optional(
21241
+ Type99.Union([Type99.Literal("volatile"), Type99.Literal("stable")], {
20816
21242
  description: "Pool type: 'volatile' (default) or 'stable' for stablecoin pairs"
20817
21243
  })
20818
21244
  ),
20819
- slippage: Type97.Optional(
20820
- Type97.Number({
21245
+ slippage: Type99.Optional(
21246
+ Type99.Number({
20821
21247
  description: "Slippage tolerance (0.01 = 1%, default: 0.01)",
20822
21248
  minimum: 1e-3,
20823
21249
  maximum: 0.5
@@ -20859,7 +21285,7 @@ var dedustSwapExecutor = async (params, context) => {
20859
21285
  };
20860
21286
  }
20861
21287
  }
20862
- const endpoint = await getHttpEndpoint14({ network: "mainnet" });
21288
+ const endpoint = await getCachedHttpEndpoint();
20863
21289
  const tonClient = new TonClient14({ endpoint });
20864
21290
  const factory = tonClient.open(
20865
21291
  Factory2.createFromAddress(Address15.parse(DEDUST_FACTORY_MAINNET))
@@ -20963,23 +21389,23 @@ var dedustSwapExecutor = async (params, context) => {
20963
21389
  };
20964
21390
 
20965
21391
  // src/agent/tools/dedust/pools.ts
20966
- import { Type as Type98 } from "@sinclair/typebox";
21392
+ import { Type as Type100 } from "@sinclair/typebox";
20967
21393
  var dedustPoolsTool = {
20968
21394
  name: "dedust_pools",
20969
21395
  description: "List liquidity pools on DeDust DEX. Can filter by jetton address or pool type. Shows reserves, fees, and trading volume.",
20970
- parameters: Type98.Object({
20971
- jetton_address: Type98.Optional(
20972
- Type98.String({
21396
+ parameters: Type100.Object({
21397
+ jetton_address: Type100.Optional(
21398
+ Type100.String({
20973
21399
  description: "Filter by jetton master address (EQ... format) to find pools containing this token"
20974
21400
  })
20975
21401
  ),
20976
- pool_type: Type98.Optional(
20977
- Type98.Union([Type98.Literal("volatile"), Type98.Literal("stable")], {
21402
+ pool_type: Type100.Optional(
21403
+ Type100.Union([Type100.Literal("volatile"), Type100.Literal("stable")], {
20978
21404
  description: "Filter by pool type: 'volatile' or 'stable'"
20979
21405
  })
20980
21406
  ),
20981
- limit: Type98.Optional(
20982
- Type98.Number({
21407
+ limit: Type100.Optional(
21408
+ Type100.Number({
20983
21409
  description: "Maximum number of pools to return (default: 20)",
20984
21410
  minimum: 1,
20985
21411
  maximum: 100
@@ -21089,33 +21515,32 @@ var dedustPoolsExecutor = async (params, context) => {
21089
21515
 
21090
21516
  // src/agent/tools/dedust/index.ts
21091
21517
  var tools19 = [
21092
- { tool: dedustSwapTool, executor: dedustSwapExecutor, scope: "dm-only" },
21518
+ { tool: dedustSwapTool, executor: dedustSwapExecutor },
21093
21519
  { tool: dedustQuoteTool, executor: dedustQuoteExecutor },
21094
21520
  { tool: dedustPoolsTool, executor: dedustPoolsExecutor }
21095
21521
  ];
21096
21522
 
21097
21523
  // src/agent/tools/dex/smart-quote.ts
21098
- import { Type as Type99 } from "@sinclair/typebox";
21524
+ import { Type as Type101 } from "@sinclair/typebox";
21099
21525
  import { TonClient as TonClient15, toNano as toNano24, fromNano as fromNano7 } from "@ton/ton";
21100
21526
  import { Address as Address16 } from "@ton/core";
21101
- import { getHttpEndpoint as getHttpEndpoint15 } from "@orbs-network/ton-access";
21102
21527
  import { Factory as Factory3, Asset as Asset3, PoolType as PoolType3, ReadinessStatus as ReadinessStatus3 } from "@dedust/sdk";
21103
21528
  var dexQuoteTool = {
21104
21529
  name: "dex_quote",
21105
21530
  description: "Smart router that compares quotes from STON.fi and DeDust DEX to find the best price. Returns comparison table with expected outputs, fees, and recommends the best DEX for your swap. Use 'ton' for TON or jetton master address.",
21106
- parameters: Type99.Object({
21107
- from_asset: Type99.String({
21531
+ parameters: Type101.Object({
21532
+ from_asset: Type101.String({
21108
21533
  description: "Source asset: 'ton' for TON, or jetton master address (EQ... format)"
21109
21534
  }),
21110
- to_asset: Type99.String({
21535
+ to_asset: Type101.String({
21111
21536
  description: "Destination asset: 'ton' for TON, or jetton master address (EQ... format)"
21112
21537
  }),
21113
- amount: Type99.Number({
21538
+ amount: Type101.Number({
21114
21539
  description: "Amount to swap in human-readable units",
21115
21540
  minimum: 1e-3
21116
21541
  }),
21117
- slippage: Type99.Optional(
21118
- Type99.Number({
21542
+ slippage: Type101.Optional(
21543
+ Type101.Number({
21119
21544
  description: "Slippage tolerance (0.01 = 1%, default: 0.01)",
21120
21545
  minimum: 1e-3,
21121
21546
  maximum: 0.5
@@ -21245,7 +21670,7 @@ async function getDedustQuote(fromAsset, toAsset, amount, slippage, tonClient) {
21245
21670
  var dexQuoteExecutor = async (params, context) => {
21246
21671
  try {
21247
21672
  const { from_asset, to_asset, amount, slippage = 0.01 } = params;
21248
- const endpoint = await getHttpEndpoint15({ network: "mainnet" });
21673
+ const endpoint = await getCachedHttpEndpoint();
21249
21674
  const tonClient = new TonClient15({ endpoint });
21250
21675
  const [stonfiQuote, dedustQuote] = await Promise.all([
21251
21676
  getStonfiQuote(from_asset, to_asset, amount, slippage),
@@ -21353,35 +21778,34 @@ Use dex_swap to execute on the best DEX, or jetton_swap/dedust_swap for specific
21353
21778
  };
21354
21779
 
21355
21780
  // src/agent/tools/dex/smart-swap.ts
21356
- import { Type as Type100 } from "@sinclair/typebox";
21781
+ import { Type as Type102 } from "@sinclair/typebox";
21357
21782
  import { mnemonicToPrivateKey as mnemonicToPrivateKey11 } from "@ton/crypto";
21358
21783
  import { WalletContractV5R1 as WalletContractV5R111, TonClient as TonClient16, toNano as toNano25, fromNano as fromNano8 } from "@ton/ton";
21359
21784
  import { Address as Address17, SendMode as SendMode9, internal as internal9 } from "@ton/core";
21360
- import { getHttpEndpoint as getHttpEndpoint16 } from "@orbs-network/ton-access";
21361
21785
  import { Factory as Factory4, Asset as Asset4, PoolType as PoolType4, ReadinessStatus as ReadinessStatus4, JettonRoot as JettonRoot2, VaultJetton as VaultJetton2 } from "@dedust/sdk";
21362
21786
  var dexSwapTool = {
21363
21787
  name: "dex_swap",
21364
21788
  description: "Smart router that executes swap on the best DEX (STON.fi or DeDust). Automatically compares prices and routes to the DEX with better output. Use preferred_dex to force a specific DEX. Use 'ton' for TON or jetton master address.",
21365
- parameters: Type100.Object({
21366
- from_asset: Type100.String({
21789
+ parameters: Type102.Object({
21790
+ from_asset: Type102.String({
21367
21791
  description: "Source asset: 'ton' for TON, or jetton master address (EQ... format)"
21368
21792
  }),
21369
- to_asset: Type100.String({
21793
+ to_asset: Type102.String({
21370
21794
  description: "Destination asset: 'ton' for TON, or jetton master address (EQ... format)"
21371
21795
  }),
21372
- amount: Type100.Number({
21796
+ amount: Type102.Number({
21373
21797
  description: "Amount to swap in human-readable units",
21374
21798
  minimum: 1e-3
21375
21799
  }),
21376
- slippage: Type100.Optional(
21377
- Type100.Number({
21800
+ slippage: Type102.Optional(
21801
+ Type102.Number({
21378
21802
  description: "Slippage tolerance (0.01 = 1%, default: 0.01)",
21379
21803
  minimum: 1e-3,
21380
21804
  maximum: 0.5
21381
21805
  })
21382
21806
  ),
21383
- preferred_dex: Type100.Optional(
21384
- Type100.Union([Type100.Literal("stonfi"), Type100.Literal("dedust"), Type100.Literal("auto")], {
21807
+ preferred_dex: Type102.Optional(
21808
+ Type102.Union([Type102.Literal("stonfi"), Type102.Literal("dedust"), Type102.Literal("auto")], {
21385
21809
  description: "Preferred DEX: 'auto' (default, best price), 'stonfi', or 'dedust'"
21386
21810
  })
21387
21811
  )
@@ -21579,7 +22003,7 @@ var dexSwapExecutor = async (params, context) => {
21579
22003
  if (!isTonOutput && !to_asset.match(/^[EUe][Qq][A-Za-z0-9_-]{46}$/)) {
21580
22004
  return { success: false, error: `Invalid to_asset address: ${to_asset}` };
21581
22005
  }
21582
- const endpoint = await getHttpEndpoint16({ network: "mainnet" });
22006
+ const endpoint = await getCachedHttpEndpoint();
21583
22007
  const tonClient = new TonClient16({ endpoint });
21584
22008
  let stonfiQuote = null;
21585
22009
  let dedustQuote = null;
@@ -21693,12 +22117,12 @@ var dexSwapExecutor = async (params, context) => {
21693
22117
 
21694
22118
  // src/agent/tools/dex/index.ts
21695
22119
  var tools20 = [
21696
- { tool: dexSwapTool, executor: dexSwapExecutor, scope: "dm-only" },
22120
+ { tool: dexSwapTool, executor: dexSwapExecutor },
21697
22121
  { tool: dexQuoteTool, executor: dexQuoteExecutor }
21698
22122
  ];
21699
22123
 
21700
22124
  // src/agent/tools/journal/log.ts
21701
- import { Type as Type101 } from "@sinclair/typebox";
22125
+ import { Type as Type103 } from "@sinclair/typebox";
21702
22126
  var journalLogTool = {
21703
22127
  name: "journal_log",
21704
22128
  description: `Log a business operation to the trading journal.
@@ -21716,44 +22140,44 @@ Examples:
21716
22140
  - gift: "Sold Deluxe Heart at 120% floor - buyer was eager"
21717
22141
  - middleman: "Escrow for 150 TON gift trade - 3% fee"
21718
22142
  - kol: "Posted project review in channel - 75 TON fee"`,
21719
- parameters: Type101.Object({
21720
- type: Type101.Union(
21721
- [Type101.Literal("trade"), Type101.Literal("gift"), Type101.Literal("middleman"), Type101.Literal("kol")],
22143
+ parameters: Type103.Object({
22144
+ type: Type103.Union(
22145
+ [Type103.Literal("trade"), Type103.Literal("gift"), Type103.Literal("middleman"), Type103.Literal("kol")],
21722
22146
  { description: "Type of operation" }
21723
22147
  ),
21724
- action: Type101.String({
22148
+ action: Type103.String({
21725
22149
  description: "Brief action description (e.g., 'buy', 'sell', 'swap', 'escrow', 'post')"
21726
22150
  }),
21727
- asset_from: Type101.Optional(
21728
- Type101.String({ description: "Asset sent/sold (e.g., 'TON', 'USDT', 'Deluxe Heart')" })
22151
+ asset_from: Type103.Optional(
22152
+ Type103.String({ description: "Asset sent/sold (e.g., 'TON', 'USDT', 'Deluxe Heart')" })
21729
22153
  ),
21730
- asset_to: Type101.Optional(Type101.String({ description: "Asset received/bought" })),
21731
- amount_from: Type101.Optional(Type101.Number({ description: "Amount sent/sold" })),
21732
- amount_to: Type101.Optional(Type101.Number({ description: "Amount received/bought" })),
21733
- price_ton: Type101.Optional(Type101.Number({ description: "Price in TON (for gifts, services)" })),
21734
- counterparty: Type101.Optional(
21735
- Type101.String({ description: "Username or ID of the other party (if applicable)" })
22154
+ asset_to: Type103.Optional(Type103.String({ description: "Asset received/bought" })),
22155
+ amount_from: Type103.Optional(Type103.Number({ description: "Amount sent/sold" })),
22156
+ amount_to: Type103.Optional(Type103.Number({ description: "Amount received/bought" })),
22157
+ price_ton: Type103.Optional(Type103.Number({ description: "Price in TON (for gifts, services)" })),
22158
+ counterparty: Type103.Optional(
22159
+ Type103.String({ description: "Username or ID of the other party (if applicable)" })
21736
22160
  ),
21737
- platform: Type101.Optional(
21738
- Type101.String({ description: "Platform used (e.g., 'STON.fi', 'Telegram', 'DeDust')" })
22161
+ platform: Type103.Optional(
22162
+ Type103.String({ description: "Platform used (e.g., 'STON.fi', 'Telegram', 'DeDust')" })
21739
22163
  ),
21740
- reasoning: Type101.String({
22164
+ reasoning: Type103.String({
21741
22165
  description: "WHY you took this action - explain your decision-making (this is CRITICAL for learning and auditing)"
21742
22166
  }),
21743
- outcome: Type101.Optional(
21744
- Type101.Union(
22167
+ outcome: Type103.Optional(
22168
+ Type103.Union(
21745
22169
  [
21746
- Type101.Literal("pending"),
21747
- Type101.Literal("profit"),
21748
- Type101.Literal("loss"),
21749
- Type101.Literal("neutral"),
21750
- Type101.Literal("cancelled")
22170
+ Type103.Literal("pending"),
22171
+ Type103.Literal("profit"),
22172
+ Type103.Literal("loss"),
22173
+ Type103.Literal("neutral"),
22174
+ Type103.Literal("cancelled")
21751
22175
  ],
21752
22176
  { description: "Outcome status (default: 'pending')" }
21753
22177
  )
21754
22178
  ),
21755
- tx_hash: Type101.Optional(
21756
- Type101.String({ description: "Blockchain transaction hash (if applicable)" })
22179
+ tx_hash: Type103.Optional(
22180
+ Type103.String({ description: "Blockchain transaction hash (if applicable)" })
21757
22181
  )
21758
22182
  })
21759
22183
  };
@@ -21813,7 +22237,7 @@ var journalLogExecutor = async (params, context) => {
21813
22237
  };
21814
22238
 
21815
22239
  // src/agent/tools/journal/query.ts
21816
- import { Type as Type102 } from "@sinclair/typebox";
22240
+ import { Type as Type104 } from "@sinclair/typebox";
21817
22241
  var journalQueryTool = {
21818
22242
  name: "journal_query",
21819
22243
  description: `Query the trading journal to analyze past operations.
@@ -21829,38 +22253,38 @@ Examples:
21829
22253
  - "What gifts did I sell this week?"
21830
22254
  - "Show all profitable TON trades"
21831
22255
  - "What's my win rate on crypto trades?"`,
21832
- parameters: Type102.Object({
21833
- type: Type102.Optional(
21834
- Type102.Union(
22256
+ parameters: Type104.Object({
22257
+ type: Type104.Optional(
22258
+ Type104.Union(
21835
22259
  [
21836
- Type102.Literal("trade"),
21837
- Type102.Literal("gift"),
21838
- Type102.Literal("middleman"),
21839
- Type102.Literal("kol")
22260
+ Type104.Literal("trade"),
22261
+ Type104.Literal("gift"),
22262
+ Type104.Literal("middleman"),
22263
+ Type104.Literal("kol")
21840
22264
  ],
21841
22265
  { description: "Filter by operation type" }
21842
22266
  )
21843
22267
  ),
21844
- asset: Type102.Optional(
21845
- Type102.String({ description: "Filter by asset (e.g., 'TON', 'USDT', 'Deluxe Heart')" })
22268
+ asset: Type104.Optional(
22269
+ Type104.String({ description: "Filter by asset (e.g., 'TON', 'USDT', 'Deluxe Heart')" })
21846
22270
  ),
21847
- outcome: Type102.Optional(
21848
- Type102.Union(
22271
+ outcome: Type104.Optional(
22272
+ Type104.Union(
21849
22273
  [
21850
- Type102.Literal("pending"),
21851
- Type102.Literal("profit"),
21852
- Type102.Literal("loss"),
21853
- Type102.Literal("neutral"),
21854
- Type102.Literal("cancelled")
22274
+ Type104.Literal("pending"),
22275
+ Type104.Literal("profit"),
22276
+ Type104.Literal("loss"),
22277
+ Type104.Literal("neutral"),
22278
+ Type104.Literal("cancelled")
21855
22279
  ],
21856
22280
  { description: "Filter by outcome status" }
21857
22281
  )
21858
22282
  ),
21859
- days: Type102.Optional(
21860
- Type102.Number({ description: "Limit to last N days (e.g., 7 for last week)", minimum: 1 })
22283
+ days: Type104.Optional(
22284
+ Type104.Number({ description: "Limit to last N days (e.g., 7 for last week)", minimum: 1 })
21861
22285
  ),
21862
- limit: Type102.Optional(
21863
- Type102.Number({ description: "Max number of results (default: 20)", minimum: 1 })
22286
+ limit: Type104.Optional(
22287
+ Type104.Number({ description: "Max number of results (default: 20)", minimum: 1 })
21864
22288
  )
21865
22289
  })
21866
22290
  };
@@ -21948,7 +22372,7 @@ var journalQueryExecutor = async (params) => {
21948
22372
  };
21949
22373
 
21950
22374
  // src/agent/tools/journal/update.ts
21951
- import { Type as Type103 } from "@sinclair/typebox";
22375
+ import { Type as Type105 } from "@sinclair/typebox";
21952
22376
  var journalUpdateTool = {
21953
22377
  name: "journal_update",
21954
22378
  description: `Update a journal entry with outcome and P&L.
@@ -21967,25 +22391,25 @@ Examples:
21967
22391
  - "Close trade #42 - sold at profit"
21968
22392
  - "Mark gift sale #38 as complete"
21969
22393
  - "Update escrow #55 with tx hash"`,
21970
- parameters: Type103.Object({
21971
- id: Type103.Number({ description: "Journal entry ID to update" }),
21972
- outcome: Type103.Optional(
21973
- Type103.Union(
22394
+ parameters: Type105.Object({
22395
+ id: Type105.Number({ description: "Journal entry ID to update" }),
22396
+ outcome: Type105.Optional(
22397
+ Type105.Union(
21974
22398
  [
21975
- Type103.Literal("pending"),
21976
- Type103.Literal("profit"),
21977
- Type103.Literal("loss"),
21978
- Type103.Literal("neutral"),
21979
- Type103.Literal("cancelled")
22399
+ Type105.Literal("pending"),
22400
+ Type105.Literal("profit"),
22401
+ Type105.Literal("loss"),
22402
+ Type105.Literal("neutral"),
22403
+ Type105.Literal("cancelled")
21980
22404
  ],
21981
22405
  { description: "Update outcome status" }
21982
22406
  )
21983
22407
  ),
21984
- pnl_ton: Type103.Optional(
21985
- Type103.Number({ description: "Profit/loss in TON (positive = profit, negative = loss)" })
22408
+ pnl_ton: Type105.Optional(
22409
+ Type105.Number({ description: "Profit/loss in TON (positive = profit, negative = loss)" })
21986
22410
  ),
21987
- pnl_pct: Type103.Optional(Type103.Number({ description: "Profit/loss percentage" })),
21988
- tx_hash: Type103.Optional(Type103.String({ description: "Add or update transaction hash" }))
22411
+ pnl_pct: Type105.Optional(Type105.Number({ description: "Profit/loss percentage" })),
22412
+ tx_hash: Type105.Optional(Type105.String({ description: "Add or update transaction hash" }))
21989
22413
  })
21990
22414
  };
21991
22415
  var journalUpdateExecutor = async (params) => {
@@ -22054,7 +22478,7 @@ var tools21 = [
22054
22478
  ];
22055
22479
 
22056
22480
  // src/agent/tools/workspace/list.ts
22057
- import { Type as Type104 } from "@sinclair/typebox";
22481
+ import { Type as Type106 } from "@sinclair/typebox";
22058
22482
  import { readdirSync as readdirSync3, lstatSync as lstatSync2 } from "fs";
22059
22483
  import { join as join11 } from "path";
22060
22484
  var workspaceListTool = {
@@ -22069,19 +22493,19 @@ Your workspace is at ~/.teleton/workspace/ and contains:
22069
22493
  - temp/ (temporary files)
22070
22494
 
22071
22495
  You can ONLY access files within this workspace. Files outside (config.yaml, wallet.json, etc.) are protected.`,
22072
- parameters: Type104.Object({
22073
- path: Type104.Optional(
22074
- Type104.String({
22496
+ parameters: Type106.Object({
22497
+ path: Type106.Optional(
22498
+ Type106.String({
22075
22499
  description: "Subdirectory to list (relative to workspace root). Leave empty for root."
22076
22500
  })
22077
22501
  ),
22078
- recursive: Type104.Optional(
22079
- Type104.Boolean({
22502
+ recursive: Type106.Optional(
22503
+ Type106.Boolean({
22080
22504
  description: "List files recursively (default: false)"
22081
22505
  })
22082
22506
  ),
22083
- filter: Type104.Optional(
22084
- Type104.String({
22507
+ filter: Type106.Optional(
22508
+ Type106.String({
22085
22509
  description: "Filter by type: 'all' (default), 'files', or 'directories'",
22086
22510
  enum: ["all", "files", "directories"]
22087
22511
  })
@@ -22153,7 +22577,7 @@ var workspaceListExecutor = async (params, _context) => {
22153
22577
  };
22154
22578
 
22155
22579
  // src/agent/tools/workspace/read.ts
22156
- import { Type as Type105 } from "@sinclair/typebox";
22580
+ import { Type as Type107 } from "@sinclair/typebox";
22157
22581
  import { readFileSync as readFileSync13, lstatSync as lstatSync3 } from "fs";
22158
22582
  var workspaceReadTool = {
22159
22583
  name: "workspace_read",
@@ -22169,18 +22593,18 @@ Examples:
22169
22593
  - Read your memory: path="MEMORY.md"
22170
22594
  - Read today's log: path="memory/2024-01-15.md"
22171
22595
  - Read downloaded image info: path="downloads/image.jpg" (will return metadata only)`,
22172
- parameters: Type105.Object({
22173
- path: Type105.String({
22596
+ parameters: Type107.Object({
22597
+ path: Type107.String({
22174
22598
  description: "Path to file (relative to workspace root)"
22175
22599
  }),
22176
- encoding: Type105.Optional(
22177
- Type105.String({
22600
+ encoding: Type107.Optional(
22601
+ Type107.String({
22178
22602
  description: "File encoding: 'utf-8' (default) or 'base64'",
22179
22603
  enum: ["utf-8", "base64"]
22180
22604
  })
22181
22605
  ),
22182
- maxSize: Type105.Optional(
22183
- Type105.Number({
22606
+ maxSize: Type107.Optional(
22607
+ Type107.Number({
22184
22608
  description: "Max file size to read in bytes (default: 1MB)"
22185
22609
  })
22186
22610
  )
@@ -22255,7 +22679,7 @@ var workspaceReadExecutor = async (params, _context) => {
22255
22679
  };
22256
22680
 
22257
22681
  // src/agent/tools/workspace/write.ts
22258
- import { Type as Type106 } from "@sinclair/typebox";
22682
+ import { Type as Type108 } from "@sinclair/typebox";
22259
22683
  import { writeFileSync as writeFileSync9, appendFileSync as appendFileSync3, mkdirSync as mkdirSync9, existsSync as existsSync13 } from "fs";
22260
22684
  import { dirname as dirname6 } from "path";
22261
22685
  var workspaceWriteTool = {
@@ -22272,26 +22696,26 @@ You CANNOT write to protected locations like config.yaml, wallet.json, etc.
22272
22696
  Examples:
22273
22697
  - Save a note: path="memory/note.md", content="..."
22274
22698
  - Prepare upload: path="uploads/message.txt", content="..."`,
22275
- parameters: Type106.Object({
22276
- path: Type106.String({
22699
+ parameters: Type108.Object({
22700
+ path: Type108.String({
22277
22701
  description: "Path to file (relative to workspace root)"
22278
22702
  }),
22279
- content: Type106.String({
22703
+ content: Type108.String({
22280
22704
  description: "Content to write"
22281
22705
  }),
22282
- encoding: Type106.Optional(
22283
- Type106.String({
22706
+ encoding: Type108.Optional(
22707
+ Type108.String({
22284
22708
  description: "Content encoding: 'utf-8' (default) or 'base64'",
22285
22709
  enum: ["utf-8", "base64"]
22286
22710
  })
22287
22711
  ),
22288
- append: Type106.Optional(
22289
- Type106.Boolean({
22712
+ append: Type108.Optional(
22713
+ Type108.Boolean({
22290
22714
  description: "Append to file instead of overwriting (default: false)"
22291
22715
  })
22292
22716
  ),
22293
- createDirs: Type106.Optional(
22294
- Type106.Boolean({
22717
+ createDirs: Type108.Optional(
22718
+ Type108.Boolean({
22295
22719
  description: "Create parent directories if they don't exist (default: true)"
22296
22720
  })
22297
22721
  )
@@ -22312,7 +22736,6 @@ var workspaceWriteExecutor = async (params, _context) => {
22312
22736
  writeContent = content;
22313
22737
  }
22314
22738
  const contentSize = Buffer.byteLength(writeContent);
22315
- const MAX_WRITE_SIZE = 50 * 1024 * 1024;
22316
22739
  if (contentSize > MAX_WRITE_SIZE) {
22317
22740
  return {
22318
22741
  success: false,
@@ -22349,7 +22772,7 @@ var workspaceWriteExecutor = async (params, _context) => {
22349
22772
  };
22350
22773
 
22351
22774
  // src/agent/tools/workspace/delete.ts
22352
- import { Type as Type107 } from "@sinclair/typebox";
22775
+ import { Type as Type109 } from "@sinclair/typebox";
22353
22776
  import { unlinkSync as unlinkSync3, rmdirSync, readdirSync as readdirSync4, rmSync } from "fs";
22354
22777
  var PROTECTED_WORKSPACE_FILES = [
22355
22778
  "SOUL.md",
@@ -22371,12 +22794,12 @@ You CAN delete:
22371
22794
  - Custom files you've created
22372
22795
 
22373
22796
  Use recursive=true to delete non-empty directories.`,
22374
- parameters: Type107.Object({
22375
- path: Type107.String({
22797
+ parameters: Type109.Object({
22798
+ path: Type109.String({
22376
22799
  description: "Path to file or directory to delete"
22377
22800
  }),
22378
- recursive: Type107.Optional(
22379
- Type107.Boolean({
22801
+ recursive: Type109.Optional(
22802
+ Type109.Boolean({
22380
22803
  description: "Delete directory recursively (default: false)"
22381
22804
  })
22382
22805
  )
@@ -22431,7 +22854,7 @@ var workspaceDeleteExecutor = async (params, _context) => {
22431
22854
  };
22432
22855
 
22433
22856
  // src/agent/tools/workspace/info.ts
22434
- import { Type as Type108 } from "@sinclair/typebox";
22857
+ import { Type as Type110 } from "@sinclair/typebox";
22435
22858
  import { lstatSync as lstatSync4, readdirSync as readdirSync5, existsSync as existsSync14 } from "fs";
22436
22859
  import { join as join12 } from "path";
22437
22860
  var MEMES_DIR = WORKSPACE_PATHS.MEMES_DIR;
@@ -22444,9 +22867,9 @@ Returns:
22444
22867
  - Directory structure
22445
22868
  - File counts and sizes
22446
22869
  - Usage limits`,
22447
- parameters: Type108.Object({
22448
- detailed: Type108.Optional(
22449
- Type108.Boolean({
22870
+ parameters: Type110.Object({
22871
+ detailed: Type110.Optional(
22872
+ Type110.Boolean({
22450
22873
  description: "Include detailed file listing (default: false)"
22451
22874
  })
22452
22875
  )
@@ -22545,7 +22968,7 @@ var workspaceInfoExecutor = async (params, _context) => {
22545
22968
  };
22546
22969
 
22547
22970
  // src/agent/tools/workspace/rename.ts
22548
- import { Type as Type109 } from "@sinclair/typebox";
22971
+ import { Type as Type111 } from "@sinclair/typebox";
22549
22972
  import { renameSync, existsSync as existsSync15 } from "fs";
22550
22973
  import { dirname as dirname7 } from "path";
22551
22974
  import { mkdirSync as mkdirSync10 } from "fs";
@@ -22564,15 +22987,15 @@ Examples:
22564
22987
  - Organize: from="downloads/doc.pdf", to="downloads/contracts/2026/lease.pdf"
22565
22988
 
22566
22989
  CANNOT move/rename files outside workspace or to protected locations.`,
22567
- parameters: Type109.Object({
22568
- from: Type109.String({
22990
+ parameters: Type111.Object({
22991
+ from: Type111.String({
22569
22992
  description: "Current path of the file (relative to workspace)"
22570
22993
  }),
22571
- to: Type109.String({
22994
+ to: Type111.String({
22572
22995
  description: "New path for the file (relative to workspace)"
22573
22996
  }),
22574
- overwrite: Type109.Optional(
22575
- Type109.Boolean({
22997
+ overwrite: Type111.Optional(
22998
+ Type111.Boolean({
22576
22999
  description: "Overwrite if destination exists (default: false)"
22577
23000
  })
22578
23001
  )
@@ -22772,7 +23195,7 @@ function getCasinoDb() {
22772
23195
  }
22773
23196
 
22774
23197
  // src/agent/tools/casino/balance.ts
22775
- import { Type as Type110 } from "@sinclair/typebox";
23198
+ import { Type as Type112 } from "@sinclair/typebox";
22776
23199
  var casinoBalanceTool = {
22777
23200
  name: "casino_balance",
22778
23201
  description: `Check Teleton Casino bankroll status and betting limits.
@@ -22789,7 +23212,7 @@ Teleton Casino needs sufficient balance to cover potential payouts (up to 5x the
22789
23212
 
22790
23213
  IMPORTANT: When a player wants to bet, tell them to send TON to Teleton Casino address with their username as memo.
22791
23214
  Example: "Send 2 TON to EQxxx with memo: john_doe"`,
22792
- parameters: Type110.Object({})
23215
+ parameters: Type112.Object({})
22793
23216
  };
22794
23217
  var casinoBalanceExecutor = async (params, context) => {
22795
23218
  try {
@@ -22855,7 +23278,7 @@ var casinoBalanceExecutor = async (params, context) => {
22855
23278
  };
22856
23279
 
22857
23280
  // src/agent/tools/casino/spin.ts
22858
- import { Type as Type111 } from "@sinclair/typebox";
23281
+ import { Type as Type113 } from "@sinclair/typebox";
22859
23282
 
22860
23283
  // src/casino/game-engine.ts
22861
23284
  import { Api as Api57 } from "telegram";
@@ -22928,10 +23351,10 @@ async function sendPayout(playerAddress, amount, message) {
22928
23351
  const endpoint = await getCachedHttpEndpoint();
22929
23352
  const client = new TonClient17({ endpoint });
22930
23353
  const contract = client.open(wallet);
22931
- const seqno = await withBlockchainRetry(() => contract.getSeqno(), "getSeqno");
22932
- await withBlockchainRetry(
22933
- () => contract.sendTransfer({
22934
- seqno,
23354
+ const seqno = await withBlockchainRetry(async () => {
23355
+ const seq = await contract.getSeqno();
23356
+ await contract.sendTransfer({
23357
+ seqno: seq,
22935
23358
  secretKey: keyPair.secretKey,
22936
23359
  sendMode: SendMode10.PAY_GAS_SEPARATELY,
22937
23360
  messages: [
@@ -22942,9 +23365,9 @@ async function sendPayout(playerAddress, amount, message) {
22942
23365
  bounce: false
22943
23366
  })
22944
23367
  ]
22945
- }),
22946
- "sendTransfer"
22947
- );
23368
+ });
23369
+ return seq;
23370
+ }, "payout");
22948
23371
  const walletAddr = wallet.address.toString({ bounceable: false });
22949
23372
  const walletShort = walletAddr.slice(-8);
22950
23373
  const timestamp = Date.now();
@@ -23233,19 +23656,19 @@ Process:
23233
23656
  6. AUTO-PAYOUT if player wins
23234
23657
 
23235
23658
  Tell the user: "Send X TON to [casino_address] with memo: your_username"`,
23236
- parameters: Type111.Object({
23237
- chat_id: Type111.String({
23659
+ parameters: Type113.Object({
23660
+ chat_id: Type113.String({
23238
23661
  description: "Telegram chat ID where to send the spin"
23239
23662
  }),
23240
- bet_amount: Type111.Number({
23663
+ bet_amount: Type113.Number({
23241
23664
  description: "Bet amount in TON",
23242
23665
  minimum: 0.1
23243
23666
  }),
23244
- player_username: Type111.String({
23667
+ player_username: Type113.String({
23245
23668
  description: "Player's Telegram username (without @)"
23246
23669
  }),
23247
- reply_to: Type111.Optional(
23248
- Type111.Number({
23670
+ reply_to: Type113.Optional(
23671
+ Type113.Number({
23249
23672
  description: "Message ID to reply to"
23250
23673
  })
23251
23674
  )
@@ -23269,7 +23692,7 @@ var casinoSpinExecutor = async (params, context) => {
23269
23692
  };
23270
23693
 
23271
23694
  // src/agent/tools/casino/dice.ts
23272
- import { Type as Type112 } from "@sinclair/typebox";
23695
+ import { Type as Type114 } from "@sinclair/typebox";
23273
23696
  var casinoDiceTool = {
23274
23697
  name: "casino_dice",
23275
23698
  description: `Execute a Teleton Casino dice roll with full security checks.
@@ -23289,19 +23712,19 @@ Same security as slot:
23289
23712
  6. AUTO-PAYOUT if player wins
23290
23713
 
23291
23714
  Tell the user: "Send X TON to [casino_address] with memo: your_username"`,
23292
- parameters: Type112.Object({
23293
- chat_id: Type112.String({
23715
+ parameters: Type114.Object({
23716
+ chat_id: Type114.String({
23294
23717
  description: "Telegram chat ID where to send the dice"
23295
23718
  }),
23296
- bet_amount: Type112.Number({
23719
+ bet_amount: Type114.Number({
23297
23720
  description: "Bet amount in TON",
23298
23721
  minimum: 0.1
23299
23722
  }),
23300
- player_username: Type112.String({
23723
+ player_username: Type114.String({
23301
23724
  description: "Player's Telegram username (without @)"
23302
23725
  }),
23303
- reply_to: Type112.Optional(
23304
- Type112.Number({
23726
+ reply_to: Type114.Optional(
23727
+ Type114.Number({
23305
23728
  description: "Message ID to reply to"
23306
23729
  })
23307
23730
  )
@@ -23325,7 +23748,7 @@ var casinoDiceExecutor = async (params, context) => {
23325
23748
  };
23326
23749
 
23327
23750
  // src/agent/tools/casino/leaderboard.ts
23328
- import { Type as Type113 } from "@sinclair/typebox";
23751
+ import { Type as Type115 } from "@sinclair/typebox";
23329
23752
  var casinoLeaderboardTool = {
23330
23753
  name: "casino_leaderboard",
23331
23754
  description: `Show Teleton Casino leaderboard with top players.
@@ -23340,16 +23763,16 @@ Shows:
23340
23763
  - Total amount wagered
23341
23764
  - Total wins/losses
23342
23765
  - Win rate percentage`,
23343
- parameters: Type113.Object({
23344
- limit: Type113.Optional(
23345
- Type113.Number({
23766
+ parameters: Type115.Object({
23767
+ limit: Type115.Optional(
23768
+ Type115.Number({
23346
23769
  description: "Number of players to show (default: 10)",
23347
23770
  minimum: 1,
23348
23771
  maximum: 50
23349
23772
  })
23350
23773
  ),
23351
- type: Type113.Optional(
23352
- Type113.String({
23774
+ type: Type115.Optional(
23775
+ Type115.String({
23353
23776
  description: "Leaderboard type: winners, losers, or wagered",
23354
23777
  enum: ["winners", "losers", "wagered"]
23355
23778
  })
@@ -23454,7 +23877,7 @@ ${formattedPlayers}`
23454
23877
  };
23455
23878
 
23456
23879
  // src/agent/tools/casino/my-stats.ts
23457
- import { Type as Type114 } from "@sinclair/typebox";
23880
+ import { Type as Type116 } from "@sinclair/typebox";
23458
23881
  var casinoMyStatsTool = {
23459
23882
  name: "casino_my_stats",
23460
23883
  description: `Show the current player's personal Teleton Casino statistics.
@@ -23467,7 +23890,7 @@ Returns:
23467
23890
  - Net profit/loss
23468
23891
  - Win rate percentage
23469
23892
  - Last bet timestamp`,
23470
- parameters: Type114.Object({})
23893
+ parameters: Type116.Object({})
23471
23894
  };
23472
23895
  var casinoMyStatsExecutor = async (params, context) => {
23473
23896
  try {
@@ -23596,7 +24019,7 @@ var MarketScraperService = class {
23596
24019
  this.isScrapingInProgress = true;
23597
24020
  console.log("\u{1F504} Starting full market scrape...");
23598
24021
  try {
23599
- const { runScraper } = await import("./scraper-DSAYK6QJ.js");
24022
+ const { runScraper } = await import("./scraper-KXRBQMVQ.js");
23600
24023
  const result = await runScraper({
23601
24024
  workers: 4,
23602
24025
  limit: 0
@@ -23940,6 +24363,85 @@ function loadModules(registry, config, db3) {
23940
24363
  return loaded;
23941
24364
  }
23942
24365
 
24366
+ // src/agent/tools/module-permissions.ts
24367
+ var PROTECTED_MODULES = /* @__PURE__ */ new Set(["telegram", "memory"]);
24368
+ var ModulePermissions = class {
24369
+ db;
24370
+ /** chatId → module → level */
24371
+ cache = /* @__PURE__ */ new Map();
24372
+ constructor(db3) {
24373
+ this.db = db3;
24374
+ this.ensureTable();
24375
+ this.loadAll();
24376
+ }
24377
+ ensureTable() {
24378
+ this.db.exec(`
24379
+ CREATE TABLE IF NOT EXISTS group_modules (
24380
+ chat_id TEXT NOT NULL,
24381
+ module TEXT NOT NULL,
24382
+ level TEXT NOT NULL CHECK(level IN ('open', 'admin', 'disabled')),
24383
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
24384
+ updated_by INTEGER,
24385
+ PRIMARY KEY (chat_id, module)
24386
+ )
24387
+ `);
24388
+ }
24389
+ loadAll() {
24390
+ const rows = this.db.prepare("SELECT chat_id, module, level FROM group_modules").all();
24391
+ for (const row of rows) {
24392
+ let chatMap = this.cache.get(row.chat_id);
24393
+ if (!chatMap) {
24394
+ chatMap = /* @__PURE__ */ new Map();
24395
+ this.cache.set(row.chat_id, chatMap);
24396
+ }
24397
+ chatMap.set(row.module, row.level);
24398
+ }
24399
+ }
24400
+ /** Get the effective level for a module in a chat. Default: "open". */
24401
+ getLevel(chatId, module) {
24402
+ return this.cache.get(chatId)?.get(module) ?? "open";
24403
+ }
24404
+ /** Set the level for a module in a chat. Throws if module is protected. */
24405
+ setLevel(chatId, module, level, userId) {
24406
+ if (PROTECTED_MODULES.has(module)) {
24407
+ throw new Error(`Module "${module}" est prot\xE9g\xE9`);
24408
+ }
24409
+ if (level === "open") {
24410
+ this.db.prepare("DELETE FROM group_modules WHERE chat_id = ? AND module = ?").run(chatId, module);
24411
+ this.cache.get(chatId)?.delete(module);
24412
+ } else {
24413
+ this.db.prepare(
24414
+ `INSERT OR REPLACE INTO group_modules (chat_id, module, level, updated_at, updated_by)
24415
+ VALUES (?, ?, ?, unixepoch(), ?)`
24416
+ ).run(chatId, module, level, userId ?? null);
24417
+ let chatMap = this.cache.get(chatId);
24418
+ if (!chatMap) {
24419
+ chatMap = /* @__PURE__ */ new Map();
24420
+ this.cache.set(chatId, chatMap);
24421
+ }
24422
+ chatMap.set(module, level);
24423
+ }
24424
+ }
24425
+ /** Reset a single module to default ("open"). */
24426
+ resetModule(chatId, module) {
24427
+ this.db.prepare("DELETE FROM group_modules WHERE chat_id = ? AND module = ?").run(chatId, module);
24428
+ this.cache.get(chatId)?.delete(module);
24429
+ }
24430
+ /** Reset all modules for a chat to default ("open"). */
24431
+ resetAll(chatId) {
24432
+ this.db.prepare("DELETE FROM group_modules WHERE chat_id = ?").run(chatId);
24433
+ this.cache.delete(chatId);
24434
+ }
24435
+ /** Get all non-default overrides for a chat. */
24436
+ getOverrides(chatId) {
24437
+ return this.cache.get(chatId) ?? /* @__PURE__ */ new Map();
24438
+ }
24439
+ /** Check if a module is protected (always open, cannot be changed). */
24440
+ isProtected(module) {
24441
+ return PROTECTED_MODULES.has(module);
24442
+ }
24443
+ };
24444
+
23943
24445
  // src/index.ts
23944
24446
  var TonnetApp = class {
23945
24447
  config;
@@ -23971,22 +24473,23 @@ var TonnetApp = class {
23971
24473
  autoReconnect: true,
23972
24474
  floodSleepThreshold: TELEGRAM_FLOOD_SLEEP_THRESHOLD
23973
24475
  });
23974
- const VECTOR_DIMENSIONS = 512;
24476
+ const VECTOR_DIMENSIONS = 384;
23975
24477
  const memory = initializeMemory({
23976
24478
  database: {
23977
24479
  path: join16(TELETON_ROOT, "memory.db"),
23978
- enableVectorSearch: false,
24480
+ enableVectorSearch: true,
23979
24481
  vectorDimensions: VECTOR_DIMENSIONS
23980
24482
  },
23981
24483
  embeddings: {
23982
- provider: "none",
23983
- apiKey: "",
24484
+ provider: "local",
23984
24485
  model: ""
23985
24486
  },
23986
24487
  workspaceDir: join16(TELETON_ROOT)
23987
24488
  });
23988
24489
  const db3 = getDatabase().getDb();
23989
24490
  this.modules = loadModules(this.toolRegistry, this.config, db3);
24491
+ const modulePermissions = new ModulePermissions(db3);
24492
+ this.toolRegistry.setPermissions(modulePermissions);
23990
24493
  this.toolCount = this.toolRegistry.count;
23991
24494
  this.messageHandler = new MessageHandler(
23992
24495
  this.bridge,
@@ -23999,7 +24502,13 @@ var TonnetApp = class {
23999
24502
  this.config
24000
24503
  // Pass full config for vision tool API key access
24001
24504
  );
24002
- this.adminHandler = new AdminHandler(this.bridge, this.config.telegram, this.agent);
24505
+ this.adminHandler = new AdminHandler(
24506
+ this.bridge,
24507
+ this.config.telegram,
24508
+ this.agent,
24509
+ modulePermissions,
24510
+ this.toolRegistry
24511
+ );
24003
24512
  }
24004
24513
  /**
24005
24514
  * Start the agent
@@ -24038,19 +24547,18 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24038
24547
  `\u26A0\uFE0F Tool count (${this.toolCount}) exceeds ${providerMeta.displayName} limit (${providerMeta.toolLimit})`
24039
24548
  );
24040
24549
  }
24041
- const { migrateSessionsToDb } = await import("./migrate-7XOO67O5.js");
24550
+ const { migrateSessionsToDb } = await import("./migrate-7OG67FXP.js");
24042
24551
  migrateSessionsToDb();
24043
24552
  const { cleanupOldTranscripts } = await import("./transcript-DF2Y6CFY.js");
24044
24553
  cleanupOldTranscripts(30);
24045
24554
  const memory = initializeMemory({
24046
24555
  database: {
24047
24556
  path: join16(TELETON_ROOT, "memory.db"),
24048
- enableVectorSearch: false,
24049
- vectorDimensions: 512
24557
+ enableVectorSearch: true,
24558
+ vectorDimensions: 384
24050
24559
  },
24051
24560
  embeddings: {
24052
- provider: "none",
24053
- apiKey: "",
24561
+ provider: "local",
24054
24562
  model: ""
24055
24563
  },
24056
24564
  workspaceDir: join16(TELETON_ROOT)
@@ -24159,7 +24667,8 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24159
24667
  const response = await this.adminHandler.handleCommand(
24160
24668
  adminCmd,
24161
24669
  message.chatId,
24162
- message.senderId
24670
+ message.senderId,
24671
+ message.isGroup
24163
24672
  );
24164
24673
  await this.bridge.sendMessage({
24165
24674
  chatId: message.chatId,
@@ -24187,8 +24696,8 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24187
24696
  }
24188
24697
  const taskId = match[1];
24189
24698
  const { getTaskStore } = await import("./tasks-M3QDPTGY.js");
24190
- const { executeScheduledTask } = await import("./task-executor-MNI4VIZL.js");
24191
- const { getDatabase: getDatabase2 } = await import("./memory-OEOLPEXP.js");
24699
+ const { executeScheduledTask } = await import("./task-executor-AUTT3VAL.js");
24700
+ const { getDatabase: getDatabase2 } = await import("./memory-ZXDAJBL6.js");
24192
24701
  const db3 = getDatabase2().getDb();
24193
24702
  const taskStore = getTaskStore(db3);
24194
24703
  const task = taskStore.getTask(taskId);
@@ -24254,7 +24763,7 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24254
24763
  taskStore.completeTask(taskId, response.content);
24255
24764
  console.log(`\u2705 Executed scheduled task ${taskId}: ${task.description}`);
24256
24765
  if (!this.dependencyResolver) {
24257
- const { TaskDependencyResolver } = await import("./task-dependency-resolver-CWG6DTU4.js");
24766
+ const { TaskDependencyResolver } = await import("./task-dependency-resolver-S45DFI5C.js");
24258
24767
  this.dependencyResolver = new TaskDependencyResolver(taskStore, this.bridge);
24259
24768
  }
24260
24769
  await this.dependencyResolver.onTaskComplete(taskId);
@@ -24262,8 +24771,8 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24262
24771
  console.error("Error handling scheduled task:", error);
24263
24772
  try {
24264
24773
  const { getTaskStore } = await import("./tasks-M3QDPTGY.js");
24265
- const { TaskDependencyResolver } = await import("./task-dependency-resolver-CWG6DTU4.js");
24266
- const { getDatabase: getDatabase2 } = await import("./memory-OEOLPEXP.js");
24774
+ const { TaskDependencyResolver } = await import("./task-dependency-resolver-S45DFI5C.js");
24775
+ const { getDatabase: getDatabase2 } = await import("./memory-ZXDAJBL6.js");
24267
24776
  const db3 = getDatabase2().getDb();
24268
24777
  const taskStore = getTaskStore(db3);
24269
24778
  const match = message.text.match(/^\[TASK:([^\]]+)\]/);
@@ -24303,14 +24812,19 @@ async function main(configPath) {
24303
24812
  console.error("\u{1F4A5} Uncaught exception:", error);
24304
24813
  process.exit(1);
24305
24814
  });
24306
- process.on("SIGINT", async () => {
24307
- await app.stop();
24308
- process.exit(0);
24309
- });
24310
- process.on("SIGTERM", async () => {
24815
+ const gracefulShutdown = async () => {
24816
+ const { SHUTDOWN_TIMEOUT_MS } = await import("./timeouts-ZAK6NELA.js");
24817
+ const forceExit = setTimeout(() => {
24818
+ console.error("\u26A0\uFE0F Shutdown timed out, forcing exit");
24819
+ process.exit(1);
24820
+ }, SHUTDOWN_TIMEOUT_MS);
24821
+ forceExit.unref();
24311
24822
  await app.stop();
24823
+ clearTimeout(forceExit);
24312
24824
  process.exit(0);
24313
- });
24825
+ };
24826
+ process.on("SIGINT", gracefulShutdown);
24827
+ process.on("SIGTERM", gracefulShutdown);
24314
24828
  await app.start();
24315
24829
  }
24316
24830
  if (import.meta.url === `file://${process.argv[1]}`) {