omnius 1.0.42 → 1.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -25710,7 +25710,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
25710
25710
  bits2int_modN: "function"
25711
25711
  });
25712
25712
  ecdsaOpts = Object.assign({}, ecdsaOpts);
25713
- const randomBytes26 = ecdsaOpts.randomBytes || randomBytes7;
25713
+ const randomBytes27 = ecdsaOpts.randomBytes || randomBytes7;
25714
25714
  const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
25715
25715
  const { Fp, Fn } = Point;
25716
25716
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
@@ -25852,7 +25852,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
25852
25852
  throw new Error("invalid private key");
25853
25853
  const seedArgs = [int2octets(d2), int2octets(h1int)];
25854
25854
  if (extraEntropy != null && extraEntropy !== false) {
25855
- const e2 = extraEntropy === true ? randomBytes26(lengths.secretKey) : extraEntropy;
25855
+ const e2 = extraEntropy === true ? randomBytes27(lengths.secretKey) : extraEntropy;
25856
25856
  seedArgs.push(abytes(e2, void 0, "extraEntropy"));
25857
25857
  }
25858
25858
  const seed = concatBytes(...seedArgs);
@@ -62362,9 +62362,9 @@ var init_cookies = __esm({
62362
62362
 
62363
62363
  // ../node_modules/@libp2p/http-peer-id-auth/dist/src/utils.js
62364
62364
  function generateChallenge() {
62365
- const randomBytes26 = new Uint8Array(32);
62366
- crypto.getRandomValues(randomBytes26);
62367
- return toString2(randomBytes26, "base64urlpad");
62365
+ const randomBytes27 = new Uint8Array(32);
62366
+ crypto.getRandomValues(randomBytes27);
62367
+ return toString2(randomBytes27, "base64urlpad");
62368
62368
  }
62369
62369
  function encodeAuthParams(params) {
62370
62370
  const encodedParams = Object.entries(params).map(([key, value2]) => `${key}="${value2}"`).join(", ");
@@ -234560,7 +234560,7 @@ var require_websocket2 = __commonJS({
234560
234560
  var http6 = __require("http");
234561
234561
  var net5 = __require("net");
234562
234562
  var tls2 = __require("tls");
234563
- var { randomBytes: randomBytes26, createHash: createHash28 } = __require("crypto");
234563
+ var { randomBytes: randomBytes27, createHash: createHash28 } = __require("crypto");
234564
234564
  var { Duplex: Duplex3, Readable } = __require("stream");
234565
234565
  var { URL: URL3 } = __require("url");
234566
234566
  var PerMessageDeflate3 = require_permessage_deflate2();
@@ -235090,7 +235090,7 @@ var require_websocket2 = __commonJS({
235090
235090
  }
235091
235091
  }
235092
235092
  const defaultPort = isSecure ? 443 : 80;
235093
- const key = randomBytes26(16).toString("base64");
235093
+ const key = randomBytes27(16).toString("base64");
235094
235094
  const request = isSecure ? https4.request : http6.request;
235095
235095
  const protocolSet = /* @__PURE__ */ new Set();
235096
235096
  let perMessageDeflate;
@@ -553818,7 +553818,7 @@ var require_websocket3 = __commonJS({
553818
553818
  var http6 = __require("http");
553819
553819
  var net5 = __require("net");
553820
553820
  var tls2 = __require("tls");
553821
- var { randomBytes: randomBytes26, createHash: createHash28 } = __require("crypto");
553821
+ var { randomBytes: randomBytes27, createHash: createHash28 } = __require("crypto");
553822
553822
  var { Duplex: Duplex3, Readable } = __require("stream");
553823
553823
  var { URL: URL3 } = __require("url");
553824
553824
  var PerMessageDeflate3 = require_permessage_deflate3();
@@ -554348,7 +554348,7 @@ var require_websocket3 = __commonJS({
554348
554348
  }
554349
554349
  }
554350
554350
  const defaultPort = isSecure ? 443 : 80;
554351
- const key = randomBytes26(16).toString("base64");
554351
+ const key = randomBytes27(16).toString("base64");
554352
554352
  const request = isSecure ? https4.request : http6.request;
554353
554353
  const protocolSet = /* @__PURE__ */ new Set();
554354
554354
  let perMessageDeflate;
@@ -555836,6 +555836,9 @@ var init_command_registry = __esm({
555836
555836
  ["/telegram personal <user_id> <limit>", "Fetch last messages from a user's profile personal chat"],
555837
555837
  ["/telegram access get <managed_bot_user_id>", "Show managed-bot access restriction settings"],
555838
555838
  ["/telegram access set <managed_bot_user_id> <restricted|open> [added_user_ids]", "Set managed-bot access restrictions"],
555839
+ ["/telegram tools [group <chat>|panel <chat> [policy_chat]]", "Show unified Telegram tool policy or send inline admin toggle buttons"],
555840
+ ["/telegram delete-message <chat> <message> [reason]", "Delete one Telegram message after policy and Bot API rights checks"],
555841
+ ["/telegram delete-messages <chat> <message,message> [reason]", "Delete multiple Telegram messages after policy and Bot API rights checks"],
555839
555842
  ["/telegram delete-reaction <chat> <message> [--user <id>|--actor <chat>]", "Delete a Telegram message reaction"],
555840
555843
  ["/telegram delete-reactions <chat> [--user <id>|--actor <chat>]", "Delete recent Telegram message reactions by user or chat"],
555841
555844
  ["/platforms", "Show gateway platform state"],
@@ -564917,7 +564920,7 @@ function setTerminalTitle(task, version4) {
564917
564920
  const title = task ? `${task.slice(0, 60)} · ${ver}` : ver;
564918
564921
  process.stdout.write(`\x1B]2;${title}\x07`);
564919
564922
  }
564920
- var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, PANEL_BG_SEQ, CONTENT_BG_SEQ, BOX_FG, TEXT_PRIMARY, TEXT_DIM, BOX_TL, BOX_TR, BOX_BL, BOX_BR, BOX_H, BOX_V, _globalFooterLock, RESET2, CURSOR_BLINK_BLOCK, _isWindows, StatusBar;
564923
+ var EXPERT_TOOL_BASELINES, CONTEXT_SWITCH_OVERHEAD, TURN_PLANNING_OVERHEAD, DEFAULT_TOOL_BASELINE, CODE_READ_CHARS_PER_SEC, PROSE_READ_CHARS_PER_SEC, MIN_CONTENT_FOR_READING, CODE_CONTENT_TOOLS, PROSE_CONTENT_TOOLS, HumanSpeedTracker, PANEL_BG_SEQ, CONTENT_BG_SEQ, BOX_FG, TEXT_PRIMARY, TEXT_DIM, NO_SUB_AGENTS_HEADER_LABEL, BOX_TL, BOX_TR, BOX_BL, BOX_BR, BOX_H, BOX_V, _globalFooterLock, RESET2, CURSOR_BLINK_BLOCK, _isWindows, StatusBar;
564921
564924
  var init_status_bar = __esm({
564922
564925
  "packages/cli/src/tui/status-bar.ts"() {
564923
564926
  "use strict";
@@ -565089,6 +565092,7 @@ var init_status_bar = __esm({
565089
565092
  BOX_FG = tuiBoxFg();
565090
565093
  TEXT_PRIMARY = tuiTextPrimary() < 0 ? 252 : tuiTextPrimary();
565091
565094
  TEXT_DIM = tuiTextDim();
565095
+ NO_SUB_AGENTS_HEADER_LABEL = " no sub-agents ";
565092
565096
  BOX_TL = "╭";
565093
565097
  BOX_TR = "╮";
565094
565098
  BOX_BL = "╰";
@@ -565538,8 +565542,8 @@ var init_status_bar = __esm({
565538
565542
  }
565539
565543
  } else {
565540
565544
  sysItems.push({
565541
- render: () => `\x1B[38;5;${TEXT_DIM}m no sub-agents `,
565542
- w: 14
565545
+ render: () => `\x1B[38;5;${TEXT_DIM}m${NO_SUB_AGENTS_HEADER_LABEL}`,
565546
+ w: NO_SUB_AGENTS_HEADER_LABEL.length
565543
565547
  });
565544
565548
  }
565545
565549
  const sysSeparatorOffset = sysItems.reduce((sum, item) => sum + item.w, 0);
@@ -565622,7 +565626,10 @@ var init_status_bar = __esm({
565622
565626
  return identity3.separatorOffsets.map((offset) => chrome.contentStartCol + offset).filter((col) => col > 1 && col < termWidth);
565623
565627
  }
565624
565628
  if (panel.meta.kind === "system" && this._sysSeparatorOffset !== null) {
565625
- const col = chrome.contentStartCol + this._sysSeparatorOffset;
565629
+ const rendered = stripAnsi(panel.render(chrome.innerWidth));
565630
+ const renderedOffset = Array.from(rendered).indexOf("│");
565631
+ const offset = renderedOffset >= 0 ? renderedOffset : this._sysSeparatorOffset;
565632
+ const col = chrome.contentStartCol + offset;
565626
565633
  if (col > 1 && col < termWidth) return [col];
565627
565634
  }
565628
565635
  return [];
@@ -565703,7 +565710,7 @@ var init_status_bar = __esm({
565703
565710
  zones.push({ w: base3.length + 1, id: view.id, render: () => "" });
565704
565711
  }
565705
565712
  } else {
565706
- zones.push({ w: 14, render: () => "" });
565713
+ zones.push({ w: NO_SUB_AGENTS_HEADER_LABEL.length, render: () => "" });
565707
565714
  }
565708
565715
  zones.push({ w: 2, render: () => "" });
565709
565716
  const voiceLabel = this._voiceActive ? ` ${this._voiceModelId || "voice"} ` : " voice ";
@@ -582243,6 +582250,10 @@ function formatTelegramJsonResult(value2) {
582243
582250
  if (value2 === null || value2 === void 0) return "No data returned.";
582244
582251
  return JSON.stringify(value2, null, 2).slice(0, 3900);
582245
582252
  }
582253
+ function parseTelegramMessageIdList(value2) {
582254
+ if (!value2) return [];
582255
+ return [...new Set(value2.split(",").map((part) => Number(part.trim())).filter((num) => Number.isFinite(num)))];
582256
+ }
582246
582257
  async function runSudoScript(ctx3, script) {
582247
582258
  const stdinAny = process.stdin;
582248
582259
  const hadRaw = !!(stdinAny && stdinAny.isTTY && stdinAny.isRaw);
@@ -582736,11 +582747,11 @@ async function handleSlashCommand(input, ctx3) {
582736
582747
  }
582737
582748
  if (action === "new") {
582738
582749
  try {
582739
- const { randomBytes: randomBytes26 } = await import("node:crypto");
582750
+ const { randomBytes: randomBytes27 } = await import("node:crypto");
582740
582751
  const { homedir: homedir48 } = await import("node:os");
582741
582752
  const { mkdirSync: mkdirSync79, writeFileSync: writeFileSync71 } = await import("node:fs");
582742
582753
  const { join: join144 } = await import("node:path");
582743
- const newKey = randomBytes26(16).toString("hex");
582754
+ const newKey = randomBytes27(16).toString("hex");
582744
582755
  process.env["OMNIUS_API_KEY"] = newKey;
582745
582756
  const dir = join144(homedir48(), ".omnius");
582746
582757
  mkdirSync79(dir, { recursive: true });
@@ -582997,8 +583008,8 @@ async function handleSlashCommand(input, ctx3) {
582997
583008
  }
582998
583009
  process.env["OMNIUS_ACCESS"] = val2;
582999
583010
  if (val2 === "any" && !process.env["OMNIUS_API_KEY"]) {
583000
- const { randomBytes: randomBytes26 } = await import("node:crypto");
583001
- const apiKey = randomBytes26(16).toString("hex");
583011
+ const { randomBytes: randomBytes27 } = await import("node:crypto");
583012
+ const apiKey = randomBytes27(16).toString("hex");
583002
583013
  process.env["OMNIUS_API_KEY"] = apiKey;
583003
583014
  renderInfo(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
583004
583015
  renderInfo(
@@ -583117,8 +583128,8 @@ async function handleSlashCommand(input, ctx3) {
583117
583128
  }
583118
583129
  process.env["OMNIUS_ACCESS"] = val;
583119
583130
  if (val === "any" && !process.env["OMNIUS_API_KEY"]) {
583120
- const { randomBytes: randomBytes26 } = await import("node:crypto");
583121
- const apiKey = randomBytes26(16).toString("hex");
583131
+ const { randomBytes: randomBytes27 } = await import("node:crypto");
583132
+ const apiKey = randomBytes27(16).toString("hex");
583122
583133
  process.env["OMNIUS_API_KEY"] = apiKey;
583123
583134
  renderInfo(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
583124
583135
  renderInfo(
@@ -587363,6 +587374,73 @@ sleep 1
587363
587374
  }
587364
587375
  return "handled";
587365
587376
  }
587377
+ if (parts[0] === "tools" || parts[0] === "tooling") {
587378
+ if (parts[1] === "group" || parts[1] === "chat") {
587379
+ const chatId = parseTelegramChatTarget(parts[2]);
587380
+ if (!chatId) {
587381
+ renderWarning("Usage: /telegram tools group <chat>");
587382
+ return "handled";
587383
+ }
587384
+ const policy2 = ctx3.telegramGetToolPolicy?.() ?? {};
587385
+ const override = policy2.chatOverrides?.[String(chatId)] ?? {};
587386
+ renderInfo(`Unified Telegram tool policy override for chat ${String(chatId)}:`);
587387
+ renderInfo(formatTelegramJsonResult(override));
587388
+ renderInfo("Use /telegram tools panel <admin_dm_chat_id> <policy_chat> to edit this scope with buttons.");
587389
+ return "handled";
587390
+ }
587391
+ if (parts[1] === "panel" || parts[1] === "buttons") {
587392
+ const panelChatId = parseTelegramChatTarget(parts[2]);
587393
+ const policyChatId = parts[3] && parts[3] !== "global" ? parseTelegramChatTarget(parts[3]) : void 0;
587394
+ if (!panelChatId) {
587395
+ renderWarning("Usage: /telegram tools panel <panel_chat> [policy_chat|global]");
587396
+ return "handled";
587397
+ }
587398
+ try {
587399
+ const result = await ctx3.telegramSendToolPanel?.(panelChatId, policyChatId ?? void 0);
587400
+ renderInfo(formatTelegramJsonResult(result));
587401
+ } catch (err) {
587402
+ renderError(`Telegram tools panel failed: ${err instanceof Error ? err.message : String(err)}`);
587403
+ }
587404
+ return "handled";
587405
+ }
587406
+ const policy = ctx3.telegramGetToolPolicy?.();
587407
+ renderInfo("Unified Telegram tool policy:");
587408
+ renderInfo(formatTelegramJsonResult(policy ?? {}));
587409
+ renderInfo("Use /telegram tools panel <admin_dm_chat_id> [policy_chat] to send inline toggle buttons.");
587410
+ return "handled";
587411
+ }
587412
+ if (parts[0] === "delete-message") {
587413
+ const chatId = parseTelegramChatTarget(parts[1]);
587414
+ const messageId = Number(parts[2]);
587415
+ const reason = parts.slice(3).join(" ").trim() || "TUI admin delete-message command";
587416
+ if (!chatId || !Number.isFinite(messageId)) {
587417
+ renderWarning("Usage: /telegram delete-message <chat> <message_id> [reason]");
587418
+ return "handled";
587419
+ }
587420
+ try {
587421
+ const result = await ctx3.telegramDeleteMessage?.(chatId, messageId, reason);
587422
+ renderInfo(formatTelegramJsonResult(result));
587423
+ } catch (err) {
587424
+ renderError(`Telegram delete message failed: ${err instanceof Error ? err.message : String(err)}`);
587425
+ }
587426
+ return "handled";
587427
+ }
587428
+ if (parts[0] === "delete-messages") {
587429
+ const chatId = parseTelegramChatTarget(parts[1]);
587430
+ const messageIds = parseTelegramMessageIdList(parts[2]);
587431
+ const reason = parts.slice(3).join(" ").trim() || "TUI admin delete-messages command";
587432
+ if (!chatId || messageIds.length === 0) {
587433
+ renderWarning("Usage: /telegram delete-messages <chat> <message_id,message_id> [reason]");
587434
+ return "handled";
587435
+ }
587436
+ try {
587437
+ const result = await ctx3.telegramDeleteMessages?.(chatId, messageIds, reason);
587438
+ renderInfo(formatTelegramJsonResult(result));
587439
+ } catch (err) {
587440
+ renderError(`Telegram delete messages failed: ${err instanceof Error ? err.message : String(err)}`);
587441
+ }
587442
+ return "handled";
587443
+ }
587366
587444
  if (parts[0] === "delete-reaction") {
587367
587445
  const chatId = parseTelegramChatTarget(parts[1]);
587368
587446
  const messageId = Number(parts[2]);
@@ -587473,6 +587551,9 @@ sleep 1
587473
587551
  renderInfo(" /telegram admins <chat> [--bots] List chat admins");
587474
587552
  renderInfo(" /telegram personal <user_id> <limit> Fetch profile personal chat messages");
587475
587553
  renderInfo(" /telegram access get|set <managed_bot_user_id> ... Manage bot access restriction");
587554
+ renderInfo(" /telegram tools [group <chat>|panel <chat> [policy_chat]] Show/send unified tool policy buttons");
587555
+ renderInfo(" /telegram delete-message <chat> <msg> [reason] Delete one message");
587556
+ renderInfo(" /telegram delete-messages <chat> <msg,msg> [reason] Delete multiple messages");
587476
587557
  renderInfo(" /telegram delete-reaction <chat> <msg> --user <id> Delete a reaction");
587477
587558
  renderInfo(" /telegram delete-reactions <chat> --user <id> Delete recent reactions");
587478
587559
  renderInfo(" Add --local to scope settings to this project only");
@@ -588338,7 +588419,9 @@ async function showPlatformOnboardingMenu(ctx3, id) {
588338
588419
  } else if (result.key === "telegram-start") {
588339
588420
  const settings = ctx3.getTelegramSettings?.() ?? {};
588340
588421
  if (!settings.key) renderWarning("No Telegram bot token configured.");
588341
- else await ctx3.telegramStart?.(settings.key, settings.admin);
588422
+ else if (ctx3.isTelegramActive?.()) {
588423
+ renderWarning("Telegram bridge already active. Use /telegram stop before restarting.");
588424
+ } else await ctx3.telegramStart?.(settings.key, settings.admin);
588342
588425
  } else if (result.key === "telegram-stop") {
588343
588426
  ctx3.telegramStop?.();
588344
588427
  }
@@ -593675,11 +593758,11 @@ var init_commands = __esm({
593675
593758
  process.env["OMNIUS_ACCESS"] = val;
593676
593759
  if (val === "any" && !process.env["OMNIUS_API_KEY"]) {
593677
593760
  try {
593678
- const { randomBytes: randomBytes26 } = await import("node:crypto");
593761
+ const { randomBytes: randomBytes27 } = await import("node:crypto");
593679
593762
  const { homedir: homedir48 } = await import("node:os");
593680
593763
  const { mkdirSync: mkdirSync79, writeFileSync: writeFileSync71 } = await import("node:fs");
593681
593764
  const { join: join144 } = await import("node:path");
593682
- const apiKey = randomBytes26(16).toString("hex");
593765
+ const apiKey = randomBytes27(16).toString("hex");
593683
593766
  process.env["OMNIUS_API_KEY"] = apiKey;
593684
593767
  const dir = join144(homedir48(), ".omnius");
593685
593768
  mkdirSync79(dir, { recursive: true });
@@ -600534,6 +600617,7 @@ var init_tool_policy = __esm({
600534
600617
  "reminders",
600535
600618
  "explore_tools",
600536
600619
  "telegram_media_recent",
600620
+ "telegram",
600537
600621
  "generate_image",
600538
600622
  "generate_audio",
600539
600623
  "generate_tts",
@@ -600566,6 +600650,7 @@ var init_tool_policy = __esm({
600566
600650
  "reminders",
600567
600651
  "explore_tools",
600568
600652
  "telegram_media_recent",
600653
+ "telegram",
600569
600654
  "generate_image",
600570
600655
  "generate_audio",
600571
600656
  "generate_tts",
@@ -602819,7 +602904,7 @@ var init_vision_ingress = __esm({
602819
602904
  import { mkdirSync as mkdirSync63, existsSync as existsSync108, unlinkSync as unlinkSync21, readdirSync as readdirSync37, statSync as statSync36, statfsSync as statfsSync3, readFileSync as readFileSync88, writeFileSync as writeFileSync57 } from "node:fs";
602820
602905
  import { join as join123, resolve as resolve41, basename as basename27, relative as relative13, isAbsolute as isAbsolute7, extname as extname16 } from "node:path";
602821
602906
  import { writeFile as writeFileAsync } from "node:fs/promises";
602822
- import { createHash as createHash23, randomInt } from "node:crypto";
602907
+ import { createHash as createHash23, randomBytes as randomBytes22, randomInt } from "node:crypto";
602823
602908
  function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
602824
602909
  const cleaned = stripTelegramHiddenThinking(text).replace(/```(?:json)?/gi, "").replace(/```/g, "").trim();
602825
602910
  const jsonText = cleaned.startsWith("{") ? cleaned : cleaned.match(/\{[\s\S]*\}/)?.[0] ?? "";
@@ -603803,6 +603888,23 @@ function normalizeTelegramUpdate(update2) {
603803
603888
  sourceUpdateType
603804
603889
  };
603805
603890
  }
603891
+ function normalizeTelegramCallbackQuery(update2) {
603892
+ const callback = update2.callback_query;
603893
+ if (!callback || typeof callback !== "object") return null;
603894
+ const from3 = callback.from && typeof callback.from === "object" ? callback.from : {};
603895
+ const message2 = callback.message && typeof callback.message === "object" ? callback.message : void 0;
603896
+ const data = typeof callback.data === "string" ? callback.data : "";
603897
+ if (!callback.id || !data) return null;
603898
+ return {
603899
+ id: String(callback.id),
603900
+ fromUserId: telegramOptionalNumber(from3.id) ?? 0,
603901
+ username: telegramOptionalString(from3.username) ?? "unknown",
603902
+ firstName: telegramOptionalString(from3.first_name),
603903
+ chatId: message2?.chat?.id !== void 0 ? normalizeTelegramChatId(message2.chat.id) : void 0,
603904
+ messageId: telegramOptionalNumber(message2?.message_id),
603905
+ data
603906
+ };
603907
+ }
603806
603908
  function adaptTool5(tool, todoSessionId) {
603807
603909
  return {
603808
603910
  name: tool.name,
@@ -603945,7 +604047,7 @@ function renderTelegramSubAgentError(username, error) {
603945
604047
  process.stdout.write(` ${c3.dim("⎿")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
603946
604048
  `);
603947
604049
  }
603948
- var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TelegramBridge;
604050
+ var TELEGRAM_TOOL_ACTION_GROUPS, TELEGRAM_TOOL_ACTION_GROUP, TELEGRAM_TOOL_MUTATING_GROUPS, DEFAULT_TELEGRAM_TOOL_GROUP_POLICY, TELEGRAM_TOOL_BUTTON_LABELS, TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_REMINDER_SLASH_COMMANDS, TELEGRAM_REFLECTION_SLASH_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TELEGRAM_CHANNEL_DMN_SWEEP_MS, TELEGRAM_CHANNEL_DMN_IDLE_AFTER_MS, TELEGRAM_CHANNEL_DMN_MIN_INTERVAL_MS, TELEGRAM_CHANNEL_DMN_MIN_MESSAGES, TelegramBridge;
603949
604051
  var init_telegram_bridge = __esm({
603950
604052
  "packages/cli/src/tui/telegram-bridge.ts"() {
603951
604053
  "use strict";
@@ -603965,6 +604067,90 @@ var init_telegram_bridge = __esm({
603965
604067
  init_telegram_channel_dmn();
603966
604068
  init_telegram_reflection_corpus();
603967
604069
  init_telegram_reflection_extraction();
604070
+ TELEGRAM_TOOL_ACTION_GROUPS = [
604071
+ "read",
604072
+ "message",
604073
+ "media",
604074
+ "janitorial",
604075
+ "reaction",
604076
+ "moderation",
604077
+ "bot_admin",
604078
+ "policy"
604079
+ ];
604080
+ TELEGRAM_TOOL_ACTION_GROUP = {
604081
+ scope_status: "read",
604082
+ get_tool_scope: "read",
604083
+ get_me: "read",
604084
+ get_chat: "read",
604085
+ get_chat_member: "read",
604086
+ get_chat_administrators: "read",
604087
+ get_message_context: "read",
604088
+ send_message: "message",
604089
+ edit_message_text: "message",
604090
+ edit_message_caption: "message",
604091
+ edit_message_reply_markup: "message",
604092
+ pin_message: "message",
604093
+ unpin_message: "message",
604094
+ unpin_all_messages: "message",
604095
+ delete_message: "janitorial",
604096
+ delete_messages: "janitorial",
604097
+ send_file: "media",
604098
+ send_photo: "media",
604099
+ send_document: "media",
604100
+ send_audio: "media",
604101
+ send_voice: "media",
604102
+ send_video: "media",
604103
+ set_message_reaction: "reaction",
604104
+ delete_message_reaction: "reaction",
604105
+ delete_all_message_reactions: "reaction",
604106
+ ban_chat_member: "moderation",
604107
+ unban_chat_member: "moderation",
604108
+ restrict_chat_member: "moderation",
604109
+ promote_chat_member: "moderation",
604110
+ set_chat_permissions: "moderation",
604111
+ approve_chat_join_request: "moderation",
604112
+ decline_chat_join_request: "moderation",
604113
+ set_my_commands: "bot_admin",
604114
+ get_my_commands: "bot_admin",
604115
+ set_chat_menu_button: "bot_admin",
604116
+ get_chat_menu_button: "bot_admin",
604117
+ set_my_description: "bot_admin",
604118
+ get_my_description: "bot_admin",
604119
+ enable_action: "policy",
604120
+ disable_action: "policy",
604121
+ enable_group: "policy",
604122
+ disable_group: "policy",
604123
+ render_admin_buttons: "policy"
604124
+ };
604125
+ TELEGRAM_TOOL_MUTATING_GROUPS = /* @__PURE__ */ new Set([
604126
+ "message",
604127
+ "media",
604128
+ "janitorial",
604129
+ "reaction",
604130
+ "moderation",
604131
+ "bot_admin",
604132
+ "policy"
604133
+ ]);
604134
+ DEFAULT_TELEGRAM_TOOL_GROUP_POLICY = {
604135
+ read: true,
604136
+ message: false,
604137
+ media: true,
604138
+ janitorial: false,
604139
+ reaction: false,
604140
+ moderation: false,
604141
+ bot_admin: false,
604142
+ policy: true
604143
+ };
604144
+ TELEGRAM_TOOL_BUTTON_LABELS = {
604145
+ read: "Read",
604146
+ message: "Send/Edit",
604147
+ media: "Media",
604148
+ janitorial: "Delete",
604149
+ reaction: "Reactions",
604150
+ moderation: "Moderation",
604151
+ bot_admin: "Bot Admin",
604152
+ policy: "Policy"
604153
+ };
603968
604154
  TELEGRAM_SAFETY_PROMPT = `
603969
604155
  CRITICAL SAFETY NOTICE — PUBLIC TELEGRAM CHANNEL
603970
604156
 
@@ -604162,8 +604348,10 @@ Telegram response contract:
604162
604348
  this.agentConfig = agentConfig;
604163
604349
  this.repoRoot = repoRoot;
604164
604350
  this.toolPolicyConfig = toolPolicyConfig;
604351
+ this.telegramToolPolicy = resolveSettings(repoRoot || ".").telegramToolPolicy ?? {};
604165
604352
  this.mediaCacheDir = resolve41(repoRoot || ".", ".omnius", "telegram-media-cache");
604166
604353
  this.telegramConversationDir = resolve41(repoRoot || ".", ".omnius", "telegram-conversations");
604354
+ this.telegramToolButtonDir = resolve41(repoRoot || ".", ".omnius", "telegram-tool-buttons");
604167
604355
  }
604168
604356
  botToken;
604169
604357
  onMessage;
@@ -604206,6 +604394,14 @@ Telegram response contract:
604206
604394
  onSubAgentEvent = null;
604207
604395
  /** Tool policy config — user overrides from config */
604208
604396
  toolPolicyConfig;
604397
+ /** Action-level policy for the unified Telegram tool */
604398
+ telegramToolPolicy;
604399
+ /** Cached bot id from getMe for permission checks */
604400
+ botUserId = null;
604401
+ /** Cached bot chat rights for mutation guard checks */
604402
+ telegramBotRightsCache = /* @__PURE__ */ new Map();
604403
+ /** Short-lived Telegram inline button state directory */
604404
+ telegramToolButtonDir;
604209
604405
  /** Command handler for admin DM slash commands (wired from interactive.ts) */
604210
604406
  commandHandler = null;
604211
604407
  /** Callback fired after a Telegram user completes the TUI-only admin auth challenge */
@@ -604271,6 +604467,12 @@ Telegram response contract:
604271
604467
  setToolPolicyConfig(config) {
604272
604468
  this.toolPolicyConfig = config;
604273
604469
  }
604470
+ setTelegramToolPolicy(policy) {
604471
+ this.telegramToolPolicy = policy ?? {};
604472
+ }
604473
+ getTelegramToolPolicy() {
604474
+ return this.cloneTelegramToolPolicy(this.telegramToolPolicy);
604475
+ }
604274
604476
  /**
604275
604477
  * Set a command handler for admin DM slash commands.
604276
604478
  * When an admin sends a /command in DM, it gets routed through the TUI's
@@ -606073,6 +606275,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
606073
606275
  messagesSent: 0,
606074
606276
  activeSubAgents: 0
606075
606277
  };
606278
+ this.botUserId = typeof me.result?.id === "number" ? me.result.id : this.botUserId;
606076
606279
  this.polling = true;
606077
606280
  this.abortController = new AbortController();
606078
606281
  try {
@@ -606192,11 +606395,12 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
606192
606395
  const chunks = splitTelegramMessageText(html, 3900);
606193
606396
  if (chunks.length === 0) return null;
606194
606397
  const replyToMessageId = msg.chatType !== "private" ? msg.messageId : void 0;
606398
+ const suppressMedia = this.deliveredArtifactMediaSuppressorForMessage(msg);
606195
606399
  if (liveMessageId && !msg.guestQueryId) {
606196
606400
  const edited = await this.editLiveMessage(msg.chatId, liveMessageId, chunks[0]);
606197
606401
  if (edited) {
606198
606402
  for (const chunk of chunks.slice(1)) {
606199
- await this.sendMessageHTML(msg.chatId, chunk);
606403
+ await this.sendMessageHTML(msg.chatId, chunk, void 0, { suppressMedia });
606200
606404
  }
606201
606405
  return liveMessageId;
606202
606406
  }
@@ -606207,7 +606411,12 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
606207
606411
  }
606208
606412
  let firstMessageId = null;
606209
606413
  for (let idx = 0; idx < chunks.length; idx++) {
606210
- const messageId = await this.sendMessageHTML(msg.chatId, chunks[idx], idx === 0 ? replyToMessageId : void 0);
606414
+ const messageId = await this.sendMessageHTML(
606415
+ msg.chatId,
606416
+ chunks[idx],
606417
+ idx === 0 ? replyToMessageId : void 0,
606418
+ { suppressMedia }
606419
+ );
606211
606420
  if (firstMessageId === null) firstMessageId = messageId;
606212
606421
  }
606213
606422
  return firstMessageId;
@@ -606239,6 +606448,20 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
606239
606448
  await this.handleTelegramReflectionSlash(msg, normalizedCommandText);
606240
606449
  return;
606241
606450
  }
606451
+ if (msg.text.trim().startsWith("/") && (telegramSlash === "tools" || telegramSlash === "telegram_tools")) {
606452
+ if (!isAdmin) {
606453
+ await this.replyToTelegramMessage(msg, "Telegram tool controls require admin authentication.");
606454
+ return;
606455
+ }
606456
+ const parts = normalizedCommandText.trim().split(/\s+/).slice(1);
606457
+ const target = parts[0] === "chat" || parts[0] === "group" ? this.resolveTelegramFileTarget({ target: parts[1] }, msg.chatId, msg.chatType === "private", msg) : { ok: true, chatId: msg.chatType === "private" ? void 0 : msg.chatId };
606458
+ if (!target.ok) {
606459
+ await this.replyToTelegramMessage(msg, target.error);
606460
+ return;
606461
+ }
606462
+ await this.sendTelegramToolPanel(msg.chatId, target.chatId);
606463
+ return;
606464
+ }
606242
606465
  if (!this.agentConfig || !this.repoRoot) {
606243
606466
  this.onMessage(msg);
606244
606467
  return;
@@ -606407,6 +606630,7 @@ Join: ${newUrl}`);
606407
606630
  creativeWorkspaceRoot: this.creativeWorkspaceRootForMessage(msg, toolContext),
606408
606631
  generatedArtifacts: [],
606409
606632
  deliveredArtifacts: [],
606633
+ deliveredFileSends: /* @__PURE__ */ new Set(),
606410
606634
  surfacedToolCallFingerprints: /* @__PURE__ */ new Set()
606411
606635
  };
606412
606636
  this.subAgents.set(sessionKey, subAgent);
@@ -606519,6 +606743,7 @@ Join: ${newUrl}`);
606519
606743
  creativeWorkspaceRoot: this.creativeWorkspaceRootForMessage(msg, toolContext),
606520
606744
  generatedArtifacts: [],
606521
606745
  deliveredArtifacts: [],
606746
+ deliveredFileSends: /* @__PURE__ */ new Set(),
606522
606747
  surfacedToolCallFingerprints: /* @__PURE__ */ new Set()
606523
606748
  };
606524
606749
  this.subAgents.set(sessionKey, subAgent);
@@ -607451,6 +607676,7 @@ Scoped workspace: ${scopedRoot}`,
607451
607676
  new SkillListTool(repoRoot),
607452
607677
  sharedSkillExtractTool,
607453
607678
  this.buildTelegramMediaRecentTool(chatId, msg),
607679
+ this.buildTelegramTool(context2, repoRoot, chatId, msg),
607454
607680
  ...this.buildTelegramReminderTools(context2, repoRoot, chatId, msg)
607455
607681
  ];
607456
607682
  const adminTools = [
@@ -607513,6 +607739,7 @@ Scoped workspace: ${scopedRoot}`,
607513
607739
  new SkillListTool(repoRoot),
607514
607740
  new SkillExecuteTool(repoRoot),
607515
607741
  adminSkillExtractTool,
607742
+ this.buildTelegramTool(context2, repoRoot, chatId, msg),
607516
607743
  ...this.buildTelegramReminderTools(context2, repoRoot, chatId, msg),
607517
607744
  fullSubAgentTool,
607518
607745
  this.buildTelegramSendFileTool(context2, repoRoot, chatId, msg)
@@ -607559,6 +607786,811 @@ Scoped workspace: ${scopedRoot}`,
607559
607786
  ]);
607560
607787
  return tools.filter((tool) => !blocked.has(tool.name));
607561
607788
  }
607789
+ buildTelegramTool(context2, repoRoot, chatId, currentMsg) {
607790
+ const bridge = this;
607791
+ const adminDm = context2 === "telegram-admin-dm";
607792
+ return {
607793
+ name: "telegram",
607794
+ description: [
607795
+ "Unified scoped Telegram tool. Use action to inspect chat state, send messages/files, delete messages, manage reactions, or manage Telegram tool policy.",
607796
+ "Deletion and moderation actions require authenticated admin context, Omnius Telegram tool policy enablement, current/known chat scoping, and Telegram bot rights.",
607797
+ "Use scope_status before risky actions when uncertain. Use render_admin_buttons to show clickable admin toggles."
607798
+ ].join(" "),
607799
+ parameters: {
607800
+ type: "object",
607801
+ properties: {
607802
+ action: {
607803
+ type: "string",
607804
+ enum: Object.keys(TELEGRAM_TOOL_ACTION_GROUP),
607805
+ description: "Telegram action to perform."
607806
+ },
607807
+ target: {
607808
+ type: "string",
607809
+ description: adminDm ? "Optional target chat/user. Defaults to the current DM; known group chat ids are allowed." : "Current chat only for group/public contexts."
607810
+ },
607811
+ chat_id: { type: "string", description: "Alias for target." },
607812
+ message_id: { type: "number", description: "Telegram message id for single-message actions." },
607813
+ message_ids: {
607814
+ type: "array",
607815
+ items: { type: "number" },
607816
+ description: "Telegram message ids for batch actions."
607817
+ },
607818
+ user_id: { type: "number", description: "Telegram user id for member/moderation/reaction actions." },
607819
+ text: { type: "string", description: "Text for send/edit actions." },
607820
+ caption: { type: "string", description: "Caption for media or caption edit actions." },
607821
+ reply_markup: { type: "object", description: "Raw Telegram reply_markup object for edit_message_reply_markup." },
607822
+ permissions: { type: "object", description: "Raw Telegram ChatPermissions object for restriction or default chat permissions." },
607823
+ rights: { type: "object", description: "Raw Telegram admin-rights fields for promote_chat_member." },
607824
+ reaction: { type: "array", items: { type: "object" }, description: "ReactionType array for set_message_reaction." },
607825
+ commands: { type: "array", items: { type: "object" }, description: "Bot command objects for set_my_commands." },
607826
+ menu_button: { type: "object", description: "Raw MenuButton object for set_chat_menu_button." },
607827
+ description: { type: "string", description: "Bot description text for set_my_description." },
607828
+ language_code: { type: "string", description: "Optional Telegram language code for bot command/description actions." },
607829
+ until_date: { type: "number", description: "Unix timestamp for ban/restrict expiry." },
607830
+ path: { type: "string", description: "File path for send_file; same scoping as telegram_send_file." },
607831
+ kind: {
607832
+ type: "string",
607833
+ enum: ["auto", "image", "photo", "document", "audio", "voice", "video", "animation"],
607834
+ description: "Media kind for send_file."
607835
+ },
607836
+ group: {
607837
+ type: "string",
607838
+ enum: TELEGRAM_TOOL_ACTION_GROUPS,
607839
+ description: "Action group for enable_group/disable_group."
607840
+ },
607841
+ tool_action: { type: "string", description: "Action name for enable_action/disable_action." },
607842
+ scope: { type: "string", enum: ["global", "chat"], description: "Policy scope for enable/disable actions." },
607843
+ dry_run: { type: "boolean", description: "When true, validate and report without calling Telegram." },
607844
+ reason: { type: "string", description: "Short audit reason for risky Telegram actions." }
607845
+ },
607846
+ required: ["action"]
607847
+ },
607848
+ async execute(args) {
607849
+ const start2 = performance.now();
607850
+ const action = String(args["action"] || "").trim().toLowerCase();
607851
+ if (!action || !(action in TELEGRAM_TOOL_ACTION_GROUP)) {
607852
+ return { success: false, output: "", error: `Unsupported Telegram action: ${action || "(missing)"}`, durationMs: performance.now() - start2 };
607853
+ }
607854
+ try {
607855
+ const result = await bridge.executeTelegramToolAction(action, args, {
607856
+ context: context2,
607857
+ repoRoot,
607858
+ chatId,
607859
+ currentMsg
607860
+ });
607861
+ return {
607862
+ success: true,
607863
+ output: typeof result === "string" ? result : JSON.stringify(result, null, 2),
607864
+ llmContent: typeof result === "string" ? result : JSON.stringify(result),
607865
+ durationMs: performance.now() - start2,
607866
+ mutated: TELEGRAM_TOOL_MUTATING_GROUPS.has(TELEGRAM_TOOL_ACTION_GROUP[action]),
607867
+ mutatedFiles: []
607868
+ };
607869
+ } catch (err) {
607870
+ return {
607871
+ success: false,
607872
+ output: "",
607873
+ error: err instanceof Error ? err.message : String(err),
607874
+ durationMs: performance.now() - start2
607875
+ };
607876
+ }
607877
+ }
607878
+ };
607879
+ }
607880
+ async executeTelegramToolAction(action, args, env2) {
607881
+ const target = this.resolveTelegramFileTarget(args, env2.chatId, env2.context === "telegram-admin-dm", env2.currentMsg);
607882
+ if (!target.ok && this.telegramToolHasExplicitTarget(args)) throw new Error(target.error);
607883
+ const targetChatId = target.ok ? target.chatId : env2.chatId;
607884
+ const gate = this.assertTelegramToolActionAllowed(action, env2.context, env2.currentMsg, targetChatId, args);
607885
+ if (!gate.ok) throw new Error(gate.error);
607886
+ const dryRun = this.telegramToolDryRun(args, targetChatId);
607887
+ const messageId = this.telegramNumberArg(args["message_id"]);
607888
+ const messageIds = this.telegramMessageIdsArg(args["message_ids"] ?? args["message_id"]);
607889
+ const userId = this.telegramNumberArg(args["user_id"]);
607890
+ if (!dryRun) {
607891
+ await this.assertTelegramBotRightsForAction(action, targetChatId, messageIds, env2.currentMsg);
607892
+ }
607893
+ switch (action) {
607894
+ case "scope_status":
607895
+ case "get_tool_scope":
607896
+ return this.formatTelegramToolScopeStatus(env2.context, env2.currentMsg, targetChatId);
607897
+ case "get_me":
607898
+ return this.telegramApiResult("getMe", {});
607899
+ case "get_chat":
607900
+ if (targetChatId === void 0) throw new Error("target/chat_id is required for get_chat.");
607901
+ return this.telegramApiResult("getChat", { chat_id: targetChatId });
607902
+ case "get_chat_member":
607903
+ if (targetChatId === void 0 || userId === void 0) throw new Error("target/chat_id and user_id are required for get_chat_member.");
607904
+ return this.telegramApiResult("getChatMember", { chat_id: targetChatId, user_id: userId });
607905
+ case "get_chat_administrators":
607906
+ if (targetChatId === void 0) throw new Error("target/chat_id is required for get_chat_administrators.");
607907
+ return this.getChatAdministrators(targetChatId, args["return_bots"] === true);
607908
+ case "get_message_context":
607909
+ if (targetChatId === void 0) throw new Error("target/chat_id is required for get_message_context.");
607910
+ return this.telegramMessageContext(targetChatId, messageId);
607911
+ case "send_message": {
607912
+ if (targetChatId === void 0) throw new Error("target/chat_id is required for send_message.");
607913
+ const text = String(args["text"] || "").trim();
607914
+ if (!text) throw new Error("text is required for send_message.");
607915
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { text });
607916
+ const sent = await this.sendMessage(targetChatId, text);
607917
+ return { action, telegram_method: "sendMessage", ok: true, chat_id: targetChatId, message_id: sent };
607918
+ }
607919
+ case "edit_message_text": {
607920
+ if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for edit_message_text.");
607921
+ const text = String(args["text"] || "").trim();
607922
+ if (!text) throw new Error("text is required for edit_message_text.");
607923
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_id: messageId, text });
607924
+ return this.telegramApiResult("editMessageText", { chat_id: targetChatId, message_id: messageId, text: convertMarkdownToTelegramHTML(text), parse_mode: "HTML" });
607925
+ }
607926
+ case "edit_message_caption": {
607927
+ if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for edit_message_caption.");
607928
+ const caption = String(args["caption"] ?? args["text"] ?? "").trim();
607929
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_id: messageId, caption });
607930
+ return this.telegramApiResult("editMessageCaption", { chat_id: targetChatId, message_id: messageId, caption: convertMarkdownToTelegramHTML(caption), parse_mode: "HTML" });
607931
+ }
607932
+ case "edit_message_reply_markup": {
607933
+ if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for edit_message_reply_markup.");
607934
+ const replyMarkup = this.telegramObjectArg(args["reply_markup"]);
607935
+ if (!replyMarkup) throw new Error("reply_markup object is required for edit_message_reply_markup.");
607936
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_id: messageId, reply_markup: replyMarkup });
607937
+ return this.telegramApiResult("editMessageReplyMarkup", { chat_id: targetChatId, message_id: messageId, reply_markup: replyMarkup });
607938
+ }
607939
+ case "delete_message": {
607940
+ if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for delete_message.");
607941
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_id: messageId });
607942
+ return this.deleteTelegramMessage(targetChatId, messageId, env2.currentMsg);
607943
+ }
607944
+ case "delete_messages": {
607945
+ if (targetChatId === void 0 || messageIds.length === 0) throw new Error("target/chat_id and message_ids are required for delete_messages.");
607946
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_ids: messageIds });
607947
+ return this.deleteTelegramMessages(targetChatId, messageIds, env2.currentMsg);
607948
+ }
607949
+ case "pin_message":
607950
+ if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for pin_message.");
607951
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_id: messageId });
607952
+ return this.telegramApiResult("pinChatMessage", { chat_id: targetChatId, message_id: messageId, disable_notification: args["disable_notification"] === true });
607953
+ case "unpin_message":
607954
+ if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for unpin_message.");
607955
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_id: messageId });
607956
+ return this.telegramApiResult("unpinChatMessage", { chat_id: targetChatId, message_id: messageId });
607957
+ case "unpin_all_messages":
607958
+ if (targetChatId === void 0) throw new Error("target/chat_id is required for unpin_all_messages.");
607959
+ if (dryRun) return this.telegramDryRun(action, targetChatId, {});
607960
+ return this.telegramApiResult("unpinAllChatMessages", { chat_id: targetChatId });
607961
+ case "set_message_reaction":
607962
+ if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for set_message_reaction.");
607963
+ if (!Array.isArray(args["reaction"])) throw new Error("reaction array is required for set_message_reaction.");
607964
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_id: messageId, reaction: args["reaction"] });
607965
+ return this.telegramApiResult("setMessageReaction", {
607966
+ chat_id: targetChatId,
607967
+ message_id: messageId,
607968
+ reaction: args["reaction"],
607969
+ is_big: args["is_big"] === true
607970
+ });
607971
+ case "delete_message_reaction":
607972
+ if (targetChatId === void 0 || messageId === void 0) throw new Error("target/chat_id and message_id are required for delete_message_reaction.");
607973
+ if (userId === void 0 && args["actor_chat_id"] === void 0) throw new Error("user_id or actor_chat_id is required for delete_message_reaction.");
607974
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { message_id: messageId, user_id: userId });
607975
+ return { action, ok: await this.deleteMessageReaction(targetChatId, messageId, { userId, actorChatId: args["actor_chat_id"] }) };
607976
+ case "delete_all_message_reactions":
607977
+ if (targetChatId === void 0) throw new Error("target/chat_id is required for delete_all_message_reactions.");
607978
+ if (userId === void 0 && args["actor_chat_id"] === void 0) throw new Error("user_id or actor_chat_id is required for delete_all_message_reactions.");
607979
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { user_id: userId });
607980
+ return { action, ok: await this.deleteAllMessageReactions(targetChatId, { userId, actorChatId: args["actor_chat_id"] }) };
607981
+ case "send_file": {
607982
+ const tool = this.buildTelegramSendFileTool(env2.context, env2.repoRoot, env2.chatId, env2.currentMsg);
607983
+ const result = await tool.execute(args);
607984
+ if (!result.success) throw new Error(result.error || result.output || "telegram send_file failed");
607985
+ return { action, ok: true, output: result.output };
607986
+ }
607987
+ case "send_photo":
607988
+ case "send_document":
607989
+ case "send_audio":
607990
+ case "send_voice":
607991
+ case "send_video": {
607992
+ const kind = action.slice("send_".length);
607993
+ const tool = this.buildTelegramSendFileTool(env2.context, env2.repoRoot, env2.chatId, env2.currentMsg);
607994
+ const result = await tool.execute({ ...args, kind });
607995
+ if (!result.success) throw new Error(result.error || result.output || `telegram ${action} failed`);
607996
+ return { action, ok: true, output: result.output };
607997
+ }
607998
+ case "ban_chat_member":
607999
+ if (targetChatId === void 0 || userId === void 0) throw new Error("target/chat_id and user_id are required for ban_chat_member.");
608000
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { user_id: userId });
608001
+ return this.telegramApiResult("banChatMember", {
608002
+ chat_id: targetChatId,
608003
+ user_id: userId,
608004
+ ...args["until_date"] !== void 0 ? { until_date: this.telegramNumberArg(args["until_date"]) } : {},
608005
+ revoke_messages: args["revoke_messages"] === true
608006
+ });
608007
+ case "unban_chat_member":
608008
+ if (targetChatId === void 0 || userId === void 0) throw new Error("target/chat_id and user_id are required for unban_chat_member.");
608009
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { user_id: userId });
608010
+ return this.telegramApiResult("unbanChatMember", {
608011
+ chat_id: targetChatId,
608012
+ user_id: userId,
608013
+ only_if_banned: args["only_if_banned"] === true
608014
+ });
608015
+ case "restrict_chat_member": {
608016
+ if (targetChatId === void 0 || userId === void 0) throw new Error("target/chat_id, user_id, and permissions are required for restrict_chat_member.");
608017
+ const permissions = this.telegramObjectArg(args["permissions"]);
608018
+ if (!permissions) throw new Error("permissions object is required for restrict_chat_member.");
608019
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { user_id: userId, permissions });
608020
+ return this.telegramApiResult("restrictChatMember", {
608021
+ chat_id: targetChatId,
608022
+ user_id: userId,
608023
+ permissions,
608024
+ ...args["until_date"] !== void 0 ? { until_date: this.telegramNumberArg(args["until_date"]) } : {},
608025
+ use_independent_chat_permissions: args["use_independent_chat_permissions"] === true
608026
+ });
608027
+ }
608028
+ case "promote_chat_member": {
608029
+ if (targetChatId === void 0 || userId === void 0) throw new Error("target/chat_id, user_id, and rights are required for promote_chat_member.");
608030
+ const rights = this.telegramObjectArg(args["rights"]);
608031
+ if (!rights) throw new Error("rights object is required for promote_chat_member.");
608032
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { user_id: userId, rights });
608033
+ return this.telegramApiResult("promoteChatMember", { chat_id: targetChatId, user_id: userId, ...rights });
608034
+ }
608035
+ case "set_chat_permissions": {
608036
+ if (targetChatId === void 0) throw new Error("target/chat_id and permissions are required for set_chat_permissions.");
608037
+ const permissions = this.telegramObjectArg(args["permissions"]);
608038
+ if (!permissions) throw new Error("permissions object is required for set_chat_permissions.");
608039
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { permissions });
608040
+ return this.telegramApiResult("setChatPermissions", {
608041
+ chat_id: targetChatId,
608042
+ permissions,
608043
+ use_independent_chat_permissions: args["use_independent_chat_permissions"] === true
608044
+ });
608045
+ }
608046
+ case "approve_chat_join_request":
608047
+ if (targetChatId === void 0 || userId === void 0) throw new Error("target/chat_id and user_id are required for approve_chat_join_request.");
608048
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { user_id: userId });
608049
+ return this.telegramApiResult("approveChatJoinRequest", { chat_id: targetChatId, user_id: userId });
608050
+ case "decline_chat_join_request":
608051
+ if (targetChatId === void 0 || userId === void 0) throw new Error("target/chat_id and user_id are required for decline_chat_join_request.");
608052
+ if (dryRun) return this.telegramDryRun(action, targetChatId, { user_id: userId });
608053
+ return this.telegramApiResult("declineChatJoinRequest", { chat_id: targetChatId, user_id: userId });
608054
+ case "set_my_commands": {
608055
+ if (!Array.isArray(args["commands"])) throw new Error("commands array is required for set_my_commands.");
608056
+ if (dryRun) return this.telegramDryRun(action, targetChatId ?? "bot", { commands: args["commands"] });
608057
+ return this.telegramApiResult("setMyCommands", {
608058
+ commands: args["commands"],
608059
+ ...args["language_code"] ? { language_code: String(args["language_code"]) } : {}
608060
+ });
608061
+ }
608062
+ case "get_my_commands":
608063
+ return this.telegramApiResult("getMyCommands", args["language_code"] ? { language_code: String(args["language_code"]) } : {});
608064
+ case "set_chat_menu_button": {
608065
+ const menuButton = this.telegramObjectArg(args["menu_button"]);
608066
+ if (!menuButton) throw new Error("menu_button object is required for set_chat_menu_button.");
608067
+ if (dryRun) return this.telegramDryRun(action, targetChatId ?? "bot", { menu_button: menuButton });
608068
+ return this.telegramApiResult("setChatMenuButton", {
608069
+ ...targetChatId !== void 0 ? { chat_id: targetChatId } : {},
608070
+ menu_button: menuButton
608071
+ });
608072
+ }
608073
+ case "get_chat_menu_button":
608074
+ return this.telegramApiResult("getChatMenuButton", targetChatId !== void 0 ? { chat_id: targetChatId } : {});
608075
+ case "set_my_description": {
608076
+ const description = String(args["description"] ?? args["text"] ?? "");
608077
+ if (dryRun) return this.telegramDryRun(action, targetChatId ?? "bot", { description });
608078
+ return this.telegramApiResult("setMyDescription", {
608079
+ description,
608080
+ ...args["language_code"] ? { language_code: String(args["language_code"]) } : {}
608081
+ });
608082
+ }
608083
+ case "get_my_description":
608084
+ return this.telegramApiResult("getMyDescription", args["language_code"] ? { language_code: String(args["language_code"]) } : {});
608085
+ case "enable_group":
608086
+ case "disable_group":
608087
+ return this.setTelegramToolGroupPolicy(args, action === "enable_group", targetChatId);
608088
+ case "enable_action":
608089
+ case "disable_action":
608090
+ return this.setTelegramToolActionPolicy(args, action === "enable_action", targetChatId);
608091
+ case "render_admin_buttons":
608092
+ if (!env2.currentMsg) throw new Error("render_admin_buttons requires a Telegram message context.");
608093
+ if (dryRun) return this.telegramDryRun(action, env2.currentMsg.chatId, {});
608094
+ return this.sendTelegramToolPanel(env2.currentMsg.chatId, String(args["scope"] || "").toLowerCase() === "chat" ? targetChatId : void 0);
608095
+ default:
608096
+ throw new Error(`Unsupported Telegram action: ${action}`);
608097
+ }
608098
+ }
608099
+ cloneTelegramToolPolicy(policy) {
608100
+ return JSON.parse(JSON.stringify(policy ?? {}));
608101
+ }
608102
+ telegramToolHasExplicitTarget(args) {
608103
+ return ["target", "chat_id", "target_chat_id", "username"].some((key) => {
608104
+ const value2 = args[key];
608105
+ return value2 !== void 0 && String(value2).trim() !== "";
608106
+ });
608107
+ }
608108
+ telegramObjectArg(value2) {
608109
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return void 0;
608110
+ return value2;
608111
+ }
608112
+ telegramNumberArg(value2) {
608113
+ if (typeof value2 === "number" && Number.isFinite(value2)) return Math.trunc(value2);
608114
+ if (typeof value2 === "string" && /^-?\d+$/.test(value2.trim())) return Number(value2.trim());
608115
+ return void 0;
608116
+ }
608117
+ telegramMessageIdsArg(value2) {
608118
+ if (Array.isArray(value2)) {
608119
+ return [...new Set(value2.map((item) => this.telegramNumberArg(item)).filter((item) => item !== void 0))];
608120
+ }
608121
+ if (typeof value2 === "string" && value2.includes(",")) {
608122
+ return [...new Set(value2.split(",").map((item) => this.telegramNumberArg(item.trim())).filter((item) => item !== void 0))];
608123
+ }
608124
+ const single = this.telegramNumberArg(value2);
608125
+ return single === void 0 ? [] : [single];
608126
+ }
608127
+ effectiveTelegramToolPolicy(chatId) {
608128
+ const base3 = this.cloneTelegramToolPolicy(this.telegramToolPolicy);
608129
+ const override = chatId !== void 0 ? base3.chatOverrides?.[String(chatId)] : void 0;
608130
+ if (!override) return base3;
608131
+ return {
608132
+ ...base3,
608133
+ enabledActionGroups: {
608134
+ ...base3.enabledActionGroups ?? {},
608135
+ ...override.enabledActionGroups ?? {}
608136
+ },
608137
+ enabledActions: {
608138
+ ...base3.enabledActions ?? {},
608139
+ ...override.enabledActions ?? {}
608140
+ },
608141
+ requireAdminForMutation: override.requireAdminForMutation ?? base3.requireAdminForMutation,
608142
+ dryRunByDefault: override.dryRunByDefault ?? base3.dryRunByDefault
608143
+ };
608144
+ }
608145
+ telegramToolActionEnabled(action, chatId) {
608146
+ const group = TELEGRAM_TOOL_ACTION_GROUP[action];
608147
+ if (!group) return false;
608148
+ const policy = this.effectiveTelegramToolPolicy(chatId);
608149
+ const actionOverride = policy.enabledActions?.[action];
608150
+ if (actionOverride !== void 0) return actionOverride;
608151
+ const groupOverride = policy.enabledActionGroups?.[group];
608152
+ if (groupOverride !== void 0) return groupOverride;
608153
+ return DEFAULT_TELEGRAM_TOOL_GROUP_POLICY[group];
608154
+ }
608155
+ telegramToolDryRun(args, chatId) {
608156
+ if (args["dry_run"] === true) return true;
608157
+ if (args["dry_run"] === false) return false;
608158
+ return this.effectiveTelegramToolPolicy(chatId).dryRunByDefault === true;
608159
+ }
608160
+ isAdminActor(userId, username) {
608161
+ if (!this.adminUserId) return false;
608162
+ if (userId !== void 0 && String(userId) === this.adminUserId) return true;
608163
+ return Boolean(username && username === this.adminUserId);
608164
+ }
608165
+ assertTelegramToolActionAllowed(action, context2, currentMsg, targetChatId, args) {
608166
+ const group = TELEGRAM_TOOL_ACTION_GROUP[action];
608167
+ if (!group) return { ok: false, error: `Unsupported Telegram action: ${action}` };
608168
+ const currentChatId = currentMsg?.chatId;
608169
+ const isCurrentChat = currentChatId === void 0 || targetChatId === void 0 || String(currentChatId) === String(targetChatId);
608170
+ const allowedPrivateSharedGroupMedia = group === "media" && currentMsg?.chatType === "private";
608171
+ if (context2 !== "telegram-admin-dm" && !isCurrentChat && !allowedPrivateSharedGroupMedia) {
608172
+ return { ok: false, error: "This Telegram context can only target the current chat." };
608173
+ }
608174
+ if (context2 === "telegram-public" && (group === "janitorial" || group === "reaction" || group === "moderation" || group === "bot_admin" || group === "policy" || group === "message")) {
608175
+ return { ok: false, error: `Telegram ${group} actions require authenticated admin context.` };
608176
+ }
608177
+ if ((group === "policy" || group === "bot_admin" || group === "moderation") && context2 !== "telegram-admin-dm" && context2 !== "telegram-admin-group") {
608178
+ return { ok: false, error: `Telegram ${group} actions require admin context.` };
608179
+ }
608180
+ const mutating = TELEGRAM_TOOL_MUTATING_GROUPS.has(group);
608181
+ const policy = this.effectiveTelegramToolPolicy(targetChatId);
608182
+ if (mutating && policy.requireAdminForMutation !== false && context2 === "telegram-public" && group !== "media") {
608183
+ return { ok: false, error: "Telegram mutation requires admin context by policy." };
608184
+ }
608185
+ if (!this.telegramToolActionEnabled(action, targetChatId)) {
608186
+ const scope = targetChatId !== void 0 && this.telegramToolPolicy.chatOverrides?.[String(targetChatId)] ? `chat ${String(targetChatId)}` : "global/default";
608187
+ return { ok: false, error: `Telegram action ${action} is disabled by ${scope} policy. Enable ${group} or ${action} first.` };
608188
+ }
608189
+ if ((action === "delete_messages" || group === "moderation") && !String(args["reason"] || "").trim()) {
608190
+ return { ok: false, error: `Telegram ${action} requires a short reason for the audit trail.` };
608191
+ }
608192
+ return { ok: true };
608193
+ }
608194
+ formatTelegramToolScopeStatus(context2, currentMsg, targetChatId) {
608195
+ const effective = this.effectiveTelegramToolPolicy(targetChatId);
608196
+ const groups = TELEGRAM_TOOL_ACTION_GROUPS.map((group) => ({
608197
+ group,
608198
+ enabled: effective.enabledActionGroups?.[group] ?? DEFAULT_TELEGRAM_TOOL_GROUP_POLICY[group],
608199
+ default: DEFAULT_TELEGRAM_TOOL_GROUP_POLICY[group]
608200
+ }));
608201
+ const knownTargets = context2 === "telegram-admin-dm" ? this.knownTelegramChatTargets().slice(0, 12).map((target) => ({
608202
+ chat_id: target.chatId,
608203
+ chat_type: target.chatType,
608204
+ title: target.title,
608205
+ session: target.sessionKey
608206
+ })) : [];
608207
+ return {
608208
+ context: context2,
608209
+ current_chat_id: currentMsg?.chatId,
608210
+ current_chat_type: currentMsg?.chatType,
608211
+ target_chat_id: targetChatId,
608212
+ bot_username: this.state.botUsername || void 0,
608213
+ admin_configured: Boolean(this.adminUserId),
608214
+ effective_policy: {
608215
+ groups,
608216
+ enabled_actions: effective.enabledActions ?? {},
608217
+ dry_run_by_default: effective.dryRunByDefault === true,
608218
+ require_admin_for_mutation: effective.requireAdminForMutation !== false
608219
+ },
608220
+ known_targets: knownTargets
608221
+ };
608222
+ }
608223
+ telegramMessageContext(chatId, messageId) {
608224
+ this.ensureAllTelegramConversationsLoaded();
608225
+ const sessionKey = `chat:${String(chatId)}`;
608226
+ const history = this.chatHistory.get(sessionKey) ?? [];
608227
+ if (messageId === void 0) {
608228
+ return {
608229
+ chat_id: chatId,
608230
+ recent: history.slice(-20)
608231
+ };
608232
+ }
608233
+ const index = history.findIndex((entry) => entry.messageId === messageId);
608234
+ if (index === -1) {
608235
+ return {
608236
+ chat_id: chatId,
608237
+ message_id: messageId,
608238
+ found: false,
608239
+ recent: history.slice(-10)
608240
+ };
608241
+ }
608242
+ return {
608243
+ chat_id: chatId,
608244
+ message_id: messageId,
608245
+ found: true,
608246
+ before: history.slice(Math.max(0, index - 5), index),
608247
+ message: history[index],
608248
+ after: history.slice(index + 1, index + 6)
608249
+ };
608250
+ }
608251
+ telegramDryRun(action, chatId, detail) {
608252
+ return {
608253
+ action,
608254
+ dry_run: true,
608255
+ ok: true,
608256
+ chat_id: chatId,
608257
+ would_call_telegram: true,
608258
+ detail,
608259
+ policy_scope: this.telegramToolPolicy.chatOverrides?.[String(chatId)] ? "chat" : "global/default"
608260
+ };
608261
+ }
608262
+ async telegramApiResult(method, body) {
608263
+ const result = await this.apiCall(method, body);
608264
+ if (!result.ok) throw new Error(String(result.description || `Telegram ${method} failed`));
608265
+ if (method.startsWith("send") || method === "copyMessage" || method === "forwardMessage") this.state.messagesSent++;
608266
+ return {
608267
+ telegram_method: method,
608268
+ ok: true,
608269
+ result: result.result ?? true
608270
+ };
608271
+ }
608272
+ async getTelegramBotUserId() {
608273
+ if (this.botUserId !== null) return this.botUserId;
608274
+ const result = await this.apiCall("getMe", {});
608275
+ if (!result.ok || typeof result.result?.id !== "number") {
608276
+ throw new Error(String(result.description || "Telegram getMe did not return a bot id."));
608277
+ }
608278
+ const botId = result.result.id;
608279
+ this.botUserId = botId;
608280
+ if (typeof result.result.username === "string") this.state.botUsername = result.result.username;
608281
+ return botId;
608282
+ }
608283
+ async getChatMember(chatId, userId) {
608284
+ const result = await this.apiCall("getChatMember", { chat_id: chatId, user_id: userId });
608285
+ if (!result.ok) return null;
608286
+ return result.result && typeof result.result === "object" ? result.result : null;
608287
+ }
608288
+ async getBotChatRights(chatId, force = false) {
608289
+ const key = String(chatId);
608290
+ const cached = this.telegramBotRightsCache.get(key);
608291
+ if (!force && cached && Date.now() - cached.checkedAt < 6e4) return cached;
608292
+ const botId = await this.getTelegramBotUserId();
608293
+ const member = await this.getChatMember(chatId, botId);
608294
+ if (!member) throw new Error(`Could not inspect Telegram bot rights for chat ${String(chatId)}.`);
608295
+ const status = String(member.status || "unknown");
608296
+ const rights = {
608297
+ chatId,
608298
+ status,
608299
+ isCreator: status === "creator",
608300
+ isAdministrator: status === "creator" || status === "administrator",
608301
+ canDeleteMessages: status === "creator" || member.can_delete_messages === true,
608302
+ canPinMessages: status === "creator" || member.can_pin_messages === true,
608303
+ canRestrictMembers: status === "creator" || member.can_restrict_members === true,
608304
+ canPromoteMembers: status === "creator" || member.can_promote_members === true,
608305
+ canManageChat: status === "creator" || member.can_manage_chat === true,
608306
+ canChangeInfo: status === "creator" || member.can_change_info === true,
608307
+ checkedAt: Date.now(),
608308
+ raw: member
608309
+ };
608310
+ this.telegramBotRightsCache.set(key, rights);
608311
+ return rights;
608312
+ }
608313
+ isKnownAssistantTelegramMessage(chatId, messageId) {
608314
+ this.ensureAllTelegramConversationsLoaded();
608315
+ for (const history of this.chatHistory.values()) {
608316
+ if (history.some((entry) => entry.role === "assistant" && String(entry.chatId) === String(chatId) && entry.messageId === messageId)) {
608317
+ return true;
608318
+ }
608319
+ }
608320
+ return false;
608321
+ }
608322
+ telegramTargetLooksPrivate(chatId, currentMsg) {
608323
+ if (currentMsg && String(currentMsg.chatId) === String(chatId) && currentMsg.chatType === "private") return true;
608324
+ return typeof chatId === "number" && chatId > 0;
608325
+ }
608326
+ async assertTelegramBotRightsForAction(action, chatId, messageIds, currentMsg) {
608327
+ if (chatId === void 0) return;
608328
+ const group = TELEGRAM_TOOL_ACTION_GROUP[action];
608329
+ if (!group || group === "read" || group === "media" || group === "policy" || group === "bot_admin") return;
608330
+ if (group === "message" && action !== "pin_message" && action !== "unpin_message" && action !== "unpin_all_messages") return;
608331
+ if (this.telegramTargetLooksPrivate(chatId, currentMsg)) return;
608332
+ if (action === "delete_message" || action === "delete_messages") {
608333
+ if (messageIds.length > 0 && messageIds.every((id) => this.isKnownAssistantTelegramMessage(chatId, id))) return;
608334
+ const rights2 = await this.getBotChatRights(chatId);
608335
+ if (!rights2.canDeleteMessages) {
608336
+ throw new Error(`Bot lacks can_delete_messages in chat ${String(chatId)}; Telegram status=${rights2.status}.`);
608337
+ }
608338
+ return;
608339
+ }
608340
+ const rights = await this.getBotChatRights(chatId);
608341
+ if ((action === "pin_message" || action === "unpin_message" || action === "unpin_all_messages") && !rights.canPinMessages && !rights.canManageChat) {
608342
+ throw new Error(`Bot lacks pin/manage rights in chat ${String(chatId)}; Telegram status=${rights.status}.`);
608343
+ }
608344
+ if ((action === "ban_chat_member" || action === "unban_chat_member" || action === "restrict_chat_member" || action === "set_chat_permissions" || action === "approve_chat_join_request" || action === "decline_chat_join_request") && !rights.canRestrictMembers) {
608345
+ throw new Error(`Bot lacks restrict/member management rights in chat ${String(chatId)}; Telegram status=${rights.status}.`);
608346
+ }
608347
+ if (action === "promote_chat_member" && !rights.canPromoteMembers) {
608348
+ throw new Error(`Bot lacks promote rights in chat ${String(chatId)}; Telegram status=${rights.status}.`);
608349
+ }
608350
+ }
608351
+ async deleteTelegramMessage(chatId, messageId, currentMsg) {
608352
+ await this.assertTelegramBotRightsForAction("delete_message", chatId, [messageId], currentMsg);
608353
+ const result = await this.apiCall("deleteMessage", { chat_id: chatId, message_id: messageId });
608354
+ if (!result.ok) throw new Error(String(result.description || "Telegram deleteMessage failed"));
608355
+ return {
608356
+ action: "delete_message",
608357
+ telegram_method: "deleteMessage",
608358
+ ok: true,
608359
+ chat_id: chatId,
608360
+ message_id: messageId,
608361
+ bot_rights_checked: !this.telegramTargetLooksPrivate(chatId, currentMsg) && !this.isKnownAssistantTelegramMessage(chatId, messageId),
608362
+ policy_scope: this.telegramToolPolicy.chatOverrides?.[String(chatId)] ? "chat" : "global/default"
608363
+ };
608364
+ }
608365
+ async deleteTelegramMessages(chatId, messageIds, currentMsg) {
608366
+ const unique = [...new Set(messageIds)].filter((id) => Number.isFinite(id));
608367
+ if (unique.length === 0) throw new Error("deleteTelegramMessages requires at least one message id.");
608368
+ await this.assertTelegramBotRightsForAction("delete_messages", chatId, unique, currentMsg);
608369
+ const chunks = [];
608370
+ for (let idx = 0; idx < unique.length; idx += 100) chunks.push(unique.slice(idx, idx + 100));
608371
+ const results = [];
608372
+ for (const chunk of chunks) {
608373
+ const result = await this.apiCall("deleteMessages", { chat_id: chatId, message_ids: chunk });
608374
+ if (!result.ok) throw new Error(String(result.description || "Telegram deleteMessages failed"));
608375
+ results.push({ message_ids: chunk, ok: true });
608376
+ }
608377
+ return {
608378
+ action: "delete_messages",
608379
+ telegram_method: "deleteMessages",
608380
+ ok: true,
608381
+ chat_id: chatId,
608382
+ message_ids: unique,
608383
+ batches: results,
608384
+ bot_rights_checked: !this.telegramTargetLooksPrivate(chatId, currentMsg) && !unique.every((id) => this.isKnownAssistantTelegramMessage(chatId, id)),
608385
+ policy_scope: this.telegramToolPolicy.chatOverrides?.[String(chatId)] ? "chat" : "global/default"
608386
+ };
608387
+ }
608388
+ telegramToolPolicyTarget(args, targetChatId) {
608389
+ const requested = String(args["scope"] || "").trim().toLowerCase();
608390
+ if (requested === "chat" || targetChatId !== void 0 && this.telegramToolHasExplicitTarget(args)) {
608391
+ if (targetChatId === void 0) throw new Error("scope=chat requires target/chat_id.");
608392
+ return { scope: "chat", chatId: targetChatId };
608393
+ }
608394
+ return { scope: "global" };
608395
+ }
608396
+ persistTelegramToolPolicy() {
608397
+ if (!this.repoRoot) return;
608398
+ saveProjectSettings(this.repoRoot, { telegramToolPolicy: this.cloneTelegramToolPolicy(this.telegramToolPolicy) });
608399
+ }
608400
+ setTelegramToolGroupPolicy(args, enabled2, targetChatId) {
608401
+ const group = String(args["group"] || "").trim().toLowerCase();
608402
+ if (!TELEGRAM_TOOL_ACTION_GROUPS.includes(group)) throw new Error(`Unknown Telegram action group: ${group || "(missing)"}`);
608403
+ const target = this.telegramToolPolicyTarget(args, targetChatId);
608404
+ if (target.scope === "chat") {
608405
+ const key = String(target.chatId);
608406
+ this.telegramToolPolicy.chatOverrides ??= {};
608407
+ const override = this.telegramToolPolicy.chatOverrides[key] ?? {};
608408
+ override.enabledActionGroups = { ...override.enabledActionGroups ?? {}, [group]: enabled2 };
608409
+ this.telegramToolPolicy.chatOverrides[key] = override;
608410
+ } else {
608411
+ this.telegramToolPolicy.enabledActionGroups = { ...this.telegramToolPolicy.enabledActionGroups ?? {}, [group]: enabled2 };
608412
+ }
608413
+ this.persistTelegramToolPolicy();
608414
+ return { ok: true, action: enabled2 ? "enable_group" : "disable_group", group, scope: target.scope, chat_id: target.chatId, enabled: enabled2 };
608415
+ }
608416
+ setTelegramToolActionPolicy(args, enabled2, targetChatId) {
608417
+ const toolAction = String(args["tool_action"] ?? args["telegram_action"] ?? "").trim().toLowerCase();
608418
+ if (!toolAction || !(toolAction in TELEGRAM_TOOL_ACTION_GROUP)) throw new Error(`Unknown Telegram action: ${toolAction || "(missing)"}`);
608419
+ const target = this.telegramToolPolicyTarget(args, targetChatId);
608420
+ if (target.scope === "chat") {
608421
+ const key = String(target.chatId);
608422
+ this.telegramToolPolicy.chatOverrides ??= {};
608423
+ const override = this.telegramToolPolicy.chatOverrides[key] ?? {};
608424
+ override.enabledActions = { ...override.enabledActions ?? {}, [toolAction]: enabled2 };
608425
+ this.telegramToolPolicy.chatOverrides[key] = override;
608426
+ } else {
608427
+ this.telegramToolPolicy.enabledActions = { ...this.telegramToolPolicy.enabledActions ?? {}, [toolAction]: enabled2 };
608428
+ }
608429
+ this.persistTelegramToolPolicy();
608430
+ return { ok: true, action: enabled2 ? "enable_action" : "disable_action", tool_action: toolAction, scope: target.scope, chat_id: target.chatId, enabled: enabled2 };
608431
+ }
608432
+ telegramToolButtonPath(nonce) {
608433
+ const safe = nonce.replace(/[^A-Za-z0-9_-]/g, "");
608434
+ return join123(this.telegramToolButtonDir, `${safe}.json`);
608435
+ }
608436
+ writeTelegramToolButtonState(state) {
608437
+ mkdirSync63(this.telegramToolButtonDir, { recursive: true });
608438
+ writeFileSync57(this.telegramToolButtonPath(state.nonce), JSON.stringify(state, null, 2) + "\n", "utf-8");
608439
+ }
608440
+ readTelegramToolButtonState(nonce) {
608441
+ try {
608442
+ const parsed = JSON.parse(readFileSync88(this.telegramToolButtonPath(nonce), "utf-8"));
608443
+ if (!parsed || parsed.expiresAt < Date.now()) return null;
608444
+ return parsed;
608445
+ } catch {
608446
+ return null;
608447
+ }
608448
+ }
608449
+ telegramToolPanelPayload(state) {
608450
+ const policyChatId = state.policyScope === "chat" ? state.policyChatId : void 0;
608451
+ const effective = this.effectiveTelegramToolPolicy(policyChatId);
608452
+ const rows = TELEGRAM_TOOL_ACTION_GROUPS.map((group) => {
608453
+ const enabled2 = effective.enabledActionGroups?.[group] ?? DEFAULT_TELEGRAM_TOOL_GROUP_POLICY[group];
608454
+ return {
608455
+ text: `${enabled2 ? "[x]" : "[ ]"} ${TELEGRAM_TOOL_BUTTON_LABELS[group]}`,
608456
+ callback_data: `omni:v1:tgtool:${state.nonce}:group:${group}`
608457
+ };
608458
+ });
608459
+ const dryRun = effective.dryRunByDefault === true;
608460
+ const requireAdmin = effective.requireAdminForMutation !== false;
608461
+ const keyboard = [];
608462
+ for (let idx = 0; idx < rows.length; idx += 2) keyboard.push(rows.slice(idx, idx + 2));
608463
+ keyboard.push([
608464
+ { text: `${dryRun ? "[x]" : "[ ]"} Dry run`, callback_data: `omni:v1:tgtool:${state.nonce}:flag:dryrun` },
608465
+ { text: `${requireAdmin ? "[x]" : "[ ]"} Admin mutations`, callback_data: `omni:v1:tgtool:${state.nonce}:flag:admin` }
608466
+ ]);
608467
+ const scopeLabel = state.policyScope === "chat" ? `chat ${String(state.policyChatId)}` : "global";
608468
+ return {
608469
+ text: [
608470
+ "<b>Telegram tool controls</b>",
608471
+ `Scope: <code>${escapeTelegramHTML(scopeLabel)}</code>`,
608472
+ "Buttons toggle Omnius policy only. Telegram bot admin rights are still checked before privileged Bot API calls."
608473
+ ].join("\n"),
608474
+ reply_markup: { inline_keyboard: keyboard }
608475
+ };
608476
+ }
608477
+ async sendTelegramToolPanel(panelChatId, policyChatId) {
608478
+ const state = {
608479
+ nonce: randomBytes22(8).toString("hex"),
608480
+ createdAt: Date.now(),
608481
+ expiresAt: Date.now() + 15 * 6e4,
608482
+ panelChatId,
608483
+ policyScope: policyChatId === void 0 ? "global" : "chat",
608484
+ policyChatId
608485
+ };
608486
+ const payload = this.telegramToolPanelPayload(state);
608487
+ const result = await this.apiCall("sendMessage", {
608488
+ chat_id: panelChatId,
608489
+ text: payload.text,
608490
+ parse_mode: "HTML",
608491
+ reply_markup: payload.reply_markup
608492
+ });
608493
+ if (!result.ok) throw new Error(String(result.description || "Telegram sendMessage failed"));
608494
+ this.state.messagesSent++;
608495
+ state.messageId = result.result?.message_id;
608496
+ this.writeTelegramToolButtonState(state);
608497
+ return {
608498
+ action: "render_admin_buttons",
608499
+ telegram_method: "sendMessage",
608500
+ ok: true,
608501
+ chat_id: panelChatId,
608502
+ message_id: state.messageId,
608503
+ policy_scope: state.policyScope,
608504
+ policy_chat_id: state.policyChatId
608505
+ };
608506
+ }
608507
+ async answerCallbackQuery(callbackQueryId, text, showAlert = false) {
608508
+ const result = await this.apiCall("answerCallbackQuery", {
608509
+ callback_query_id: callbackQueryId,
608510
+ ...text ? { text } : {},
608511
+ show_alert: showAlert
608512
+ });
608513
+ return Boolean(result.ok);
608514
+ }
608515
+ async handleTelegramCallbackQuery(callback) {
608516
+ let answerText = "Updated.";
608517
+ let alert = false;
608518
+ try {
608519
+ if (!this.isAdminActor(callback.fromUserId, callback.username)) {
608520
+ answerText = "Only the configured Omnius Telegram admin can change tool policy.";
608521
+ alert = true;
608522
+ return;
608523
+ }
608524
+ const parts = callback.data.split(":");
608525
+ if (parts.length !== 6 || parts[0] !== "omni" || parts[1] !== "v1" || parts[2] !== "tgtool") {
608526
+ answerText = "Unknown Omnius Telegram control.";
608527
+ alert = true;
608528
+ return;
608529
+ }
608530
+ const state = this.readTelegramToolButtonState(parts[3] ?? "");
608531
+ if (!state) {
608532
+ answerText = "This Telegram tool panel expired. Open a new one with /telegram tools.";
608533
+ alert = true;
608534
+ return;
608535
+ }
608536
+ const kind = parts[4];
608537
+ const value2 = parts[5];
608538
+ if (kind === "group") {
608539
+ const group = value2;
608540
+ if (!TELEGRAM_TOOL_ACTION_GROUPS.includes(group)) throw new Error(`Unknown group: ${value2}`);
608541
+ const policy = this.effectiveTelegramToolPolicy(state.policyChatId);
608542
+ const enabled2 = !(policy.enabledActionGroups?.[group] ?? DEFAULT_TELEGRAM_TOOL_GROUP_POLICY[group]);
608543
+ this.setTelegramToolGroupPolicy(
608544
+ { group, scope: state.policyScope, ...state.policyChatId !== void 0 ? { target: String(state.policyChatId) } : {} },
608545
+ enabled2,
608546
+ state.policyChatId
608547
+ );
608548
+ answerText = `${TELEGRAM_TOOL_BUTTON_LABELS[group]} ${enabled2 ? "enabled" : "disabled"}.`;
608549
+ } else if (kind === "flag" && value2 === "dryrun") {
608550
+ this.setTelegramToolBooleanPolicy(state, "dryRunByDefault", this.effectiveTelegramToolPolicy(state.policyChatId).dryRunByDefault !== true);
608551
+ answerText = "Dry-run default toggled.";
608552
+ } else if (kind === "flag" && value2 === "admin") {
608553
+ this.setTelegramToolBooleanPolicy(state, "requireAdminForMutation", this.effectiveTelegramToolPolicy(state.policyChatId).requireAdminForMutation === false);
608554
+ answerText = "Admin mutation requirement toggled.";
608555
+ } else {
608556
+ throw new Error("Unsupported Telegram tool panel action.");
608557
+ }
608558
+ const messageId = state.messageId ?? callback.messageId;
608559
+ if (messageId !== void 0) {
608560
+ const payload = this.telegramToolPanelPayload(state);
608561
+ await this.apiCall("editMessageText", {
608562
+ chat_id: state.panelChatId,
608563
+ message_id: messageId,
608564
+ text: payload.text,
608565
+ parse_mode: "HTML",
608566
+ reply_markup: payload.reply_markup
608567
+ });
608568
+ }
608569
+ } catch (err) {
608570
+ answerText = err instanceof Error ? err.message : String(err);
608571
+ alert = true;
608572
+ } finally {
608573
+ await this.answerCallbackQuery(callback.id, answerText.slice(0, 180), alert).catch(() => false);
608574
+ }
608575
+ }
608576
+ async runTelegramToolActionForAdmin(action, args) {
608577
+ return this.executeTelegramToolAction(action.trim().toLowerCase(), { ...args, action }, {
608578
+ context: "telegram-admin-dm",
608579
+ repoRoot: this.repoRoot ?? "."
608580
+ });
608581
+ }
608582
+ setTelegramToolBooleanPolicy(state, key, value2) {
608583
+ if (state.policyScope === "chat" && state.policyChatId !== void 0) {
608584
+ const chatKey = String(state.policyChatId);
608585
+ this.telegramToolPolicy.chatOverrides ??= {};
608586
+ const override = this.telegramToolPolicy.chatOverrides[chatKey] ?? {};
608587
+ override[key] = value2;
608588
+ this.telegramToolPolicy.chatOverrides[chatKey] = override;
608589
+ } else {
608590
+ this.telegramToolPolicy[key] = value2;
608591
+ }
608592
+ this.persistTelegramToolPolicy();
608593
+ }
607562
608594
  buildTelegramReminderTool(context2, repoRoot, chatId, currentMsg) {
607563
608595
  const rawChatId = String(chatId ?? currentMsg?.chatId ?? "unknown");
607564
608596
  const isPrivate2 = currentMsg?.chatType === "private";
@@ -607730,6 +608762,10 @@ Scoped workspace: ${scopedRoot}`,
607730
608762
  if (!target.ok) {
607731
608763
  return { success: false, output: "", error: target.error, durationMs: performance.now() - start2 };
607732
608764
  }
608765
+ const gate = bridge.assertTelegramToolActionAllowed("send_file", context2, currentMsg, target.chatId, args);
608766
+ if (!gate.ok) {
608767
+ return { success: false, output: "", error: gate.error, durationMs: performance.now() - start2 };
608768
+ }
607733
608769
  const file = bridge.resolveTelegramFilePath(rawPath, repoRoot, scopedRoot);
607734
608770
  if (!file.ok) {
607735
608771
  return { success: false, output: "", error: file.error, durationMs: performance.now() - start2 };
@@ -607737,13 +608773,33 @@ Scoped workspace: ${scopedRoot}`,
607737
608773
  const kind = normalizeTelegramSendKind(args["kind"], file.path);
607738
608774
  const caption = typeof args["caption"] === "string" ? args["caption"].trim().slice(0, 1024) : void 0;
607739
608775
  const replyTo = Number(args["reply_to_message_id"]);
608776
+ const replyToMessageId = Number.isFinite(replyTo) && replyTo > 0 ? Math.floor(replyTo) : void 0;
608777
+ const ledgerPath = file.logicalPath ?? file.path;
608778
+ const sendFingerprint = bridge.telegramFileSendFingerprint(
608779
+ target.chatId,
608780
+ ledgerPath,
608781
+ kind,
608782
+ caption,
608783
+ replyToMessageId
608784
+ );
608785
+ if (bridge.telegramFileSendAlreadyDeliveredForMessage(currentMsg, sendFingerprint)) {
608786
+ return {
608787
+ success: true,
608788
+ output: `Telegram file already sent in this turn: ${basename27(file.path)} as ${kind} to ${String(target.chatId)}`,
608789
+ llmContent: `Already sent ${basename27(file.path)} to Telegram as ${kind}; do not send it again.`,
608790
+ durationMs: performance.now() - start2,
608791
+ mutated: false,
608792
+ mutatedFiles: []
608793
+ };
608794
+ }
607740
608795
  try {
607741
608796
  const messageId = await bridge.sendTelegramFileToChat(target.chatId, file.path, {
607742
608797
  kind,
607743
608798
  caption: caption || void 0,
607744
- replyToMessageId: Number.isFinite(replyTo) && replyTo > 0 ? Math.floor(replyTo) : void 0
608799
+ replyToMessageId
607745
608800
  });
607746
- bridge.rememberTelegramDeliveredArtifactForMessage(currentMsg, file.logicalPath ?? file.path);
608801
+ bridge.rememberTelegramFileSendForMessage(currentMsg, sendFingerprint);
608802
+ bridge.rememberTelegramDeliveredArtifactForMessage(currentMsg, ledgerPath);
607747
608803
  return {
607748
608804
  success: true,
607749
608805
  output: `Sent Telegram file: ${basename27(file.path)} as ${kind} to ${String(target.chatId)}${messageId ? ` (message_id ${messageId})` : ""}`,
@@ -607841,6 +608897,27 @@ ${knownList}` : "Private-user telegram_send_file target must be this DM or a kno
607841
608897
  if (!subAgent) return;
607842
608898
  this.rememberTelegramDeliveredArtifact(subAgent, path11);
607843
608899
  }
608900
+ telegramFileSendFingerprint(chatId, path11, kind, caption, replyToMessageId) {
608901
+ return stableTelegramValueKey({
608902
+ chatId: String(chatId),
608903
+ path: resolve41(path11),
608904
+ kind,
608905
+ caption: caption ?? "",
608906
+ replyToMessageId: replyToMessageId ?? null
608907
+ });
608908
+ }
608909
+ telegramFileSendAlreadyDeliveredForMessage(msg, fingerprint) {
608910
+ if (!msg) return false;
608911
+ const subAgent = this.subAgents.get(this.sessionKeyForMessage(msg));
608912
+ return Boolean(subAgent?.deliveredFileSends?.has(fingerprint));
608913
+ }
608914
+ rememberTelegramFileSendForMessage(msg, fingerprint) {
608915
+ if (!msg) return;
608916
+ const subAgent = this.subAgents.get(this.sessionKeyForMessage(msg));
608917
+ if (!subAgent) return;
608918
+ subAgent.deliveredFileSends ??= /* @__PURE__ */ new Set();
608919
+ subAgent.deliveredFileSends.add(fingerprint);
608920
+ }
607844
608921
  /** Check if a message is from the admin user (uses fromUserId, NOT chatId) */
607845
608922
  isAdminUser(msg) {
607846
608923
  if (!this.adminUserId) return false;
@@ -608057,23 +609134,25 @@ ${text}`.trim());
608057
609134
  }
608058
609135
  // ── Message sending ───────────────────────────────────────────────────
608059
609136
  /** Send a response back to a Telegram chat (Markdown → HTML conversion) */
608060
- async sendMessage(chatId, text, replyToMessageId) {
609137
+ async sendMessage(chatId, text, replyToMessageId, options2 = {}) {
608061
609138
  const extracted = extractMediaReferences(text);
609139
+ const mediaRefs = this.filterTelegramMediaReferences(extracted.media, options2);
608062
609140
  const html = convertMarkdownToTelegramHTML(extracted.text);
608063
- const msgId = extracted.text.trim() ? await this.sendMessageHTML(chatId, html, replyToMessageId) : null;
608064
- for (const media of extracted.media) {
609141
+ const msgId = extracted.text.trim() ? await this.sendMessageHTML(chatId, html, replyToMessageId, options2) : null;
609142
+ for (const media of mediaRefs) {
608065
609143
  await this.sendMediaReference(chatId, media, { replyToMessageId }).catch(() => null);
608066
609144
  }
608067
609145
  return msgId;
608068
609146
  }
608069
609147
  /** Send an HTML-formatted message to a Telegram chat */
608070
- async sendMessageHTML(chatId, html, replyToMessageId) {
609148
+ async sendMessageHTML(chatId, html, replyToMessageId, options2 = {}) {
608071
609149
  const extracted = extractMediaReferences(html);
609150
+ const mediaRefs = this.filterTelegramMediaReferences(extracted.media, options2);
608072
609151
  const sendHtml = extracted.text || (extracted.media.length > 0 ? "" : html);
608073
609152
  let sentId = null;
608074
609153
  if (!sendHtml.trim()) {
608075
- for (let idx = 0; idx < extracted.media.length; idx++) {
608076
- const media = extracted.media[idx];
609154
+ for (let idx = 0; idx < mediaRefs.length; idx++) {
609155
+ const media = mediaRefs[idx];
608077
609156
  const mediaId = await this.sendMediaReference(
608078
609157
  chatId,
608079
609158
  media,
@@ -608112,11 +609191,24 @@ ${text}`.trim());
608112
609191
  }
608113
609192
  }
608114
609193
  }
608115
- for (const media of extracted.media) {
609194
+ for (const media of mediaRefs) {
608116
609195
  await this.sendMediaReference(chatId, media).catch(() => null);
608117
609196
  }
608118
609197
  return sentId;
608119
609198
  }
609199
+ filterTelegramMediaReferences(media, options2) {
609200
+ const suppress = options2.suppressMedia;
609201
+ if (!suppress) return media;
609202
+ return media.filter((ref) => !suppress(ref));
609203
+ }
609204
+ deliveredArtifactMediaSuppressorForMessage(msg) {
609205
+ const subAgent = this.subAgents.get(this.sessionKeyForMessage(msg));
609206
+ const delivered = new Set(
609207
+ (subAgent?.deliveredArtifacts ?? []).map((path11) => resolve41(path11))
609208
+ );
609209
+ if (delivered.size === 0) return void 0;
609210
+ return (media) => media.source === "file" && delivered.has(resolve41(media.value));
609211
+ }
608120
609212
  async replyToTelegramMessage(msg, text, options2 = {}) {
608121
609213
  if (msg.guestQueryId) {
608122
609214
  return this.answerGuestQuery(msg.guestQueryId, text, {
@@ -608582,11 +609674,16 @@ ${caption}\r
608582
609674
  const result = await this.apiCall("getUpdates", {
608583
609675
  offset: this.lastUpdateId + 1,
608584
609676
  timeout: 30,
608585
- allowed_updates: ["message", "guest_message", "poll", "message_reaction", "message_reaction_count"]
609677
+ allowed_updates: ["message", "guest_message", "callback_query", "poll", "message_reaction", "message_reaction_count"]
608586
609678
  });
608587
609679
  if (result.ok && Array.isArray(result.result)) {
608588
609680
  for (const update2 of result.result) {
608589
609681
  this.lastUpdateId = update2.update_id;
609682
+ const callback = normalizeTelegramCallbackQuery(update2);
609683
+ if (callback) {
609684
+ await this.handleTelegramCallbackQuery(callback);
609685
+ continue;
609686
+ }
608590
609687
  const msg = normalizeTelegramUpdate(update2);
608591
609688
  if (!msg) continue;
608592
609689
  const isAdmin = this.adminUserId ? String(msg.fromUserId) === this.adminUserId || msg.username === this.adminUserId : false;
@@ -612296,7 +613393,7 @@ __export(runtime_keys_exports, {
612296
613393
  import { existsSync as existsSync115, readFileSync as readFileSync94, writeFileSync as writeFileSync61, mkdirSync as mkdirSync69, chmodSync } from "node:fs";
612297
613394
  import { join as join129 } from "node:path";
612298
613395
  import { homedir as homedir40 } from "node:os";
612299
- import { randomBytes as randomBytes22 } from "node:crypto";
613396
+ import { randomBytes as randomBytes23 } from "node:crypto";
612300
613397
  function ensureDir2() {
612301
613398
  const dir = join129(homedir40(), ".omnius");
612302
613399
  if (!existsSync115(dir)) mkdirSync69(dir, { recursive: true });
@@ -612322,7 +613419,7 @@ function persistAll(records) {
612322
613419
  }
612323
613420
  function mintKey(args) {
612324
613421
  const records = loadAll();
612325
- const key = `omnius_${randomBytes22(32).toString("hex")}`;
613422
+ const key = `omnius_${randomBytes23(32).toString("hex")}`;
612326
613423
  const record = {
612327
613424
  key,
612328
613425
  scope: args.scope,
@@ -615440,11 +616537,11 @@ async function handleAimsIncidentPost(ctx3) {
615440
616537
  }));
615441
616538
  return true;
615442
616539
  }
615443
- const { randomBytes: randomBytes26 } = await import("node:crypto");
616540
+ const { randomBytes: randomBytes27 } = await import("node:crypto");
615444
616541
  const record = await withAimsLock("incidents.json", () => {
615445
616542
  const existing = readAimsFile("incidents.json", []);
615446
616543
  const rec = {
615447
- id: `INC-${Date.now()}-${randomBytes26(4).toString("hex")}`,
616544
+ id: `INC-${Date.now()}-${randomBytes27(4).toString("hex")}`,
615448
616545
  ts: (/* @__PURE__ */ new Date()).toISOString(),
615449
616546
  raised_by: user ?? "anonymous",
615450
616547
  status: "open",
@@ -624899,7 +625996,7 @@ var init_usage_tracker = __esm({
624899
625996
  import { existsSync as existsSync120, readFileSync as readFileSync98, writeFileSync as writeFileSync64, mkdirSync as mkdirSync72, readdirSync as readdirSync41, unlinkSync as unlinkSync24 } from "node:fs";
624900
625997
  import { join as join134 } from "node:path";
624901
625998
  import { homedir as homedir43 } from "node:os";
624902
- import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv5, randomBytes as randomBytes23, scryptSync as scryptSync3 } from "node:crypto";
625999
+ import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv5, randomBytes as randomBytes24, scryptSync as scryptSync3 } from "node:crypto";
624903
626000
  function globalProfileDir() {
624904
626001
  return join134(homedir43(), ".omnius", "profiles");
624905
626002
  }
@@ -624983,9 +626080,9 @@ function deleteProfile(name10, scope = "global", projectDir2) {
624983
626080
  return false;
624984
626081
  }
624985
626082
  function encryptProfile(profile, password) {
624986
- const salt = randomBytes23(32);
626083
+ const salt = randomBytes24(32);
624987
626084
  const key = scryptSync3(password, salt, 32);
624988
- const iv = randomBytes23(16);
626085
+ const iv = randomBytes24(16);
624989
626086
  const cipher = createCipheriv5("aes-256-gcm", key, iv);
624990
626087
  const plaintext = JSON.stringify(profile);
624991
626088
  const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
@@ -625638,7 +626735,7 @@ import { dirname as dirname37, join as join137, resolve as resolve45 } from "nod
625638
626735
  import { homedir as homedir45 } from "node:os";
625639
626736
  import { spawn as spawn29, execSync as execSync57 } from "node:child_process";
625640
626737
  import { mkdirSync as mkdirSync74, writeFileSync as writeFileSync66, readFileSync as readFileSync99, readdirSync as readdirSync42, existsSync as existsSync122, watch as fsWatch3, renameSync as renameSync8, unlinkSync as unlinkSync25 } from "node:fs";
625641
- import { randomBytes as randomBytes24, randomUUID as randomUUID16 } from "node:crypto";
626738
+ import { randomBytes as randomBytes25, randomUUID as randomUUID16 } from "node:crypto";
625642
626739
  import { createHash as createHash27 } from "node:crypto";
625643
626740
  function memoryDbPaths3(baseDir = process.cwd()) {
625644
626741
  const dir = join137(baseDir, ".omnius");
@@ -627314,7 +628411,7 @@ ${messages2[firstSystemIdx].content}`
627314
628411
  messages2.unshift({ role: "system", content: SYSTEM_FACTUAL_FIRST });
627315
628412
  }
627316
628413
  }
627317
- const chatId = `chatcmpl-${randomBytes24(12).toString("hex")}`;
628414
+ const chatId = `chatcmpl-${randomBytes25(12).toString("hex")}`;
627318
628415
  const turnsLog = [];
627319
628416
  for (let turn = 1; turn <= maxTurns; turn++) {
627320
628417
  if (Date.now() > totalDeadline) {
@@ -627580,7 +628677,7 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
627580
628677
  "Cache-Control": "no-cache",
627581
628678
  "Connection": "keep-alive"
627582
628679
  });
627583
- const chatId = `chatcmpl-${randomBytes24(12).toString("hex")}`;
628680
+ const chatId = `chatcmpl-${randomBytes25(12).toString("hex")}`;
627584
628681
  let buffer2 = "";
627585
628682
  ollamaStream(
627586
628683
  targetUrl,
@@ -627622,7 +628719,7 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
627622
628719
  if (ollamaChunk.message.content) delta.content = ollamaChunk.message.content;
627623
628720
  if (ollamaChunk.message.tool_calls) {
627624
628721
  delta.tool_calls = ollamaChunk.message.tool_calls.map((tc, idx) => ({
627625
- id: tc.id || `call_${randomBytes24(8).toString("hex")}`,
628722
+ id: tc.id || `call_${randomBytes25(8).toString("hex")}`,
627626
628723
  type: "function",
627627
628724
  function: {
627628
628725
  name: tc?.function?.name ?? tc?.name ?? "",
@@ -627702,14 +628799,14 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
627702
628799
  if (ollamaResp.eval_count) metrics.totalTokensOut += ollamaResp.eval_count;
627703
628800
  if (ollamaResp.prompt_eval_count) metrics.totalTokensIn += ollamaResp.prompt_eval_count;
627704
628801
  trackTokens("local", ollamaResp.prompt_eval_count ?? 0, ollamaResp.eval_count ?? 0);
627705
- const chatId = `chatcmpl-${randomBytes24(12).toString("hex")}`;
628802
+ const chatId = `chatcmpl-${randomBytes25(12).toString("hex")}`;
627706
628803
  const responseMessage = {
627707
628804
  role: ollamaResp.message?.role ?? "assistant",
627708
628805
  content: ollamaResp.message?.content ?? ""
627709
628806
  };
627710
628807
  if (ollamaResp.message?.tool_calls && ollamaResp.message.tool_calls.length > 0) {
627711
628808
  responseMessage.tool_calls = ollamaResp.message.tool_calls.map((tc, idx) => ({
627712
- id: tc.id || `call_${randomBytes24(8).toString("hex")}`,
628809
+ id: tc.id || `call_${randomBytes25(8).toString("hex")}`,
627713
628810
  type: "function",
627714
628811
  function: {
627715
628812
  name: tc?.function?.name ?? tc?.name ?? "",
@@ -628494,7 +629591,7 @@ async function handleV1Run(req2, res) {
628494
629591
  return;
628495
629592
  }
628496
629593
  }
628497
- const id = `job-${randomBytes24(8).toString("hex")}`;
629594
+ const id = `job-${randomBytes25(8).toString("hex")}`;
628498
629595
  const dir = jobsDir();
628499
629596
  const workingDir = requestBody["working_directory"] || req2.headers["x-working-directory"];
628500
629597
  const isolate = requestBody["isolate"] === true;
@@ -639143,6 +640240,17 @@ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analy
639143
640240
  },
639144
640241
  // Telegram bridge
639145
640242
  async telegramStart(token, adminId) {
640243
+ if (telegramBridge?.isActive) {
640244
+ writeContent(
640245
+ () => renderWarning("Telegram bridge already active. Use /telegram stop before restarting.")
640246
+ );
640247
+ showPrompt();
640248
+ return;
640249
+ }
640250
+ if (telegramBridge) {
640251
+ telegramBridge.stop();
640252
+ telegramBridge = null;
640253
+ }
639146
640254
  telegramBridge = new TelegramBridge(
639147
640255
  token,
639148
640256
  (msg) => {
@@ -639175,6 +640283,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
639175
640283
  telegramBridge.setContextWindowSize(resolvedContextWindowSize);
639176
640284
  }
639177
640285
  telegramBridge.setInteractionMode(savedSettings.telegramMode ?? "auto");
640286
+ telegramBridge.setTelegramToolPolicy(savedSettings.telegramToolPolicy);
639178
640287
  if (adminId) {
639179
640288
  telegramBridge.setAdmin(adminId);
639180
640289
  }
@@ -639388,6 +640497,34 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
639388
640497
  if (!telegramBridge?.isActive) throw new Error("Telegram bridge not active.");
639389
640498
  return telegramBridge.deleteAllMessageReactions(chatId, options2);
639390
640499
  },
640500
+ async telegramDeleteMessage(chatId, messageId, reason) {
640501
+ if (!telegramBridge?.isActive) throw new Error("Telegram bridge not active.");
640502
+ return telegramBridge.runTelegramToolActionForAdmin("delete_message", {
640503
+ target: String(chatId),
640504
+ message_id: messageId,
640505
+ reason: reason || "TUI admin delete-message command"
640506
+ });
640507
+ },
640508
+ async telegramDeleteMessages(chatId, messageIds, reason) {
640509
+ if (!telegramBridge?.isActive) throw new Error("Telegram bridge not active.");
640510
+ return telegramBridge.runTelegramToolActionForAdmin("delete_messages", {
640511
+ target: String(chatId),
640512
+ message_ids: messageIds,
640513
+ reason: reason || "TUI admin delete-messages command"
640514
+ });
640515
+ },
640516
+ telegramGetToolPolicy() {
640517
+ return telegramBridge?.getTelegramToolPolicy() ?? savedSettings.telegramToolPolicy ?? {};
640518
+ },
640519
+ telegramSetToolPolicy(policy) {
640520
+ savedSettings.telegramToolPolicy = policy;
640521
+ saveProjectSettings(repoRoot, { telegramToolPolicy: policy });
640522
+ telegramBridge?.setTelegramToolPolicy(policy);
640523
+ },
640524
+ async telegramSendToolPanel(chatId, policyChatId) {
640525
+ if (!telegramBridge?.isActive) throw new Error("Telegram bridge not active.");
640526
+ return telegramBridge.sendTelegramToolPanel(chatId, policyChatId);
640527
+ },
639391
640528
  async telegramGetChatAdministrators(chatId, returnBots) {
639392
640529
  if (!telegramBridge?.isActive) throw new Error("Telegram bridge not active.");
639393
640530
  return telegramBridge.getChatAdministrators(chatId, returnBots);
@@ -642204,7 +643341,7 @@ __export(run_exports, {
642204
643341
  import { resolve as resolve47 } from "node:path";
642205
643342
  import { spawn as spawn30 } from "node:child_process";
642206
643343
  import { mkdirSync as mkdirSync77, writeFileSync as writeFileSync69, readFileSync as readFileSync102, readdirSync as readdirSync44, existsSync as existsSync124 } from "node:fs";
642207
- import { randomBytes as randomBytes25 } from "node:crypto";
643344
+ import { randomBytes as randomBytes26 } from "node:crypto";
642208
643345
  import { join as join140 } from "node:path";
642209
643346
  function jobsDir2(repoPath) {
642210
643347
  const root = resolve47(repoPath ?? process.cwd());
@@ -642302,7 +643439,7 @@ function extractSummary(captured) {
642302
643439
  return lines.slice(-3).join(" ").slice(0, 200);
642303
643440
  }
642304
643441
  async function runBackground(task, config, opts) {
642305
- const id = `job-${randomBytes25(3).toString("hex")}`;
643442
+ const id = `job-${randomBytes26(3).toString("hex")}`;
642306
643443
  const dir = jobsDir2(opts.repoPath);
642307
643444
  const repoRoot = resolve47(opts.repoPath ?? process.cwd());
642308
643445
  const job = {