teleton 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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";
@@ -14,12 +24,13 @@ import {
14
24
  MessageStore,
15
25
  USED_TRANSACTIONS_SCHEMA,
16
26
  UserStore,
27
+ closeDatabase,
17
28
  createDbWrapper,
18
29
  getDatabase,
19
30
  initializeMemory,
20
31
  migrateFromMainDb,
21
32
  openModuleDb
22
- } from "./chunk-TBIMVWQZ.js";
33
+ } from "./chunk-DR6WM6B5.js";
23
34
  import {
24
35
  COINGECKO_API_URL,
25
36
  ELEVENLABS_TTS_URL,
@@ -29,7 +40,7 @@ import {
29
40
  fetchWithTimeout,
30
41
  setTonapiKey,
31
42
  tonapiFetch
32
- } from "./chunk-WMIN6AGX.js";
43
+ } from "./chunk-DAMFGHXV.js";
33
44
  import {
34
45
  COMPACTION_KEEP_RECENT,
35
46
  COMPACTION_MAX_MESSAGES,
@@ -42,8 +53,10 @@ import {
42
53
  DEBOUNCE_MAX_BUFFER_SIZE,
43
54
  DEBOUNCE_MAX_MULTIPLIER,
44
55
  DEFAULT_GIFTS_QUERY_LIMIT,
56
+ MAX_DEPENDENTS_PER_TASK,
45
57
  MAX_POLL_QUESTION_LENGTH,
46
58
  MAX_TOOL_RESULT_SIZE,
59
+ MAX_WRITE_SIZE,
47
60
  PAYMENT_TOLERANCE_RATIO,
48
61
  PENDING_HISTORY_MAX_AGE_MS,
49
62
  PENDING_HISTORY_MAX_PER_CHAT,
@@ -52,7 +65,14 @@ import {
52
65
  TELEGRAM_CONNECTION_RETRIES,
53
66
  TELEGRAM_FLOOD_SLEEP_THRESHOLD,
54
67
  TELEGRAM_MAX_MESSAGE_LENGTH
55
- } from "./chunk-QMN6ZOA5.js";
68
+ } from "./chunk-2X4PCE7V.js";
69
+ import {
70
+ ALLOWED_EXTENSIONS,
71
+ MAX_FILE_SIZES,
72
+ TELETON_ROOT,
73
+ WORKSPACE_PATHS,
74
+ WORKSPACE_ROOT
75
+ } from "./chunk-EYWNOHMJ.js";
56
76
  import {
57
77
  GRAMJS_RETRY_DELAY_MS,
58
78
  MESSAGE_HANDLER_LOCK_TIMEOUT_MS,
@@ -63,29 +83,13 @@ import {
63
83
  RETRY_DEFAULT_MAX_ATTEMPTS,
64
84
  RETRY_DEFAULT_MAX_DELAY_MS,
65
85
  RETRY_DEFAULT_TIMEOUT_MS,
86
+ TOOL_EXECUTION_TIMEOUT_MS,
66
87
  TTS_TIMEOUT_MS
67
- } from "./chunk-LJXYESJJ.js";
68
- import {
69
- ALLOWED_EXTENSIONS,
70
- MAX_FILE_SIZES,
71
- TELETON_ROOT,
72
- WORKSPACE_PATHS,
73
- WORKSPACE_ROOT
74
- } from "./chunk-EYWNOHMJ.js";
88
+ } from "./chunk-LCMHAUNK.js";
75
89
  import {
76
90
  telegramGetMyGiftsExecutor,
77
91
  telegramGetMyGiftsTool
78
92
  } from "./chunk-B2PRMXOH.js";
79
- import {
80
- Mi,
81
- Mn,
82
- br,
83
- dt,
84
- le,
85
- qn,
86
- ut,
87
- ye
88
- } from "./chunk-U7FQYCBQ.js";
89
93
  import {
90
94
  __commonJS,
91
95
  __toESM
@@ -3408,7 +3412,13 @@ ${statsContext}`;
3408
3412
  const providerMeta = getProviderMetadata(
3409
3413
  this.config.agent.provider || "anthropic"
3410
3414
  );
3411
- const tools23 = this.toolRegistry?.getForContext(isGroup ?? false, providerMeta.toolLimit);
3415
+ const isAdmin = toolContext?.config?.telegram.admin_ids.includes(toolContext.senderId) ?? false;
3416
+ const tools23 = this.toolRegistry?.getForContext(
3417
+ isGroup ?? false,
3418
+ providerMeta.toolLimit,
3419
+ chatId,
3420
+ isAdmin
3421
+ );
3412
3422
  const maxIterations = this.config.agent.max_agentic_iterations || 5;
3413
3423
  let iteration = 0;
3414
3424
  let overflowResets = 0;
@@ -3623,22 +3633,6 @@ ${statsContext}`;
3623
3633
  */
3624
3634
  clearHistory(chatId) {
3625
3635
  const db3 = getDatabase().getDb();
3626
- db3.prepare(
3627
- `
3628
- DELETE FROM tg_messages_fts
3629
- WHERE rowid IN (
3630
- SELECT rowid FROM tg_messages WHERE chat_id = ?
3631
- )
3632
- `
3633
- ).run(chatId);
3634
- db3.prepare(
3635
- `
3636
- DELETE FROM tg_messages_vec
3637
- WHERE id IN (
3638
- SELECT id FROM tg_messages WHERE chat_id = ?
3639
- )
3640
- `
3641
- ).run(chatId);
3642
3636
  db3.prepare(`DELETE FROM tg_messages WHERE chat_id = ?`).run(chatId);
3643
3637
  resetSession(chatId);
3644
3638
  console.log(`\u{1F5D1}\uFE0F Cleared history for chat ${chatId}`);
@@ -4839,7 +4833,21 @@ import { mnemonicNew, mnemonicToPrivateKey, mnemonicValidate } from "@ton/crypto
4839
4833
  import { WalletContractV5R1, TonClient, fromNano } from "@ton/ton";
4840
4834
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync8, mkdirSync as mkdirSync6, chmodSync as chmodSync2 } from "fs";
4841
4835
  import { join as join6, dirname as dirname5 } from "path";
4836
+
4837
+ // src/ton/endpoint.ts
4842
4838
  import { getHttpEndpoint } from "@orbs-network/ton-access";
4839
+ var ENDPOINT_CACHE_TTL_MS = 6e4;
4840
+ var _cache = null;
4841
+ async function getCachedHttpEndpoint() {
4842
+ if (_cache && Date.now() - _cache.ts < ENDPOINT_CACHE_TTL_MS) {
4843
+ return _cache.url;
4844
+ }
4845
+ const url = await getHttpEndpoint({ network: "mainnet" });
4846
+ _cache = { url, ts: Date.now() };
4847
+ return url;
4848
+ }
4849
+
4850
+ // src/ton/wallet-service.ts
4843
4851
  var WALLET_FILE = join6(TELETON_ROOT, "wallet.json");
4844
4852
  async function generateWallet() {
4845
4853
  const mnemonic = await mnemonicNew(24);
@@ -4905,7 +4913,7 @@ function getWalletAddress() {
4905
4913
  }
4906
4914
  async function getWalletBalance(address4) {
4907
4915
  try {
4908
- const endpoint = await getHttpEndpoint({ network: "mainnet" });
4916
+ const endpoint = await getCachedHttpEndpoint();
4909
4917
  const client = new TonClient({ endpoint });
4910
4918
  const { Address: Address19 } = await import("@ton/core");
4911
4919
  const addressObj = Address19.parse(address4);
@@ -4999,15 +5007,20 @@ function initDealsConfig(yaml) {
4999
5007
  // src/telegram/admin.ts
5000
5008
  var VALID_DM_POLICIES = ["open", "allowlist", "pairing", "disabled"];
5001
5009
  var VALID_GROUP_POLICIES = ["open", "allowlist", "disabled"];
5010
+ var VALID_MODULE_LEVELS = ["open", "admin", "disabled"];
5002
5011
  var AdminHandler = class {
5003
5012
  bridge;
5004
5013
  config;
5005
5014
  agent;
5006
5015
  paused = false;
5007
- constructor(bridge, config, agent) {
5016
+ permissions;
5017
+ registry;
5018
+ constructor(bridge, config, agent, permissions, registry) {
5008
5019
  this.bridge = bridge;
5009
5020
  this.config = config;
5010
5021
  this.agent = agent;
5022
+ this.permissions = permissions ?? null;
5023
+ this.registry = registry ?? null;
5011
5024
  }
5012
5025
  /**
5013
5026
  * Check if user is admin
@@ -5042,7 +5055,7 @@ var AdminHandler = class {
5042
5055
  /**
5043
5056
  * Handle admin command
5044
5057
  */
5045
- async handleCommand(command, chatId, senderId) {
5058
+ async handleCommand(command, chatId, senderId, isGroup) {
5046
5059
  if (!this.isAdmin(senderId)) {
5047
5060
  return "\u26D4 Admin access required";
5048
5061
  }
@@ -5073,6 +5086,8 @@ var AdminHandler = class {
5073
5086
  return await this.handleStopCommand();
5074
5087
  case "verbose":
5075
5088
  return this.handleVerboseCommand();
5089
+ case "modules":
5090
+ return this.handleModulesCommand(command, isGroup ?? false);
5076
5091
  case "help":
5077
5092
  return this.handleHelpCommand();
5078
5093
  case "ping":
@@ -5285,6 +5300,124 @@ Usage:
5285
5300
  setVerbose(next);
5286
5301
  return next ? "\u{1F50A} Verbose logging **ON**" : "\u{1F507} Verbose logging **OFF**";
5287
5302
  }
5303
+ /**
5304
+ * /modules - Manage per-group module permissions
5305
+ */
5306
+ handleModulesCommand(command, isGroup) {
5307
+ if (!this.permissions || !this.registry) {
5308
+ return "\u274C Module permissions non disponible";
5309
+ }
5310
+ if (!isGroup) {
5311
+ return "\u274C /modules est uniquement disponible dans les groupes";
5312
+ }
5313
+ const chatId = command.chatId;
5314
+ const sub = command.args[0]?.toLowerCase();
5315
+ if (!sub) {
5316
+ return this.listModules(chatId);
5317
+ }
5318
+ switch (sub) {
5319
+ case "set":
5320
+ return this.setModuleLevel(chatId, command.args[1], command.args[2], command.senderId);
5321
+ case "info":
5322
+ return this.showModuleInfo(command.args[1], chatId);
5323
+ case "reset":
5324
+ return this.resetModules(chatId, command.args[1]);
5325
+ default:
5326
+ return `\u274C Sous-commande inconnue: "${sub}"
5327
+
5328
+ Usage: /modules | /modules set <module> <level> | /modules info <module> | /modules reset [module]`;
5329
+ }
5330
+ }
5331
+ listModules(chatId) {
5332
+ const modules = this.registry.getAvailableModules();
5333
+ const overrides = this.permissions.getOverrides(chatId);
5334
+ const lines = ["\u{1F9E9} **Modules** (ce groupe)\n"];
5335
+ for (const mod of modules) {
5336
+ const count = this.registry.getModuleToolCount(mod);
5337
+ const level = overrides.get(mod) ?? "open";
5338
+ const isProtected = this.permissions.isProtected(mod);
5339
+ let icon;
5340
+ switch (level) {
5341
+ case "open":
5342
+ icon = "\u2705";
5343
+ break;
5344
+ case "admin":
5345
+ icon = "\u{1F510}";
5346
+ break;
5347
+ case "disabled":
5348
+ icon = "\u274C";
5349
+ break;
5350
+ }
5351
+ const toolWord = count === 1 ? "tool" : "tools";
5352
+ const protectedMark = isProtected ? " \u{1F512}" : "";
5353
+ lines.push(` ${icon} **${mod}** ${count} ${toolWord} ${level}${protectedMark}`);
5354
+ }
5355
+ lines.push("");
5356
+ lines.push("Niveaux: `open` | `admin` | `disabled`");
5357
+ lines.push("Usage: `/modules set <module> <level>`");
5358
+ return lines.join("\n");
5359
+ }
5360
+ setModuleLevel(chatId, module, level, senderId) {
5361
+ if (!module || !level) {
5362
+ return "\u274C Usage: /modules set <module> <level>";
5363
+ }
5364
+ module = module.toLowerCase();
5365
+ level = level.toLowerCase();
5366
+ const available = this.registry.getAvailableModules();
5367
+ if (!available.includes(module)) {
5368
+ return `\u274C Module inconnu: "${module}"`;
5369
+ }
5370
+ if (this.permissions.isProtected(module)) {
5371
+ return `\u26D4 Module "${module}" est prot\xE9g\xE9`;
5372
+ }
5373
+ if (!VALID_MODULE_LEVELS.includes(level)) {
5374
+ return `\u274C Niveau invalide: "${level}". Valide: ${VALID_MODULE_LEVELS.join(", ")}`;
5375
+ }
5376
+ const oldLevel = this.permissions.getLevel(chatId, module);
5377
+ this.permissions.setLevel(chatId, module, level, senderId);
5378
+ const icons = { open: "\u2705", admin: "\u{1F510}", disabled: "\u274C" };
5379
+ return `${icons[level]} **${module}**: ${oldLevel} \u2192 ${level}`;
5380
+ }
5381
+ showModuleInfo(module, chatId) {
5382
+ if (!module) {
5383
+ return "\u274C Usage: /modules info <module>";
5384
+ }
5385
+ module = module.toLowerCase();
5386
+ const available = this.registry.getAvailableModules();
5387
+ if (!available.includes(module)) {
5388
+ return `\u274C Module inconnu: "${module}"`;
5389
+ }
5390
+ const tools23 = this.registry.getModuleTools(module);
5391
+ const count = tools23.length;
5392
+ const toolWord = count === 1 ? "tool" : "tools";
5393
+ const level = this.permissions.getLevel(chatId, module);
5394
+ const isProtected = this.permissions.isProtected(module);
5395
+ const protectedMark = isProtected ? " \u{1F512}" : "";
5396
+ const lines = [
5397
+ `\u{1F4E6} Module "**${module}**" \u2014 ${level}${protectedMark} (${count} ${toolWord})
5398
+ `
5399
+ ];
5400
+ for (const t of tools23) {
5401
+ lines.push(` ${t.name} ${t.scope}`);
5402
+ }
5403
+ return lines.join("\n");
5404
+ }
5405
+ resetModules(chatId, module) {
5406
+ if (module) {
5407
+ module = module.toLowerCase();
5408
+ const available = this.registry.getAvailableModules();
5409
+ if (!available.includes(module)) {
5410
+ return `\u274C Module inconnu: "${module}"`;
5411
+ }
5412
+ if (this.permissions.isProtected(module)) {
5413
+ return `\u26D4 Module "${module}" est prot\xE9g\xE9 (d\xE9j\xE0 open)`;
5414
+ }
5415
+ this.permissions.resetModule(chatId, module);
5416
+ return `\u2705 **${module}** \u2192 open`;
5417
+ }
5418
+ this.permissions.resetAll(chatId);
5419
+ return "\u2705 Tous les modules remis \xE0 **open**";
5420
+ }
5288
5421
  /**
5289
5422
  * /help - Show available commands
5290
5423
  */
@@ -5306,6 +5439,9 @@ Change access policy
5306
5439
  **/strategy** [buy|sell <percent>]
5307
5440
  View or change trading thresholds
5308
5441
 
5442
+ **/modules** [set|info|reset]
5443
+ Manage per-group module permissions
5444
+
5309
5445
  **/wallet**
5310
5446
  Check TON wallet balance
5311
5447
 
@@ -5457,6 +5593,8 @@ import { validateToolCall } from "@mariozechner/pi-ai";
5457
5593
  var ToolRegistry = class {
5458
5594
  tools = /* @__PURE__ */ new Map();
5459
5595
  scopes = /* @__PURE__ */ new Map();
5596
+ toolModules = /* @__PURE__ */ new Map();
5597
+ permissions = null;
5460
5598
  /**
5461
5599
  * Register a new tool with optional scope
5462
5600
  */
@@ -5468,6 +5606,42 @@ var ToolRegistry = class {
5468
5606
  if (scope && scope !== "always") {
5469
5607
  this.scopes.set(tool.name, scope);
5470
5608
  }
5609
+ this.toolModules.set(tool.name, tool.name.split("_")[0]);
5610
+ }
5611
+ /**
5612
+ * Set the module permissions manager
5613
+ */
5614
+ setPermissions(mp) {
5615
+ this.permissions = mp;
5616
+ }
5617
+ /**
5618
+ * Get sorted unique module names derived from registered tools
5619
+ */
5620
+ getAvailableModules() {
5621
+ const modules = new Set(this.toolModules.values());
5622
+ return Array.from(modules).sort();
5623
+ }
5624
+ /**
5625
+ * Get the number of tools in a module
5626
+ */
5627
+ getModuleToolCount(module) {
5628
+ let count = 0;
5629
+ for (const mod of this.toolModules.values()) {
5630
+ if (mod === module) count++;
5631
+ }
5632
+ return count;
5633
+ }
5634
+ /**
5635
+ * Get tools belonging to a module with their scope
5636
+ */
5637
+ getModuleTools(module) {
5638
+ const result = [];
5639
+ for (const [name, mod] of this.toolModules) {
5640
+ if (mod === module) {
5641
+ result.push({ name, scope: this.scopes.get(name) ?? "always" });
5642
+ }
5643
+ }
5644
+ return result.sort((a, b) => a.name.localeCompare(b.name));
5471
5645
  }
5472
5646
  /**
5473
5647
  * Get all registered tools for pi-ai
@@ -5499,9 +5673,43 @@ var ToolRegistry = class {
5499
5673
  error: `Tool "${toolCall.name}" is only available in group chats`
5500
5674
  };
5501
5675
  }
5676
+ if (context.isGroup && this.permissions) {
5677
+ const module = this.toolModules.get(toolCall.name);
5678
+ if (module) {
5679
+ const level = this.permissions.getLevel(context.chatId, module);
5680
+ if (level === "disabled") {
5681
+ return {
5682
+ success: false,
5683
+ error: `Module "${module}" is disabled in this group`
5684
+ };
5685
+ }
5686
+ if (level === "admin") {
5687
+ const isAdmin = context.config?.telegram.admin_ids.includes(context.senderId) ?? false;
5688
+ if (!isAdmin) {
5689
+ return {
5690
+ success: false,
5691
+ error: `Module "${module}" is restricted to admins in this group`
5692
+ };
5693
+ }
5694
+ }
5695
+ }
5696
+ }
5502
5697
  try {
5503
5698
  const validatedArgs = validateToolCall(this.getAll(), toolCall);
5504
- const result = await registered.executor(validatedArgs, context);
5699
+ let timeoutHandle;
5700
+ const result = await Promise.race([
5701
+ registered.executor(validatedArgs, context),
5702
+ new Promise((_, reject) => {
5703
+ timeoutHandle = setTimeout(
5704
+ () => reject(
5705
+ new Error(
5706
+ `Tool "${toolCall.name}" timed out after ${TOOL_EXECUTION_TIMEOUT_MS / 1e3}s`
5707
+ )
5708
+ ),
5709
+ TOOL_EXECUTION_TIMEOUT_MS
5710
+ );
5711
+ })
5712
+ ]).finally(() => clearTimeout(timeoutHandle));
5505
5713
  return result;
5506
5714
  } catch (error) {
5507
5715
  console.error(`Error executing tool ${toolCall.name}:`, error);
@@ -5525,13 +5733,25 @@ var ToolRegistry = class {
5525
5733
  return all.slice(0, toolLimit);
5526
5734
  }
5527
5735
  /**
5528
- * Get tools filtered by chat context (DM vs group) and provider limit.
5736
+ * Get tools filtered by chat context (DM vs group), module permissions, and provider limit.
5529
5737
  * - In groups: excludes "dm-only" tools (financial, private)
5530
5738
  * - In DMs: excludes "group-only" tools (moderation)
5739
+ * - In groups with permissions: excludes disabled modules, admin-only modules for non-admins
5531
5740
  */
5532
- getForContext(isGroup, toolLimit) {
5741
+ getForContext(isGroup, toolLimit, chatId, isAdmin) {
5533
5742
  const excluded = isGroup ? "dm-only" : "group-only";
5534
- const filtered = Array.from(this.tools.values()).filter((rt) => this.scopes.get(rt.tool.name) !== excluded).map((rt) => rt.tool);
5743
+ const filtered = Array.from(this.tools.values()).filter((rt) => {
5744
+ if (this.scopes.get(rt.tool.name) === excluded) return false;
5745
+ if (isGroup && chatId && this.permissions) {
5746
+ const module = this.toolModules.get(rt.tool.name);
5747
+ if (module) {
5748
+ const level = this.permissions.getLevel(chatId, module);
5749
+ if (level === "disabled") return false;
5750
+ if (level === "admin" && !isAdmin) return false;
5751
+ }
5752
+ }
5753
+ return true;
5754
+ }).map((rt) => rt.tool);
5535
5755
  if (toolLimit !== null && filtered.length > toolLimit) {
5536
5756
  console.warn(
5537
5757
  `\u26A0\uFE0F Provider tool limit: ${toolLimit}, after scope filter: ${filtered.length}. Truncating to ${toolLimit} tools.`
@@ -10006,18 +10226,20 @@ function getDeal(db3, dealId) {
10006
10226
  }
10007
10227
  function acceptDeal(db3, dealId) {
10008
10228
  const newExpiry = Math.floor(Date.now() / 1e3) + DEAL_VERIFICATION_WINDOW_SECONDS;
10009
- db3.prepare(`UPDATE deals SET status = 'accepted', expires_at = ? WHERE id = ?`).run(
10010
- newExpiry,
10011
- dealId
10012
- );
10229
+ const r3 = db3.prepare(
10230
+ `UPDATE deals SET status = 'accepted', expires_at = ? WHERE id = ? AND status = 'proposed'`
10231
+ ).run(newExpiry, dealId);
10232
+ return r3.changes === 1;
10013
10233
  }
10014
10234
  function declineDeal(db3, dealId) {
10015
- db3.prepare(`UPDATE deals SET status = 'declined' WHERE id = ?`).run(dealId);
10235
+ const r3 = db3.prepare(`UPDATE deals SET status = 'declined' WHERE id = ? AND status = 'proposed'`).run(dealId);
10236
+ return r3.changes === 1;
10016
10237
  }
10017
10238
  function claimPayment(db3, dealId) {
10018
- db3.prepare(
10019
- `UPDATE deals SET status = 'payment_claimed', payment_claimed_at = unixepoch() WHERE id = ?`
10239
+ const r3 = db3.prepare(
10240
+ `UPDATE deals SET status = 'payment_claimed', payment_claimed_at = unixepoch() WHERE id = ? AND status = 'accepted'`
10020
10241
  ).run(dealId);
10242
+ return r3.changes === 1;
10021
10243
  }
10022
10244
  function setInlineMessageId(db3, dealId, inlineMessageId) {
10023
10245
  db3.prepare(`UPDATE deals SET inline_message_id = ? WHERE id = ?`).run(inlineMessageId, dealId);
@@ -10027,7 +10249,10 @@ function isDealExpired(deal) {
10027
10249
  return now > deal.expiresAt;
10028
10250
  }
10029
10251
  function expireDeal(db3, dealId) {
10030
- db3.prepare(`UPDATE deals SET status = 'expired' WHERE id = ?`).run(dealId);
10252
+ const r3 = db3.prepare(
10253
+ `UPDATE deals SET status = 'expired' WHERE id = ? AND status IN ('proposed', 'accepted')`
10254
+ ).run(dealId);
10255
+ return r3.changes === 1;
10031
10256
  }
10032
10257
  function getDealsAwaitingVerification(db3) {
10033
10258
  const rows = db3.prepare(
@@ -10520,19 +10745,6 @@ var GramJSBotClient = class {
10520
10745
  import { TonClient as TonClient2, fromNano as fromNano2 } from "@ton/ton";
10521
10746
  import { Address } from "@ton/core";
10522
10747
 
10523
- // src/ton/endpoint.ts
10524
- import { getHttpEndpoint as getHttpEndpoint2 } from "@orbs-network/ton-access";
10525
- var ENDPOINT_CACHE_TTL_MS = 6e4;
10526
- var _cache = null;
10527
- async function getCachedHttpEndpoint() {
10528
- if (_cache && Date.now() - _cache.ts < ENDPOINT_CACHE_TTL_MS) {
10529
- return _cache.url;
10530
- }
10531
- const url = await getHttpEndpoint2({ network: "mainnet" });
10532
- _cache = { url, ts: Date.now() };
10533
- return url;
10534
- }
10535
-
10536
10748
  // src/casino/retry.ts
10537
10749
  var DEFAULT_OPTIONS = {
10538
10750
  maxAttempts: RETRY_DEFAULT_MAX_ATTEMPTS,
@@ -10767,7 +10979,6 @@ If you already sent, wait a moment and try again.`
10767
10979
  import { mnemonicToPrivateKey as mnemonicToPrivateKey2 } from "@ton/crypto";
10768
10980
  import { WalletContractV5R1 as WalletContractV5R12, TonClient as TonClient3, toNano, internal } from "@ton/ton";
10769
10981
  import { Address as Address2, SendMode } from "@ton/core";
10770
- import { getHttpEndpoint as getHttpEndpoint3 } from "@orbs-network/ton-access";
10771
10982
  async function sendTon(params) {
10772
10983
  try {
10773
10984
  const { toAddress: toAddress2, amount, comment = "", bounce = false } = params;
@@ -10788,7 +10999,7 @@ async function sendTon(params) {
10788
10999
  workchain: 0,
10789
11000
  publicKey: keyPair.publicKey
10790
11001
  });
10791
- const endpoint = await getHttpEndpoint3({ network: "mainnet" });
11002
+ const endpoint = await getCachedHttpEndpoint();
10792
11003
  const client = new TonClient3({ endpoint });
10793
11004
  const contract = client.open(wallet);
10794
11005
  const seqno = await contract.getSeqno();
@@ -11477,12 +11688,16 @@ var VerificationPoller = class {
11477
11688
  * Handle verification timeout
11478
11689
  */
11479
11690
  async handleTimeout(deal) {
11480
- this.db.prepare(
11691
+ const r3 = this.db.prepare(
11481
11692
  `UPDATE deals SET
11482
11693
  status = 'failed',
11483
11694
  notes = 'Payment verification timeout'
11484
- WHERE id = ?`
11695
+ WHERE id = ? AND status = 'payment_claimed'`
11485
11696
  ).run(deal.dealId);
11697
+ if (r3.changes !== 1) {
11698
+ this.retryMap.delete(deal.dealId);
11699
+ return;
11700
+ }
11486
11701
  if (deal.inlineMessageId) {
11487
11702
  const { text, buttons } = buildFailedMessage(
11488
11703
  deal,
@@ -11944,6 +12159,7 @@ function openDealsDb() {
11944
12159
  CREATE INDEX IF NOT EXISTS idx_deals_chat ON deals(chat_id);
11945
12160
  CREATE INDEX IF NOT EXISTS idx_deals_inline_msg ON deals(inline_message_id) WHERE inline_message_id IS NOT NULL;
11946
12161
  CREATE INDEX IF NOT EXISTS idx_deals_payment_claimed ON deals(payment_claimed_at) WHERE payment_claimed_at IS NOT NULL;
12162
+ CREATE INDEX IF NOT EXISTS idx_deals_expires ON deals(expires_at) WHERE status IN ('proposed', 'accepted');
11947
12163
 
11948
12164
  CREATE TABLE IF NOT EXISTS user_trade_stats (
11949
12165
  telegram_id INTEGER PRIMARY KEY,
@@ -14364,7 +14580,6 @@ var telegramCreateScheduledTaskExecutor = async (params, context) => {
14364
14580
  }
14365
14581
  const { getTaskStore } = await import("./tasks-M3QDPTGY.js");
14366
14582
  const taskStore = getTaskStore(context.db);
14367
- const MAX_DEPENDENTS_PER_TASK = 10;
14368
14583
  if (dependsOn && dependsOn.length > 0) {
14369
14584
  for (const parentId of dependsOn) {
14370
14585
  const existingDependents = taskStore.getDependents(parentId);
@@ -14867,7 +15082,6 @@ import { Type as Type75 } from "@sinclair/typebox";
14867
15082
  import { mnemonicToPrivateKey as mnemonicToPrivateKey3 } from "@ton/crypto";
14868
15083
  import { WalletContractV5R1 as WalletContractV5R13, TonClient as TonClient4, toNano as toNano2, internal as internal2 } from "@ton/ton";
14869
15084
  import { Address as Address3, SendMode as SendMode2 } from "@ton/core";
14870
- import { getHttpEndpoint as getHttpEndpoint4 } from "@orbs-network/ton-access";
14871
15085
  var tonSendTool = {
14872
15086
  name: "ton_send",
14873
15087
  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.",
@@ -14909,7 +15123,7 @@ var tonSendExecutor = async (params, context) => {
14909
15123
  workchain: 0,
14910
15124
  publicKey: keyPair.publicKey
14911
15125
  });
14912
- const endpoint = await getHttpEndpoint4({ network: "mainnet" });
15126
+ const endpoint = await getCachedHttpEndpoint();
14913
15127
  const client = new TonClient4({ endpoint });
14914
15128
  const contract = client.open(wallet);
14915
15129
  const seqno = await contract.getSeqno();
@@ -14949,7 +15163,6 @@ var tonSendExecutor = async (params, context) => {
14949
15163
  import { Type as Type76 } from "@sinclair/typebox";
14950
15164
  import { TonClient as TonClient5, fromNano as fromNano3 } from "@ton/ton";
14951
15165
  import { Address as Address4 } from "@ton/core";
14952
- import { getHttpEndpoint as getHttpEndpoint5 } from "@orbs-network/ton-access";
14953
15166
  var OP_CODES = {
14954
15167
  COMMENT: 0,
14955
15168
  JETTON_TRANSFER: 260734629,
@@ -15025,7 +15238,7 @@ var tonGetTransactionsExecutor = async (params, context) => {
15025
15238
  error: `Invalid address: ${address4}`
15026
15239
  };
15027
15240
  }
15028
- const endpoint = await getHttpEndpoint5({ network: "mainnet" });
15241
+ const endpoint = await getCachedHttpEndpoint();
15029
15242
  const client = new TonClient5({ endpoint });
15030
15243
  const transactions = await client.getTransactions(addressObj, {
15031
15244
  limit: Math.min(limit, 50)
@@ -15169,7 +15382,6 @@ var tonGetTransactionsExecutor = async (params, context) => {
15169
15382
  import { Type as Type77 } from "@sinclair/typebox";
15170
15383
  import { TonClient as TonClient6, fromNano as fromNano4 } from "@ton/ton";
15171
15384
  import { Address as Address5 } from "@ton/core";
15172
- import { getHttpEndpoint as getHttpEndpoint6 } from "@orbs-network/ton-access";
15173
15385
  var OP_CODES2 = {
15174
15386
  COMMENT: 0,
15175
15387
  JETTON_TRANSFER: 260734629,
@@ -15241,7 +15453,7 @@ var tonMyTransactionsExecutor = async (params, context) => {
15241
15453
  };
15242
15454
  }
15243
15455
  const addressObj = Address5.parse(walletData.address);
15244
- const endpoint = await getHttpEndpoint6({ network: "mainnet" });
15456
+ const endpoint = await getCachedHttpEndpoint();
15245
15457
  const client = new TonClient6({ endpoint });
15246
15458
  const transactions = await client.getTransactions(addressObj, {
15247
15459
  limit: Math.min(limit, 50)
@@ -15647,7 +15859,6 @@ import { Type as Type81 } from "@sinclair/typebox";
15647
15859
  import { mnemonicToPrivateKey as mnemonicToPrivateKey4 } from "@ton/crypto";
15648
15860
  import { WalletContractV5R1 as WalletContractV5R14, TonClient as TonClient7, toNano as toNano3, internal as internal3, beginCell } from "@ton/ton";
15649
15861
  import { Address as Address6, SendMode as SendMode3 } from "@ton/core";
15650
- import { getHttpEndpoint as getHttpEndpoint7 } from "@orbs-network/ton-access";
15651
15862
  var DNS_COLLECTION = "EQC3dNlesgVD8YbAazcauIrXBPfiVhMMr5YYk2in0Mtsz0Bz";
15652
15863
  var dnsStartAuctionTool = {
15653
15864
  name: "dns_start_auction",
@@ -15690,7 +15901,7 @@ var dnsStartAuctionExecutor = async (params, context) => {
15690
15901
  workchain: 0,
15691
15902
  publicKey: keyPair.publicKey
15692
15903
  });
15693
- const endpoint = await getHttpEndpoint7({ network: "mainnet" });
15904
+ const endpoint = await getCachedHttpEndpoint();
15694
15905
  const client = new TonClient7({ endpoint });
15695
15906
  const contract = client.open(wallet);
15696
15907
  const seqno = await contract.getSeqno();
@@ -15735,7 +15946,6 @@ import { Type as Type82 } from "@sinclair/typebox";
15735
15946
  import { mnemonicToPrivateKey as mnemonicToPrivateKey5 } from "@ton/crypto";
15736
15947
  import { WalletContractV5R1 as WalletContractV5R15, TonClient as TonClient8, toNano as toNano4, internal as internal4 } from "@ton/ton";
15737
15948
  import { Address as Address7, SendMode as SendMode4 } from "@ton/core";
15738
- import { getHttpEndpoint as getHttpEndpoint8 } from "@orbs-network/ton-access";
15739
15949
  var dnsBidTool = {
15740
15950
  name: "dns_bid",
15741
15951
  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).",
@@ -15808,7 +16018,7 @@ var dnsBidExecutor = async (params, context) => {
15808
16018
  workchain: 0,
15809
16019
  publicKey: keyPair.publicKey
15810
16020
  });
15811
- const endpoint = await getHttpEndpoint8({ network: "mainnet" });
16021
+ const endpoint = await getCachedHttpEndpoint();
15812
16022
  const client = new TonClient8({ endpoint });
15813
16023
  const contract = client.open(wallet);
15814
16024
  const seqno = await contract.getSeqno();
@@ -15853,7 +16063,6 @@ import { Type as Type83 } from "@sinclair/typebox";
15853
16063
  import { mnemonicToPrivateKey as mnemonicToPrivateKey6 } from "@ton/crypto";
15854
16064
  import { WalletContractV5R1 as WalletContractV5R16, TonClient as TonClient9, toNano as toNano5, internal as internal5, beginCell as beginCell2 } from "@ton/ton";
15855
16065
  import { Address as Address8, SendMode as SendMode5 } from "@ton/core";
15856
- import { getHttpEndpoint as getHttpEndpoint9 } from "@orbs-network/ton-access";
15857
16066
  var DNS_CHANGE_RECORD_OP = 1320284409;
15858
16067
  var DNS_SMC_ADDRESS_PREFIX = 40915;
15859
16068
  var WALLET_RECORD_KEY = BigInt(
@@ -15935,7 +16144,7 @@ var dnsLinkExecutor = async (params, context) => {
15935
16144
  workchain: 0,
15936
16145
  publicKey: keyPair.publicKey
15937
16146
  });
15938
- const endpoint = await getHttpEndpoint9({ network: "mainnet" });
16147
+ const endpoint = await getCachedHttpEndpoint();
15939
16148
  const client = new TonClient9({ endpoint });
15940
16149
  const contract = client.open(wallet);
15941
16150
  const seqno = await contract.getSeqno();
@@ -15981,7 +16190,6 @@ import { Type as Type84 } from "@sinclair/typebox";
15981
16190
  import { mnemonicToPrivateKey as mnemonicToPrivateKey7 } from "@ton/crypto";
15982
16191
  import { WalletContractV5R1 as WalletContractV5R17, TonClient as TonClient10, toNano as toNano6, internal as internal6, beginCell as beginCell3 } from "@ton/ton";
15983
16192
  import { Address as Address9, SendMode as SendMode6 } from "@ton/core";
15984
- import { getHttpEndpoint as getHttpEndpoint10 } from "@orbs-network/ton-access";
15985
16193
  var DNS_CHANGE_RECORD_OP2 = 1320284409;
15986
16194
  var WALLET_RECORD_KEY2 = BigInt(
15987
16195
  "0xe8d44050873dba865aa7c170ab4cce64d90839a34dcfd6cf71d14e0205443b1b"
@@ -16048,7 +16256,7 @@ var dnsUnlinkExecutor = async (params, context) => {
16048
16256
  workchain: 0,
16049
16257
  publicKey: keyPair.publicKey
16050
16258
  });
16051
- const endpoint = await getHttpEndpoint10({ network: "mainnet" });
16259
+ const endpoint = await getCachedHttpEndpoint();
16052
16260
  const client = new TonClient10({ endpoint });
16053
16261
  const contract = client.open(wallet);
16054
16262
  const seqno = await contract.getSeqno();
@@ -16208,7 +16416,6 @@ import { Type as Type86 } from "@sinclair/typebox";
16208
16416
  import { mnemonicToPrivateKey as mnemonicToPrivateKey8 } from "@ton/crypto";
16209
16417
  import { WalletContractV5R1 as WalletContractV5R18, TonClient as TonClient11, toNano as toNano19, internal as internal7 } from "@ton/ton";
16210
16418
  import { SendMode as SendMode7 } from "@ton/core";
16211
- import { getHttpEndpoint as getHttpEndpoint11 } from "@orbs-network/ton-access";
16212
16419
 
16213
16420
  // node_modules/@ston-fi/sdk/dist/chunk-HNMPFVZW.js
16214
16421
  import { Address as Address10, address } from "@ton/ton";
@@ -19579,7 +19786,7 @@ var jettonSwapExecutor = async (params, context) => {
19579
19786
  error: `Invalid to_asset address: ${toAddress2}`
19580
19787
  };
19581
19788
  }
19582
- const endpoint = await getHttpEndpoint11({ network: "mainnet" });
19789
+ const endpoint = await getCachedHttpEndpoint();
19583
19790
  const tonClient = new TonClient11({ endpoint });
19584
19791
  const stonApiClient = new StonApiClient();
19585
19792
  console.log(`Simulating swap: ${amount} ${fromAddress} \u2192 ${toAddress2}`);
@@ -19668,7 +19875,6 @@ import { Type as Type87 } from "@sinclair/typebox";
19668
19875
  import { mnemonicToPrivateKey as mnemonicToPrivateKey9 } from "@ton/crypto";
19669
19876
  import { WalletContractV5R1 as WalletContractV5R19, TonClient as TonClient12, toNano as toNano20, internal as internal8 } from "@ton/ton";
19670
19877
  import { Address as Address13, SendMode as SendMode8, beginCell as beginCell15 } from "@ton/core";
19671
- import { getHttpEndpoint as getHttpEndpoint12 } from "@orbs-network/ton-access";
19672
19878
  var JETTON_TRANSFER_OP = 260734629;
19673
19879
  var jettonSendTool = {
19674
19880
  name: "jetton_send",
@@ -19748,7 +19954,7 @@ var jettonSendExecutor = async (params, context) => {
19748
19954
  workchain: 0,
19749
19955
  publicKey: keyPair.publicKey
19750
19956
  });
19751
- const endpoint = await getHttpEndpoint12({ network: "mainnet" });
19957
+ const endpoint = await getCachedHttpEndpoint();
19752
19958
  const client = new TonClient12({ endpoint });
19753
19959
  const walletContract = client.open(wallet);
19754
19960
  const seqno = await walletContract.getSeqno();
@@ -20610,7 +20816,7 @@ var jettonPoolsExecutor = async (params, context) => {
20610
20816
 
20611
20817
  // src/agent/tools/jetton/index.ts
20612
20818
  var tools18 = [
20613
- { tool: jettonSwapTool, executor: jettonSwapExecutor, scope: "dm-only" },
20819
+ { tool: jettonSwapTool, executor: jettonSwapExecutor },
20614
20820
  { tool: jettonSendTool, executor: jettonSendExecutor, scope: "dm-only" },
20615
20821
  { tool: jettonBalancesTool, executor: jettonBalancesExecutor },
20616
20822
  { tool: jettonInfoTool, executor: jettonInfoExecutor },
@@ -20627,7 +20833,6 @@ var tools18 = [
20627
20833
  import { Type as Type96 } from "@sinclair/typebox";
20628
20834
  import { TonClient as TonClient13, toNano as toNano22, fromNano as fromNano5 } from "@ton/ton";
20629
20835
  import { Address as Address14 } from "@ton/core";
20630
- import { getHttpEndpoint as getHttpEndpoint13 } from "@orbs-network/ton-access";
20631
20836
  import { Factory, Asset, PoolType, ReadinessStatus } from "@dedust/sdk";
20632
20837
 
20633
20838
  // src/agent/tools/dedust/constants.ts
@@ -20701,7 +20906,7 @@ var dedustQuoteExecutor = async (params, context) => {
20701
20906
  };
20702
20907
  }
20703
20908
  }
20704
- const endpoint = await getHttpEndpoint13({ network: "mainnet" });
20909
+ const endpoint = await getCachedHttpEndpoint();
20705
20910
  const tonClient = new TonClient13({ endpoint });
20706
20911
  const factory = tonClient.open(
20707
20912
  Factory.createFromAddress(Address14.parse(DEDUST_FACTORY_MAINNET))
@@ -20785,7 +20990,6 @@ import { Type as Type97 } from "@sinclair/typebox";
20785
20990
  import { mnemonicToPrivateKey as mnemonicToPrivateKey10 } from "@ton/crypto";
20786
20991
  import { WalletContractV5R1 as WalletContractV5R110, TonClient as TonClient14, toNano as toNano23, fromNano as fromNano6 } from "@ton/ton";
20787
20992
  import { Address as Address15 } from "@ton/core";
20788
- import { getHttpEndpoint as getHttpEndpoint14 } from "@orbs-network/ton-access";
20789
20993
  import { Factory as Factory2, Asset as Asset2, PoolType as PoolType2, ReadinessStatus as ReadinessStatus2, JettonRoot, VaultJetton } from "@dedust/sdk";
20790
20994
  var dedustSwapTool = {
20791
20995
  name: "dedust_swap",
@@ -20849,7 +21053,7 @@ var dedustSwapExecutor = async (params, context) => {
20849
21053
  };
20850
21054
  }
20851
21055
  }
20852
- const endpoint = await getHttpEndpoint14({ network: "mainnet" });
21056
+ const endpoint = await getCachedHttpEndpoint();
20853
21057
  const tonClient = new TonClient14({ endpoint });
20854
21058
  const factory = tonClient.open(
20855
21059
  Factory2.createFromAddress(Address15.parse(DEDUST_FACTORY_MAINNET))
@@ -21079,7 +21283,7 @@ var dedustPoolsExecutor = async (params, context) => {
21079
21283
 
21080
21284
  // src/agent/tools/dedust/index.ts
21081
21285
  var tools19 = [
21082
- { tool: dedustSwapTool, executor: dedustSwapExecutor, scope: "dm-only" },
21286
+ { tool: dedustSwapTool, executor: dedustSwapExecutor },
21083
21287
  { tool: dedustQuoteTool, executor: dedustQuoteExecutor },
21084
21288
  { tool: dedustPoolsTool, executor: dedustPoolsExecutor }
21085
21289
  ];
@@ -21088,7 +21292,6 @@ var tools19 = [
21088
21292
  import { Type as Type99 } from "@sinclair/typebox";
21089
21293
  import { TonClient as TonClient15, toNano as toNano24, fromNano as fromNano7 } from "@ton/ton";
21090
21294
  import { Address as Address16 } from "@ton/core";
21091
- import { getHttpEndpoint as getHttpEndpoint15 } from "@orbs-network/ton-access";
21092
21295
  import { Factory as Factory3, Asset as Asset3, PoolType as PoolType3, ReadinessStatus as ReadinessStatus3 } from "@dedust/sdk";
21093
21296
  var dexQuoteTool = {
21094
21297
  name: "dex_quote",
@@ -21235,7 +21438,7 @@ async function getDedustQuote(fromAsset, toAsset, amount, slippage, tonClient) {
21235
21438
  var dexQuoteExecutor = async (params, context) => {
21236
21439
  try {
21237
21440
  const { from_asset, to_asset, amount, slippage = 0.01 } = params;
21238
- const endpoint = await getHttpEndpoint15({ network: "mainnet" });
21441
+ const endpoint = await getCachedHttpEndpoint();
21239
21442
  const tonClient = new TonClient15({ endpoint });
21240
21443
  const [stonfiQuote, dedustQuote] = await Promise.all([
21241
21444
  getStonfiQuote(from_asset, to_asset, amount, slippage),
@@ -21347,7 +21550,6 @@ import { Type as Type100 } from "@sinclair/typebox";
21347
21550
  import { mnemonicToPrivateKey as mnemonicToPrivateKey11 } from "@ton/crypto";
21348
21551
  import { WalletContractV5R1 as WalletContractV5R111, TonClient as TonClient16, toNano as toNano25, fromNano as fromNano8 } from "@ton/ton";
21349
21552
  import { Address as Address17, SendMode as SendMode9, internal as internal9 } from "@ton/core";
21350
- import { getHttpEndpoint as getHttpEndpoint16 } from "@orbs-network/ton-access";
21351
21553
  import { Factory as Factory4, Asset as Asset4, PoolType as PoolType4, ReadinessStatus as ReadinessStatus4, JettonRoot as JettonRoot2, VaultJetton as VaultJetton2 } from "@dedust/sdk";
21352
21554
  var dexSwapTool = {
21353
21555
  name: "dex_swap",
@@ -21569,7 +21771,7 @@ var dexSwapExecutor = async (params, context) => {
21569
21771
  if (!isTonOutput && !to_asset.match(/^[EUe][Qq][A-Za-z0-9_-]{46}$/)) {
21570
21772
  return { success: false, error: `Invalid to_asset address: ${to_asset}` };
21571
21773
  }
21572
- const endpoint = await getHttpEndpoint16({ network: "mainnet" });
21774
+ const endpoint = await getCachedHttpEndpoint();
21573
21775
  const tonClient = new TonClient16({ endpoint });
21574
21776
  let stonfiQuote = null;
21575
21777
  let dedustQuote = null;
@@ -21683,7 +21885,7 @@ var dexSwapExecutor = async (params, context) => {
21683
21885
 
21684
21886
  // src/agent/tools/dex/index.ts
21685
21887
  var tools20 = [
21686
- { tool: dexSwapTool, executor: dexSwapExecutor, scope: "dm-only" },
21888
+ { tool: dexSwapTool, executor: dexSwapExecutor },
21687
21889
  { tool: dexQuoteTool, executor: dexQuoteExecutor }
21688
21890
  ];
21689
21891
 
@@ -22302,7 +22504,6 @@ var workspaceWriteExecutor = async (params, _context) => {
22302
22504
  writeContent = content;
22303
22505
  }
22304
22506
  const contentSize = Buffer.byteLength(writeContent);
22305
- const MAX_WRITE_SIZE = 50 * 1024 * 1024;
22306
22507
  if (contentSize > MAX_WRITE_SIZE) {
22307
22508
  return {
22308
22509
  success: false,
@@ -22918,10 +23119,10 @@ async function sendPayout(playerAddress, amount, message) {
22918
23119
  const endpoint = await getCachedHttpEndpoint();
22919
23120
  const client = new TonClient17({ endpoint });
22920
23121
  const contract = client.open(wallet);
22921
- const seqno = await withBlockchainRetry(() => contract.getSeqno(), "getSeqno");
22922
- await withBlockchainRetry(
22923
- () => contract.sendTransfer({
22924
- seqno,
23122
+ const seqno = await withBlockchainRetry(async () => {
23123
+ const seq = await contract.getSeqno();
23124
+ await contract.sendTransfer({
23125
+ seqno: seq,
22925
23126
  secretKey: keyPair.secretKey,
22926
23127
  sendMode: SendMode10.PAY_GAS_SEPARATELY,
22927
23128
  messages: [
@@ -22932,9 +23133,9 @@ async function sendPayout(playerAddress, amount, message) {
22932
23133
  bounce: false
22933
23134
  })
22934
23135
  ]
22935
- }),
22936
- "sendTransfer"
22937
- );
23136
+ });
23137
+ return seq;
23138
+ }, "payout");
22938
23139
  const walletAddr = wallet.address.toString({ bounceable: false });
22939
23140
  const walletShort = walletAddr.slice(-8);
22940
23141
  const timestamp = Date.now();
@@ -23586,7 +23787,7 @@ var MarketScraperService = class {
23586
23787
  this.isScrapingInProgress = true;
23587
23788
  console.log("\u{1F504} Starting full market scrape...");
23588
23789
  try {
23589
- const { runScraper } = await import("./scraper-DSAYK6QJ.js");
23790
+ const { runScraper } = await import("./scraper-2O6Z3ZHT.js");
23590
23791
  const result = await runScraper({
23591
23792
  workers: 4,
23592
23793
  limit: 0
@@ -23930,6 +24131,85 @@ function loadModules(registry, config, db3) {
23930
24131
  return loaded;
23931
24132
  }
23932
24133
 
24134
+ // src/agent/tools/module-permissions.ts
24135
+ var PROTECTED_MODULES = /* @__PURE__ */ new Set(["telegram", "memory"]);
24136
+ var ModulePermissions = class {
24137
+ db;
24138
+ /** chatId → module → level */
24139
+ cache = /* @__PURE__ */ new Map();
24140
+ constructor(db3) {
24141
+ this.db = db3;
24142
+ this.ensureTable();
24143
+ this.loadAll();
24144
+ }
24145
+ ensureTable() {
24146
+ this.db.exec(`
24147
+ CREATE TABLE IF NOT EXISTS group_modules (
24148
+ chat_id TEXT NOT NULL,
24149
+ module TEXT NOT NULL,
24150
+ level TEXT NOT NULL CHECK(level IN ('open', 'admin', 'disabled')),
24151
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
24152
+ updated_by INTEGER,
24153
+ PRIMARY KEY (chat_id, module)
24154
+ )
24155
+ `);
24156
+ }
24157
+ loadAll() {
24158
+ const rows = this.db.prepare("SELECT chat_id, module, level FROM group_modules").all();
24159
+ for (const row of rows) {
24160
+ let chatMap = this.cache.get(row.chat_id);
24161
+ if (!chatMap) {
24162
+ chatMap = /* @__PURE__ */ new Map();
24163
+ this.cache.set(row.chat_id, chatMap);
24164
+ }
24165
+ chatMap.set(row.module, row.level);
24166
+ }
24167
+ }
24168
+ /** Get the effective level for a module in a chat. Default: "open". */
24169
+ getLevel(chatId, module) {
24170
+ return this.cache.get(chatId)?.get(module) ?? "open";
24171
+ }
24172
+ /** Set the level for a module in a chat. Throws if module is protected. */
24173
+ setLevel(chatId, module, level, userId) {
24174
+ if (PROTECTED_MODULES.has(module)) {
24175
+ throw new Error(`Module "${module}" est prot\xE9g\xE9`);
24176
+ }
24177
+ if (level === "open") {
24178
+ this.db.prepare("DELETE FROM group_modules WHERE chat_id = ? AND module = ?").run(chatId, module);
24179
+ this.cache.get(chatId)?.delete(module);
24180
+ } else {
24181
+ this.db.prepare(
24182
+ `INSERT OR REPLACE INTO group_modules (chat_id, module, level, updated_at, updated_by)
24183
+ VALUES (?, ?, ?, unixepoch(), ?)`
24184
+ ).run(chatId, module, level, userId ?? null);
24185
+ let chatMap = this.cache.get(chatId);
24186
+ if (!chatMap) {
24187
+ chatMap = /* @__PURE__ */ new Map();
24188
+ this.cache.set(chatId, chatMap);
24189
+ }
24190
+ chatMap.set(module, level);
24191
+ }
24192
+ }
24193
+ /** Reset a single module to default ("open"). */
24194
+ resetModule(chatId, module) {
24195
+ this.db.prepare("DELETE FROM group_modules WHERE chat_id = ? AND module = ?").run(chatId, module);
24196
+ this.cache.get(chatId)?.delete(module);
24197
+ }
24198
+ /** Reset all modules for a chat to default ("open"). */
24199
+ resetAll(chatId) {
24200
+ this.db.prepare("DELETE FROM group_modules WHERE chat_id = ?").run(chatId);
24201
+ this.cache.delete(chatId);
24202
+ }
24203
+ /** Get all non-default overrides for a chat. */
24204
+ getOverrides(chatId) {
24205
+ return this.cache.get(chatId) ?? /* @__PURE__ */ new Map();
24206
+ }
24207
+ /** Check if a module is protected (always open, cannot be changed). */
24208
+ isProtected(module) {
24209
+ return PROTECTED_MODULES.has(module);
24210
+ }
24211
+ };
24212
+
23933
24213
  // src/index.ts
23934
24214
  var TonnetApp = class {
23935
24215
  config;
@@ -23977,6 +24257,8 @@ var TonnetApp = class {
23977
24257
  });
23978
24258
  const db3 = getDatabase().getDb();
23979
24259
  this.modules = loadModules(this.toolRegistry, this.config, db3);
24260
+ const modulePermissions = new ModulePermissions(db3);
24261
+ this.toolRegistry.setPermissions(modulePermissions);
23980
24262
  this.toolCount = this.toolRegistry.count;
23981
24263
  this.messageHandler = new MessageHandler(
23982
24264
  this.bridge,
@@ -23989,7 +24271,13 @@ var TonnetApp = class {
23989
24271
  this.config
23990
24272
  // Pass full config for vision tool API key access
23991
24273
  );
23992
- this.adminHandler = new AdminHandler(this.bridge, this.config.telegram, this.agent);
24274
+ this.adminHandler = new AdminHandler(
24275
+ this.bridge,
24276
+ this.config.telegram,
24277
+ this.agent,
24278
+ modulePermissions,
24279
+ this.toolRegistry
24280
+ );
23993
24281
  }
23994
24282
  /**
23995
24283
  * Start the agent
@@ -24028,7 +24316,7 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24028
24316
  `\u26A0\uFE0F Tool count (${this.toolCount}) exceeds ${providerMeta.displayName} limit (${providerMeta.toolLimit})`
24029
24317
  );
24030
24318
  }
24031
- const { migrateSessionsToDb } = await import("./migrate-7XOO67O5.js");
24319
+ const { migrateSessionsToDb } = await import("./migrate-6BQZVOBN.js");
24032
24320
  migrateSessionsToDb();
24033
24321
  const { cleanupOldTranscripts } = await import("./transcript-DF2Y6CFY.js");
24034
24322
  cleanupOldTranscripts(30);
@@ -24149,7 +24437,8 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24149
24437
  const response = await this.adminHandler.handleCommand(
24150
24438
  adminCmd,
24151
24439
  message.chatId,
24152
- message.senderId
24440
+ message.senderId,
24441
+ message.isGroup
24153
24442
  );
24154
24443
  await this.bridge.sendMessage({
24155
24444
  chatId: message.chatId,
@@ -24177,8 +24466,8 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24177
24466
  }
24178
24467
  const taskId = match[1];
24179
24468
  const { getTaskStore } = await import("./tasks-M3QDPTGY.js");
24180
- const { executeScheduledTask } = await import("./task-executor-MNI4VIZL.js");
24181
- const { getDatabase: getDatabase2 } = await import("./memory-OEOLPEXP.js");
24469
+ const { executeScheduledTask } = await import("./task-executor-4SVB72GR.js");
24470
+ const { getDatabase: getDatabase2 } = await import("./memory-PDEJIPK6.js");
24182
24471
  const db3 = getDatabase2().getDb();
24183
24472
  const taskStore = getTaskStore(db3);
24184
24473
  const task = taskStore.getTask(taskId);
@@ -24244,7 +24533,7 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24244
24533
  taskStore.completeTask(taskId, response.content);
24245
24534
  console.log(`\u2705 Executed scheduled task ${taskId}: ${task.description}`);
24246
24535
  if (!this.dependencyResolver) {
24247
- const { TaskDependencyResolver } = await import("./task-dependency-resolver-CWG6DTU4.js");
24536
+ const { TaskDependencyResolver } = await import("./task-dependency-resolver-WNYOTWWM.js");
24248
24537
  this.dependencyResolver = new TaskDependencyResolver(taskStore, this.bridge);
24249
24538
  }
24250
24539
  await this.dependencyResolver.onTaskComplete(taskId);
@@ -24252,8 +24541,8 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24252
24541
  console.error("Error handling scheduled task:", error);
24253
24542
  try {
24254
24543
  const { getTaskStore } = await import("./tasks-M3QDPTGY.js");
24255
- const { TaskDependencyResolver } = await import("./task-dependency-resolver-CWG6DTU4.js");
24256
- const { getDatabase: getDatabase2 } = await import("./memory-OEOLPEXP.js");
24544
+ const { TaskDependencyResolver } = await import("./task-dependency-resolver-WNYOTWWM.js");
24545
+ const { getDatabase: getDatabase2 } = await import("./memory-PDEJIPK6.js");
24257
24546
  const db3 = getDatabase2().getDb();
24258
24547
  const taskStore = getTaskStore(db3);
24259
24548
  const match = message.text.match(/^\[TASK:([^\]]+)\]/);
@@ -24281,6 +24570,7 @@ ${blue} \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
24281
24570
  await mod.stop?.();
24282
24571
  }
24283
24572
  await this.bridge.disconnect();
24573
+ closeDatabase();
24284
24574
  }
24285
24575
  };
24286
24576
  async function main(configPath) {
@@ -24292,14 +24582,19 @@ async function main(configPath) {
24292
24582
  console.error("\u{1F4A5} Uncaught exception:", error);
24293
24583
  process.exit(1);
24294
24584
  });
24295
- process.on("SIGINT", async () => {
24296
- await app.stop();
24297
- process.exit(0);
24298
- });
24299
- process.on("SIGTERM", async () => {
24585
+ const gracefulShutdown = async () => {
24586
+ const { SHUTDOWN_TIMEOUT_MS } = await import("./timeouts-ZAK6NELA.js");
24587
+ const forceExit = setTimeout(() => {
24588
+ console.error("\u26A0\uFE0F Shutdown timed out, forcing exit");
24589
+ process.exit(1);
24590
+ }, SHUTDOWN_TIMEOUT_MS);
24591
+ forceExit.unref();
24300
24592
  await app.stop();
24593
+ clearTimeout(forceExit);
24301
24594
  process.exit(0);
24302
- });
24595
+ };
24596
+ process.on("SIGINT", gracefulShutdown);
24597
+ process.on("SIGTERM", gracefulShutdown);
24303
24598
  await app.start();
24304
24599
  }
24305
24600
  if (import.meta.url === `file://${process.argv[1]}`) {