@wrongstack/telegram 0.77.0 → 0.84.1

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.d.ts CHANGED
@@ -4,8 +4,8 @@ interface TelegramIncomingMessage {
4
4
  messageId: number;
5
5
  chatId: number;
6
6
  chatType: string;
7
- userId?: number;
8
- userName?: string;
7
+ userId?: number | undefined;
8
+ userName?: string | undefined;
9
9
  text: string;
10
10
  timestamp: number;
11
11
  }
@@ -17,34 +17,34 @@ interface TelegramPluginConfig {
17
17
  * Default chat ID for outgoing notifications.
18
18
  * The agent's `telegram_send` tool can override per-call.
19
19
  */
20
- notifyChatId?: string | number;
20
+ notifyChatId?: string | number | undefined;
21
21
  /**
22
22
  * List of user/chat IDs allowed to interact with the bot.
23
23
  * Empty = allow all. Recommended to set in production.
24
24
  */
25
- allowedUsers?: Array<string | number>;
25
+ allowedUsers?: Array<string | number> | undefined;
26
26
  /**
27
27
  * List of group/chat IDs the bot is allowed to read from.
28
28
  * Empty = allow all. Narrow this to prevent noise.
29
29
  */
30
- allowedChats?: Array<string | number>;
30
+ allowedChats?: Array<string | number> | undefined;
31
31
  /** Polling interval in seconds (default: 2). */
32
- pollIntervalSec?: number;
32
+ pollIntervalSec?: number | undefined;
33
33
  /** Notify on Telegram when a session ends. */
34
- notifyOnSessionEnd?: boolean;
34
+ notifyOnSessionEnd?: boolean | undefined;
35
35
  /** Notify when a tool runs longer than this threshold (ms). Set 0 to disable. */
36
- longToolThresholdMs?: number;
36
+ longToolThresholdMs?: number | undefined;
37
37
  /** Notify (humanized) when a `delegate` subagent finishes. Default: true. */
38
- notifyOnDelegate?: boolean;
38
+ notifyOnDelegate?: boolean | undefined;
39
39
  /** Maximum message length for Telegram (Telegram caps at 4096). */
40
- maxMessageLength?: number;
40
+ maxMessageLength?: number | undefined;
41
41
  /**
42
42
  * Path to a file that stores the Telegram polling offset. When set,
43
43
  * the offset is persisted on every successful poll and restored on startup,
44
44
  * preventing message replay after crashes or restarts.
45
45
  * The directory must already exist and be writable.
46
46
  */
47
- offsetStoragePath?: string;
47
+ offsetStoragePath?: string | undefined;
48
48
  }
49
49
 
50
50
  declare const plugin: Plugin;
package/dist/index.js CHANGED
@@ -1,6 +1,13 @@
1
+ import { sleep } from '@wrongstack/core/utils';
2
+
1
3
  // src/bot.ts
4
+ function redactToken(url, token) {
5
+ return url.replace(token, "[REDACTED]");
6
+ }
2
7
  var TelegramBot = class {
3
8
  baseUrl;
9
+ /** Base URL with token redacted, safe to use in log calls. */
10
+ safeBaseUrl;
4
11
  pollIntervalMs;
5
12
  allowedUsers;
6
13
  allowedChats;
@@ -18,6 +25,7 @@ var TelegramBot = class {
18
25
  buffer = [];
19
26
  constructor(opts) {
20
27
  this.baseUrl = `https://api.telegram.org/bot${opts.token}`;
28
+ this.safeBaseUrl = redactToken(this.baseUrl, opts.token);
21
29
  this.pollIntervalMs = opts.pollIntervalSec * 1e3;
22
30
  this.allowedUsers = opts.allowedUsers;
23
31
  this.allowedChats = opts.allowedChats;
@@ -37,7 +45,7 @@ var TelegramBot = class {
37
45
  if (this.pollActive) return;
38
46
  this.pollActive = true;
39
47
  this._startedAt = Date.now();
40
- this.log.info("Telegram bot polling started");
48
+ this.log.info(`Telegram bot polling started (${this.safeBaseUrl})`);
41
49
  this.schedulePoll();
42
50
  }
43
51
  /** Stop polling and cancel all in-flight requests. */
@@ -74,7 +82,8 @@ var TelegramBot = class {
74
82
  const before = this.buffer.length;
75
83
  let i = this.buffer.length;
76
84
  while (i-- > 0) {
77
- if (this.buffer[i].messageId <= lastMessageId) {
85
+ const buffered = this.buffer[i];
86
+ if (buffered && buffered.messageId <= lastMessageId) {
78
87
  this.buffer.splice(0, i + 1);
79
88
  break;
80
89
  }
@@ -217,9 +226,6 @@ var TelegramBot = class {
217
226
  }
218
227
  }
219
228
  };
220
- function sleep(ms) {
221
- return new Promise((r) => setTimeout(r, ms));
222
- }
223
229
  function truncateForTelegram(text, maxLen = 4e3) {
224
230
  if (text.length <= maxLen) return text;
225
231
  const cut = text.lastIndexOf("\n", maxLen - 20);
@@ -318,6 +324,12 @@ function formatDelegateCompleted(e) {
318
324
  }
319
325
 
320
326
  // src/slash-commands/index.ts
327
+ function expectDefined(value) {
328
+ if (value === null || value === void 0) {
329
+ throw new Error("Expected value to be defined");
330
+ }
331
+ return value;
332
+ }
321
333
  function tgStatusCommand(bot, cfg) {
322
334
  return {
323
335
  name: "status",
@@ -336,7 +348,7 @@ allowlist status, and notification settings.`,
336
348
  `Running: ${bot.running ? "yes" : "no"}`,
337
349
  `Started: ${bot.startedAt ? new Date(bot.startedAt).toLocaleTimeString() : "N/A"}`,
338
350
  `Poll: every ${cfg.pollIntervalSec ?? 2}s`,
339
- `Allowed: ${(cfg.allowedUsers?.length ?? 0) > 0 ? `${cfg.allowedUsers.length} users` : "everyone (users)"} / ${(cfg.allowedChats?.length ?? 0) > 0 ? `${cfg.allowedChats.length} chats` : "everyone (chats)"}`,
351
+ `Allowed: ${(cfg.allowedUsers?.length ?? 0) > 0 ? `${cfg.allowedUsers?.length} users` : "everyone (users)"} / ${(cfg.allowedChats?.length ?? 0) > 0 ? `${cfg.allowedChats?.length} chats` : "everyone (chats)"}`,
340
352
  `Notify: sessionEnd=${cfg.notifyOnSessionEnd ?? false}, longTool=${cfg.longToolThresholdMs ? `${cfg.longToolThresholdMs}ms` : "off"}`
341
353
  ];
342
354
  return { message: lines.join("\n") };
@@ -364,8 +376,8 @@ Examples:
364
376
  let text;
365
377
  const parts = args.trim().split(/\s+/);
366
378
  const maybeId = parts[0];
367
- if (/^\d+$/.test(maybeId) && parts.length > 1) {
368
- chatId = maybeId;
379
+ if (/^\d+$/.test(expectDefined(maybeId)) && parts.length > 1) {
380
+ chatId = expectDefined(maybeId);
369
381
  text = parts.slice(1).join(" ");
370
382
  } else if (defaultChatId) {
371
383
  chatId = defaultChatId;
@@ -516,6 +528,12 @@ function makeTelegramSendTool(opts) {
516
528
  }
517
529
 
518
530
  // src/index.ts
531
+ function expectDefined2(value) {
532
+ if (value === null || value === void 0) {
533
+ throw new Error("Expected value to be defined");
534
+ }
535
+ return value;
536
+ }
519
537
  var teardownState = null;
520
538
  var plugin = {
521
539
  name: PLUGIN_NAME,
@@ -540,7 +558,7 @@ var plugin = {
540
558
  log.info("Starting Telegram plugin...");
541
559
  const bot = new TelegramBot({
542
560
  token: cfg.botToken,
543
- pollIntervalSec: cfg.pollIntervalSec,
561
+ pollIntervalSec: cfg.pollIntervalSec ?? 2,
544
562
  allowedUsers: new Set((cfg.allowedUsers ?? []).map(String)),
545
563
  allowedChats: new Set((cfg.allowedChats ?? []).map(String)),
546
564
  bufferSize: 50,
@@ -555,7 +573,7 @@ var plugin = {
555
573
  const sendTool = makeTelegramSendTool({
556
574
  bot,
557
575
  defaultChatId: cfg.notifyChatId,
558
- maxMessageLength: cfg.maxMessageLength,
576
+ maxMessageLength: cfg.maxMessageLength ?? 4e3,
559
577
  log
560
578
  });
561
579
  const readTool = makeTelegramReadTool({ bot });
@@ -601,7 +619,7 @@ var plugin = {
601
619
  `Output: ${outputTokens} tokens`,
602
620
  `Total: ${totalTokens} tokens`
603
621
  ].join("\n");
604
- void bot.sendMessage(cfg.notifyChatId, msg).catch((err) => {
622
+ void bot.sendMessage(expectDefined2(cfg.notifyChatId), msg).catch((err) => {
605
623
  log.warn(`Failed to send session end notification: ${err.message}`);
606
624
  });
607
625
  })
@@ -610,7 +628,7 @@ var plugin = {
610
628
  if (cfg.longToolThresholdMs && cfg.longToolThresholdMs > 0 && cfg.notifyChatId) {
611
629
  offs.push(
612
630
  api.events.on("tool.executed", (event) => {
613
- if (event.durationMs < cfg.longToolThresholdMs) return;
631
+ if (event.durationMs < expectDefined2(cfg.longToolThresholdMs)) return;
614
632
  const sec = (event.durationMs / 1e3).toFixed(1);
615
633
  const status = event.ok ? "\u2705" : "\u274C";
616
634
  const preview = event.output ? truncateForTelegram(event.output, 500) : "(no output)";
@@ -619,7 +637,7 @@ var plugin = {
619
637
  "",
620
638
  preview
621
639
  ].join("\n");
622
- void bot.sendMessage(cfg.notifyChatId, msg).catch((err) => {
640
+ void bot.sendMessage(expectDefined2(cfg.notifyChatId), msg).catch((err) => {
623
641
  log.warn(`Failed to send tool notification: ${err.message}`);
624
642
  });
625
643
  })
@@ -632,7 +650,7 @@ var plugin = {
632
650
  formatDelegateCompleted(event),
633
651
  cfg.maxMessageLength
634
652
  );
635
- void bot.sendMessage(cfg.notifyChatId, msg).catch((err) => {
653
+ void bot.sendMessage(expectDefined2(cfg.notifyChatId), msg).catch((err) => {
636
654
  log.warn(`Failed to send delegate notification: ${err.message}`);
637
655
  });
638
656
  })
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bot.ts","../src/config.ts","../src/format.ts","../src/slash-commands/index.ts","../src/tools/telegram-read.ts","../src/tools/telegram-send.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Logger } from '@wrongstack/core';\n\n// ---------------------------------------------------------------------------\n// Redaction helpers (for future use)\n// ---------------------------------------------------------------------------\n// If logging URLs that contain the bot token in the future, use:\n// function redactToken(url: string, token: string): string {\n// return url.replace(token, '[REDACTED]');\n// }\n\n// ---------------------------------------------------------------------------\n// Telegram Bot API types (subset used by this plugin)\n// ---------------------------------------------------------------------------\n\ninterface TgUser {\n id: number;\n is_bot: boolean;\n first_name: string;\n username?: string;\n}\n\ninterface TgChat {\n id: number;\n type: 'private' | 'group' | 'supergroup' | 'channel';\n title?: string;\n username?: string;\n}\n\ninterface TgMessage {\n message_id: number;\n from?: TgUser;\n chat: TgChat;\n date: number;\n text?: string;\n}\n\ninterface TgUpdate {\n update_id: number;\n message?: TgMessage;\n edited_message?: TgMessage;\n}\n\ninterface TgResponse<T> {\n ok: boolean;\n result?: T;\n description?: string;\n error_code?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Incoming message shape emitted as a custom event\n// ---------------------------------------------------------------------------\n\nexport interface TelegramIncomingMessage {\n messageId: number;\n chatId: number;\n chatType: string;\n userId?: number;\n userName?: string;\n text: string;\n timestamp: number;\n}\n\n// ---------------------------------------------------------------------------\n// Bot options\n// ---------------------------------------------------------------------------\n\nexport interface TelegramBotOptions {\n token: string;\n pollIntervalSec: number;\n allowedUsers: Set<string>;\n allowedChats: Set<string>;\n /** Max messages to buffer for the agent to read. Default: 50. */\n bufferSize: number;\n log: Logger;\n /** Called for each incoming message that passes allowlist checks. */\n onMessage(msg: TelegramIncomingMessage): void;\n /**\n * Optional path to a file that stores the polling offset. When provided,\n * the offset is persisted on every successful poll and restored on startup,\n * preventing message replay after crashes or restarts.\n */\n offsetStoragePath?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Bot\n// ---------------------------------------------------------------------------\n\nexport class TelegramBot {\n private readonly baseUrl: string;\n private readonly pollIntervalMs: number;\n private readonly allowedUsers: Set<string>;\n private readonly allowedChats: Set<string>;\n private readonly log: Logger;\n private readonly onMessage: (msg: TelegramIncomingMessage) => void;\n private readonly controller = new AbortController();\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private pollActive = false;\n private offset = 0;\n private _startedAt: number | null = null;\n /** If set, the offset is persisted here after each successful poll. */\n private readonly offsetStoragePath?: string;\n\n // Circular buffer for incoming messages\n private readonly bufferMax: number;\n private readonly buffer: TelegramIncomingMessage[] = [];\n\n constructor(opts: TelegramBotOptions) {\n this.baseUrl = `https://api.telegram.org/bot${opts.token}`;\n this.pollIntervalMs = opts.pollIntervalSec * 1000;\n this.allowedUsers = opts.allowedUsers;\n this.allowedChats = opts.allowedChats;\n this.bufferMax = opts.bufferSize;\n this.log = opts.log;\n this.onMessage = opts.onMessage;\n this.offsetStoragePath = opts.offsetStoragePath;\n\n // Restore persisted offset so a crash/restart doesn't cause message replay.\n if (this.offsetStoragePath) {\n void this.loadOffset();\n }\n }\n\n // ------------------------------------------------------------------\n // Lifecycle\n // ------------------------------------------------------------------\n\n /** Start polling for updates. Idempotent. */\n start(): void {\n if (this.pollActive) return;\n this.pollActive = true;\n this._startedAt = Date.now();\n this.log.info('Telegram bot polling started');\n this.schedulePoll();\n }\n\n /** Stop polling and cancel all in-flight requests. */\n stop(): void {\n this.pollActive = false;\n this.controller.abort();\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.log.info('Telegram bot stopped');\n }\n\n get startedAt(): number | null {\n return this._startedAt;\n }\n\n get running(): boolean {\n return this.pollActive;\n }\n\n // ------------------------------------------------------------------\n // Buffer — incoming messages the agent can read\n // ------------------------------------------------------------------\n\n /** Return buffered messages, newest first. Optionally filter by chat. */\n getMessages(opts?: { chatId?: string | number; limit?: number }): TelegramIncomingMessage[] {\n let msgs = [...this.buffer].reverse();\n if (opts?.chatId) {\n const cid = String(opts.chatId);\n msgs = msgs.filter((m) => String(m.chatId) === cid);\n }\n const limit = opts?.limit ?? 20;\n return msgs.slice(0, limit);\n }\n\n /** Drop messages older than the given message ID from the buffer. */\n acknowledge(lastMessageId: number): number {\n const before = this.buffer.length;\n let i = this.buffer.length;\n while (i-- > 0) {\n if (this.buffer[i]!.messageId <= lastMessageId) {\n this.buffer.splice(0, i + 1);\n break;\n }\n }\n return before - this.buffer.length;\n }\n\n get bufferCount(): number {\n return this.buffer.length;\n }\n\n // ------------------------------------------------------------------\n // Outgoing — send a message\n // ------------------------------------------------------------------\n\n async sendMessage(chatId: string | number, text: string): Promise<TgResponse<TgMessage>> {\n const url = `${this.baseUrl}/sendMessage`;\n const body = JSON.stringify({\n chat_id: String(chatId),\n text,\n disable_web_page_preview: true,\n });\n\n this.log.debug(`Sending Telegram message to ${chatId} (${text.length} chars)`);\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n signal: AbortSignal.timeout(10_000),\n });\n const data = (await res.json()) as TgResponse<TgMessage>;\n if (!data.ok) {\n throw new Error(`Telegram API error ${data.error_code}: ${data.description}`);\n }\n return data;\n } catch (err) {\n lastErr = err;\n if (attempt < 3) {\n this.log.warn(`Telegram sendMessage attempt ${attempt} failed, retrying in 1s...`);\n await sleep(1000);\n }\n }\n }\n throw lastErr;\n }\n\n // ------------------------------------------------------------------\n // Health\n // ------------------------------------------------------------------\n\n async health(): Promise<{ ok: boolean; username?: string; error?: string }> {\n try {\n const url = `${this.baseUrl}/getMe`;\n const res = await fetch(url, { signal: AbortSignal.timeout(5000) });\n const data = (await res.json()) as TgResponse<TgUser>;\n if (!data.ok || !data.result) {\n return { ok: false, error: data.description ?? 'Unknown error' };\n }\n return { ok: true, username: data.result.username };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n }\n\n // ------------------------------------------------------------------\n // Polling\n // ------------------------------------------------------------------\n\n private schedulePoll(): void {\n if (!this.pollActive) return;\n this.pollTimer = setTimeout(() => {\n void this.poll().finally(() => this.schedulePoll());\n }, this.pollIntervalMs);\n }\n\n private async poll(): Promise<void> {\n try {\n const url = `${this.baseUrl}/getUpdates?offset=${this.offset}&timeout=10`;\n const res = await fetch(url, { signal: this.controller.signal });\n const data = (await res.json()) as TgResponse<TgUpdate[]>;\n\n if (!data.ok) {\n this.log.warn(`Telegram getUpdates failed: ${data.description}`);\n return;\n }\n\n const updates = data.result ?? [];\n for (const upd of updates) {\n this.offset = upd.update_id + 1;\n const raw = upd.message ?? upd.edited_message;\n if (!raw?.text) continue;\n const msg = { ...raw, text: raw.text };\n this.processMessage(msg);\n }\n\n // Persist offset after each successful poll to prevent message replay\n // after crashes or restarts.\n if (this.offsetStoragePath && this.offset > 0) {\n void this.saveOffset();\n }\n } catch (err) {\n if ((err as Error).name === 'AbortError') return;\n this.log.warn(`Telegram poll error: ${(err as Error).message}`);\n }\n }\n\n private processMessage(msg: TgMessage & { text: string }): void {\n const chatId = String(msg.chat.id);\n const userId = msg.from ? String(msg.from.id) : undefined;\n\n // Allowlist checks\n if (this.allowedUsers.size > 0 && userId && !this.allowedUsers.has(userId)) {\n this.log.debug(`Ignoring message from user ${userId} (not in allowedUsers)`);\n void this.sendMessage(chatId, '⛔ You are not authorized to interact with this bot.');\n return;\n }\n if (this.allowedChats.size > 0 && !this.allowedChats.has(chatId)) {\n this.log.debug(`Ignoring message from chat ${chatId} (not in allowedChats)`);\n return;\n }\n\n const incoming: TelegramIncomingMessage = {\n messageId: msg.message_id,\n chatId: msg.chat.id,\n chatType: msg.chat.type,\n userId: msg.from?.id,\n userName: msg.from?.username ?? msg.from?.first_name,\n text: msg.text,\n timestamp: msg.date * 1000,\n };\n\n // Push to circular buffer\n this.buffer.push(incoming);\n while (this.buffer.length > this.bufferMax) this.buffer.shift();\n\n this.onMessage(incoming);\n }\n\n private async loadOffset(): Promise<void> {\n if (!this.offsetStoragePath) return;\n try {\n const { readFileSync } = await import('node:fs');\n const raw = readFileSync(this.offsetStoragePath, 'utf8').trim();\n const n = Number.parseInt(raw, 10);\n if (Number.isFinite(n) && n >= 0) {\n this.offset = n;\n this.log.debug(`Telegram polling offset restored: ${this.offset}`);\n }\n } catch {\n // File doesn't exist yet — start from 0, which is correct.\n }\n }\n\n private async saveOffset(): Promise<void> {\n if (!this.offsetStoragePath) return;\n try {\n const { writeFileSync } = await import('node:fs');\n // Write atomically so a crash mid-write can't leave a corrupt file.\n writeFileSync(this.offsetStoragePath, String(this.offset), 'utf8');\n } catch (err) {\n this.log.warn(`Failed to persist Telegram offset: ${err}`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n/**\n * Truncate text to fit Telegram's 4096-char message limit.\n * Splits on a newline when possible; otherwise hard-cuts with \"…\" suffix.\n */\nexport function truncateForTelegram(text: string, maxLen = 4000): string {\n if (text.length <= maxLen) return text;\n const cut = text.lastIndexOf('\\n', maxLen - 20);\n const idx = cut > maxLen / 2 ? cut : maxLen - 20;\n return `${text.slice(0, idx)}\\n\\n…[truncated ${text.length - idx} chars]`;\n}\n\n/**\n * Escape HTML special chars for Telegram's HTML parse mode.\n */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n","import type { PluginAPI } from '@wrongstack/core';\r\n\r\nexport const PLUGIN_NAME = 'telegram';\r\n\r\nexport interface TelegramPluginConfig {\r\n /** Telegram Bot API token (from @BotFather). */\r\n botToken: string;\r\n /**\r\n * Default chat ID for outgoing notifications.\r\n * The agent's `telegram_send` tool can override per-call.\r\n */\r\n notifyChatId?: string | number;\r\n /**\r\n * List of user/chat IDs allowed to interact with the bot.\r\n * Empty = allow all. Recommended to set in production.\r\n */\r\n allowedUsers?: Array<string | number>;\r\n /**\r\n * List of group/chat IDs the bot is allowed to read from.\r\n * Empty = allow all. Narrow this to prevent noise.\r\n */\r\n allowedChats?: Array<string | number>;\r\n /** Polling interval in seconds (default: 2). */\r\n pollIntervalSec?: number;\r\n /** Notify on Telegram when a session ends. */\r\n notifyOnSessionEnd?: boolean;\r\n /** Notify when a tool runs longer than this threshold (ms). Set 0 to disable. */\r\n longToolThresholdMs?: number;\r\n /** Notify (humanized) when a `delegate` subagent finishes. Default: true. */\r\n notifyOnDelegate?: boolean;\r\n /** Maximum message length for Telegram (Telegram caps at 4096). */\r\n maxMessageLength?: number;\r\n /**\r\n * Path to a file that stores the Telegram polling offset. When set,\r\n * the offset is persisted on every successful poll and restored on startup,\r\n * preventing message replay after crashes or restarts.\r\n * The directory must already exist and be writable.\r\n */\r\n offsetStoragePath?: string;\r\n}\r\n\r\nexport const DEFAULT_CONFIG: Required<Omit<TelegramPluginConfig, 'botToken' | 'notifyChatId' | 'offsetStoragePath'>> = {\r\n allowedUsers: [],\r\n allowedChats: [],\r\n pollIntervalSec: 2,\r\n notifyOnSessionEnd: false,\r\n longToolThresholdMs: 30_000,\r\n notifyOnDelegate: true,\r\n maxMessageLength: 4000,\r\n};\r\n\r\nexport const telegramConfigSchema = {\r\n type: 'object',\r\n properties: {\r\n botToken: { type: 'string', description: 'Telegram Bot API token from @BotFather' },\r\n notifyChatId: {\r\n oneOf: [{ type: 'string' }, { type: 'integer' }],\r\n description: 'Default chat ID for outgoing notifications',\r\n },\r\n allowedUsers: {\r\n type: 'array',\r\n items: { oneOf: [{ type: 'string' }, { type: 'integer' }] },\r\n description: 'User IDs allowed to interact with the bot',\r\n },\r\n allowedChats: {\r\n type: 'array',\r\n items: { oneOf: [{ type: 'string' }, { type: 'integer' }] },\r\n description: 'Chat IDs the bot is allowed to read from',\r\n },\r\n pollIntervalSec: {\r\n type: 'integer',\r\n minimum: 1,\r\n maximum: 60,\r\n description: 'Polling interval in seconds',\r\n },\r\n notifyOnSessionEnd: { type: 'boolean' },\r\n longToolThresholdMs: { type: 'integer', minimum: 0 },\r\n notifyOnDelegate: { type: 'boolean' },\r\n maxMessageLength: { type: 'integer', minimum: 100, maximum: 4096 },\r\n },\r\n required: ['botToken'],\r\n};\r\n\r\nexport function readTelegramConfig(\r\n api: Pick<PluginAPI, 'config'>,\r\n): Required<Omit<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'>> &\r\n Pick<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'> {\r\n const config = api.config as unknown as Record<string, unknown>;\r\n const extensions = config.extensions as Record<string, unknown> | undefined;\r\n const pluginEntries = config.plugins;\r\n const legacyPlugins = pluginEntries as Record<string, unknown> | undefined;\r\n const legacyOpts =\r\n legacyPlugins && !Array.isArray(legacyPlugins) ? legacyPlugins[PLUGIN_NAME] : undefined;\r\n const entryOpts = pluginOptionsFromEntries(pluginEntries);\r\n const opts = {\r\n ...((legacyOpts ?? entryOpts) as TelegramPluginConfig),\r\n ...((extensions?.[PLUGIN_NAME] ?? {}) as TelegramPluginConfig),\r\n };\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...opts,\r\n };\r\n}\r\n\r\nfunction pluginOptionsFromEntries(entries: unknown): TelegramPluginConfig | undefined {\r\n if (!Array.isArray(entries)) return undefined;\r\n const found = entries.find(\r\n (entry) =>\r\n typeof entry === 'object' &&\r\n entry !== null &&\r\n 'name' in entry &&\r\n ((entry as { name?: unknown }).name === '@wrongstack/telegram' ||\r\n (entry as { name?: unknown }).name === PLUGIN_NAME),\r\n ) as { name?: unknown; options?: unknown } | undefined;\r\n return found?.options && typeof found.options === 'object'\r\n ? (found.options as TelegramPluginConfig)\r\n : undefined;\r\n}\r\n","// ---------------------------------------------------------------------------\n// Humanizers for agent events forwarded to Telegram.\n//\n// The host emits rich structured events; this module turns them into short,\n// readable chat messages. Kept pure (no bot / IO) so it's trivially testable.\n// ---------------------------------------------------------------------------\n\n/** Subset of the core `delegate.completed` event payload we render. */\nexport interface DelegateCompletedLike {\n target: string;\n task: string;\n ok: boolean;\n status?: string;\n summary: string;\n durationMs: number;\n iterations: number;\n toolCalls: number;\n costUsd?: number;\n subagentId?: string;\n}\n\n/** Compact human duration: `42s`, `3m`, `1.5h`. */\nexport function fmtDuration(ms: number): string {\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`;\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`;\n return `${(ms / 3_600_000).toFixed(1)}h`;\n}\n\n/**\n * Render a finished delegation as a readable Telegram message instead of the\n * raw, truncated JSON the generic `tool.executed` notifier would produce.\n *\n * Example:\n * ✅ Delegate → bug-hunter · success\n * Found 3 null-deref risks in auth.ts and patched the worst one…\n * ⏱ 3m · 4 iter · 37 tools · 💲0.0820\n */\nexport function formatDelegateCompleted(e: DelegateCompletedLike): string {\n const icon = e.ok ? '✅' : '❌';\n const status = e.status ?? (e.ok ? 'success' : 'failed');\n const task = e.task.length > 160 ? `${e.task.slice(0, 159)}…` : e.task;\n\n // Prefer the host's one-line summary; fall back to echoing the task when a\n // failure produced no summary.\n const body = e.summary?.trim() || `(no summary) — ${task}`;\n\n const stats = [\n `⏱ ${fmtDuration(e.durationMs)}`,\n `${e.iterations} iter`,\n `${e.toolCalls} tools`,\n ];\n if (typeof e.costUsd === 'number' && e.costUsd > 0) {\n stats.push(`💲${e.costUsd.toFixed(4)}`);\n }\n\n return [`${icon} Delegate → ${e.target} · ${status}`, body, stats.join(' · ')].join('\\n');\n}\n","import type { PluginAPI, SlashCommand } from '@wrongstack/core';\r\nimport type { TelegramBot } from '../bot.js';\r\nimport type { TelegramPluginConfig } from '../config.js';\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:status\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgStatusCommand(bot: TelegramBot, cfg: TelegramPluginConfig): SlashCommand {\r\n return {\r\n name: 'status',\r\n aliases: ['tgstat', 'tgs'],\r\n description: 'Show Telegram bot connection status and config',\r\n help: `Usage: /telegram:status\r\n\r\nShows whether the bot is connected, its username, polling interval,\r\nallowlist status, and notification settings.`,\r\n async run(_args, _ctx) {\r\n const health = await bot.health();\r\n const lines = [\r\n '═══ Telegram Plugin Status ═══',\r\n '',\r\n `Bot: ${health.ok ? `✅ @${health.username ?? 'connected'}` : `❌ ${health.error ?? 'offline'}`}`,\r\n `Running: ${bot.running ? 'yes' : 'no'}`,\r\n `Started: ${bot.startedAt ? new Date(bot.startedAt).toLocaleTimeString() : 'N/A'}`,\r\n `Poll: every ${cfg.pollIntervalSec ?? 2}s`,\r\n `Allowed: ${(cfg.allowedUsers?.length ?? 0) > 0 ? `${cfg.allowedUsers!.length} users` : 'everyone (users)'} / ${(cfg.allowedChats?.length ?? 0) > 0 ? `${cfg.allowedChats!.length} chats` : 'everyone (chats)'}`,\r\n `Notify: sessionEnd=${cfg.notifyOnSessionEnd ?? false}, longTool=${cfg.longToolThresholdMs ? `${cfg.longToolThresholdMs}ms` : 'off'}`,\r\n ];\r\n\r\n return { message: lines.join('\\n') };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:send\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgSendCommand(\r\n bot: TelegramBot,\r\n defaultChatId: string | number | undefined,\r\n): SlashCommand {\r\n return {\r\n name: 'send',\r\n description: 'Send a message to a Telegram chat',\r\n help: `Usage: /telegram:send [chat_id] <message>\r\n\r\nSend a message to a Telegram chat.\r\n- First argument (optional): chat or user ID. Uses notifyChatId from config when omitted.\r\n- Everything else: the message text.\r\n\r\nExamples:\r\n /telegram:send 123456789 Build completed successfully ✓\r\n /telegram:send Deploy finished — check staging`,\r\n async run(args, _ctx) {\r\n if (!args.trim()) {\r\n return { message: 'Usage: /telegram:send [chat_id] <message>' };\r\n }\r\n\r\n let chatId: string | number;\r\n let text: string;\r\n\r\n // First token might be a numeric chat_id\r\n const parts = args.trim().split(/\\s+/);\r\n const maybeId = parts[0];\r\n if (/^\\d+$/.test(maybeId!) && parts.length > 1) {\r\n chatId = maybeId!;\r\n text = parts.slice(1).join(' ');\r\n } else if (defaultChatId) {\r\n chatId = defaultChatId;\r\n text = args.trim();\r\n } else {\r\n return {\r\n message:\r\n 'No chat_id provided and no default notifyChatId configured.\\nUsage: /telegram:send <chat_id> <message>',\r\n };\r\n }\r\n\r\n try {\r\n const res = await bot.sendMessage(chatId, text);\r\n return {\r\n message: `✅ Message sent to ${chatId} (msg_id=${res.result?.message_id ?? '?'})`,\r\n };\r\n } catch (err) {\r\n return { message: `❌ Failed to send: ${(err as Error).message}` };\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:chatid\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgChatIdCommand(defaultChatId?: string | number): SlashCommand {\r\n const chatIdStr = defaultChatId ? String(defaultChatId) : null;\r\n return {\r\n name: 'chatid',\r\n description: 'Show the configured default chat ID',\r\n help: `Usage: /telegram:chatid\r\n\r\nShows the current default notifyChatId used for notifications\r\nand the \\`telegram_send\\` tool when no chat_id is specified.`,\r\n async run(_args, _ctx) {\r\n if (chatIdStr) {\r\n return { message: `Configured notifyChatId: ${chatIdStr}` };\r\n }\r\n return { message: 'No notifyChatId configured. Set it in the plugin config or pass chat_id explicitly to telegram_send.' };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Register all\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function registerSlashCommands(\r\n api: PluginAPI,\r\n bot: TelegramBot,\r\n cfg: TelegramPluginConfig,\r\n): string[] {\r\n const cmds = [\r\n tgStatusCommand(bot, cfg),\r\n tgSendCommand(bot, cfg.notifyChatId),\r\n tgChatIdCommand(cfg.notifyChatId),\r\n ];\r\n for (const cmd of cmds) api.slashCommands.register(cmd);\r\n return cmds.map((c) => c.name);\r\n}\r\n","import type { Tool } from '@wrongstack/core';\nimport type { TelegramBot } from '../bot.js';\n\ninterface TelegramReadInput {\n /** Filter to messages from a specific chat/user ID. Omit to see all chats. */\n chat_id?: string | number;\n /** Max messages to return (default: 10, max: 50). */\n limit?: number;\n /**\n * If a message_id is provided, acknowledge all messages up to and\n * including this ID (mark them as processed / remove from buffer).\n */\n ack_last?: number;\n}\n\nexport function makeTelegramReadTool(opts: {\n bot: TelegramBot;\n}): Tool<TelegramReadInput> {\n return {\n name: 'telegram_read',\n description:\n 'Read incoming Telegram messages from the bot. Returns recent messages the bot received, newest first. Use this to check if anyone sent instructions, questions, or feedback via Telegram. After processing messages, pass the last message_id to ack_last to clear them from the inbox.',\n usageHint: 'telegram_read(chat_id: \"123456789\", limit: 5)',\n category: 'Telegram',\n inputSchema: {\n type: 'object',\n properties: {\n chat_id: {\n oneOf: [{ type: 'string' }, { type: 'integer' }],\n description: 'Read messages only from this chat/user.',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n description: 'Max messages to return (default: 10).',\n },\n ack_last: {\n type: 'integer',\n description:\n 'After processing messages, pass the highest message_id to clear them from the buffer.',\n },\n },\n },\n permission: 'auto',\n mutating: false,\n timeoutMs: 5_000,\n async execute(input) {\n const msgs = opts.bot.getMessages({\n chatId: input.chat_id,\n limit: input.limit ?? 10,\n });\n\n let acked = 0;\n if (input.ack_last !== undefined && input.ack_last > 0) {\n acked = opts.bot.acknowledge(input.ack_last);\n }\n\n return {\n buffer_total: opts.bot.bufferCount,\n messages: msgs.map((m) => ({\n message_id: m.messageId,\n chat_id: m.chatId,\n chat_type: m.chatType,\n from: m.userName ?? `user_${m.userId ?? 'unknown'}`,\n text: m.text,\n ts: new Date(m.timestamp).toISOString(),\n })),\n acked,\n hint: acked > 0\n ? undefined\n : 'Use ack_last with the highest message_id to clear processed messages.',\n };\n },\n };\n}\n","import type { Tool } from '@wrongstack/core';\nimport type { Logger } from '@wrongstack/core';\nimport type { TelegramBot } from '../bot.js';\nimport { truncateForTelegram } from '../bot.js';\n\ninterface TelegramSendInput {\n /** Chat or user ID to send the message to. Falls back to config.notifyChatId when omitted. */\n chat_id?: string | number;\n /** Message text. */\n message: string;\n}\n\nexport function makeTelegramSendTool(opts: {\n bot: TelegramBot;\n defaultChatId?: string | number;\n maxMessageLength: number;\n log: Logger;\n}): Tool<TelegramSendInput> {\n return {\n name: 'telegram_send',\n description:\n 'Send a message via Telegram to a specified chat. Use this to notify users, report results, or communicate through Telegram.',\n usageHint: 'telegram_send(chat_id: \"123456789\", message: \"Task completed ✓\")',\n category: 'Telegram',\n inputSchema: {\n type: 'object',\n properties: {\n chat_id: {\n oneOf: [{ type: 'string' }, { type: 'integer' }],\n description: 'Target chat or user ID. Uses the plugin default when omitted.',\n },\n message: {\n type: 'string',\n description:\n 'Message text.',\n },\n },\n required: ['message'],\n },\n permission: 'confirm',\n mutating: true,\n timeoutMs: 15_000,\n async execute(input, _ctx, _opts) {\n const chatId = input.chat_id ?? opts.defaultChatId;\n if (!chatId) {\n throw new Error(\n 'No chat_id provided and no default notifyChatId configured. Set notifyChatId in plugin config or pass chat_id.',\n );\n }\n\n // Truncate message to fit Telegram's 4096 char limit\n const truncated = truncateForTelegram(input.message, opts.maxMessageLength);\n\n opts.log.info(`telegram_send → chat_id=${chatId} (${truncated.length} chars)`);\n\n const res = await opts.bot.sendMessage(chatId, truncated);\n\n return {\n ok: res.ok,\n message_id: res.result?.message_id,\n chat: res.result?.chat\n ? {\n id: res.result.chat.id,\n type: res.result.chat.type,\n title: res.result.chat.title,\n }\n : undefined,\n };\n },\n };\n}\n","import type { Plugin } from '@wrongstack/core';\nimport { TelegramBot } from './bot.js';\nimport type { TelegramIncomingMessage } from './bot.js';\nimport { truncateForTelegram } from './bot.js';\nimport { PLUGIN_NAME, readTelegramConfig, telegramConfigSchema } from './config.js';\nimport { formatDelegateCompleted } from './format.js';\nimport { registerSlashCommands } from './slash-commands/index.js';\nimport { makeTelegramReadTool } from './tools/telegram-read.js';\nimport { makeTelegramSendTool } from './tools/telegram-send.js';\n\n// ---------------------------------------------------------------------------\n// Teardown state\n// ---------------------------------------------------------------------------\n\nlet teardownState: {\n offs: Array<() => void>;\n toolNames: string[];\n commandNames: string[];\n bot: TelegramBot;\n} | null = null;\n\n// ---------------------------------------------------------------------------\n// Plugin\n// ---------------------------------------------------------------------------\n\nconst plugin: Plugin = {\n name: PLUGIN_NAME,\n version: '0.3.4',\n description: 'Telegram bridge — send/receive messages, get agent notifications.',\n apiVersion: '^0.1.10',\n capabilities: {\n tools: true,\n slashCommands: true,\n pipelines: [],\n },\n configSchema: telegramConfigSchema,\n defaultConfig: {\n pollIntervalSec: 2,\n notifyOnSessionEnd: false,\n longToolThresholdMs: 30_000,\n maxMessageLength: 4000,\n },\n\n async setup(api) {\n const cfg = readTelegramConfig(api);\n const log = api.log;\n\n log.info('Starting Telegram plugin...');\n\n // ---- Bot ----\n const bot = new TelegramBot({\n token: cfg.botToken,\n pollIntervalSec: cfg.pollIntervalSec,\n allowedUsers: new Set((cfg.allowedUsers ?? []).map(String)),\n allowedChats: new Set((cfg.allowedChats ?? []).map(String)),\n bufferSize: 50,\n log,\n offsetStoragePath: cfg.offsetStoragePath,\n onMessage(msg: TelegramIncomingMessage) {\n // Emit custom event so other plugins or the host can react.\n // The TUI can subscribe and surface it (future hook).\n api.emitCustom('telegram:message_received', msg);\n\n // Log it for the user in the TUI\n const who = msg.userName ?? msg.userId ?? 'unknown';\n log.info(`📨 Telegram: ${who} (chat=${msg.chatId}): ${msg.text.slice(0, 200)}`);\n },\n });\n\n // ---- Register tools ----\n const sendTool = makeTelegramSendTool({\n bot,\n defaultChatId: cfg.notifyChatId,\n maxMessageLength: cfg.maxMessageLength,\n log,\n });\n const readTool = makeTelegramReadTool({ bot });\n api.tools.register(sendTool);\n api.tools.register(readTool);\n\n // ---- Event subscriptions ----\n const offs: Array<() => void> = [];\n\n // System prompt contributor — inject unread Telegram messages\n const unregisterPrompt = api.registerSystemPromptContributor(async () => {\n const msgs = bot.getMessages({ limit: 5 });\n if (msgs.length === 0) return [];\n\n const blocks: Array<{ type: 'text'; text: string }> = [\n {\n type: 'text',\n text: [\n '## Telegram Inbox',\n `You have ${bot.bufferCount} unread Telegram message(s).`,\n 'Read them with `telegram_read` and reply with `telegram_send`.',\n '',\n 'Recent messages:',\n ...msgs.map((m) => {\n const who = m.userName ?? `user_${m.userId ?? 'unknown'}`;\n const ts = new Date(m.timestamp).toLocaleTimeString();\n return `- [${ts}] **${who}** (chat=${m.chatId}): ${m.text.slice(0, 200)}`;\n }),\n '',\n ].join('\\n'),\n },\n ];\n return blocks;\n });\n offs.push(unregisterPrompt);\n\n // Register slash commands\n const commandNames = registerSlashCommands(api, bot, cfg);\n\n // Notify on session end\n if (cfg.notifyOnSessionEnd && cfg.notifyChatId) {\n offs.push(\n api.events.on('session.ended', (event) => {\n const inputTokens = event.usage.input ?? 0;\n const outputTokens = event.usage.output ?? 0;\n const totalTokens = inputTokens + outputTokens;\n const msg = [\n `✅ Session ended`,\n '',\n `Session: ${event.id.slice(0, 8)}`,\n `Input: ${inputTokens} tokens`,\n `Output: ${outputTokens} tokens`,\n `Total: ${totalTokens} tokens`,\n ].join('\\n');\n\n void bot.sendMessage(cfg.notifyChatId!, msg).catch((err) => {\n log.warn(`Failed to send session end notification: ${(err as Error).message}`);\n });\n }),\n );\n }\n\n // Notify for long-running tools\n if (cfg.longToolThresholdMs && cfg.longToolThresholdMs > 0 && cfg.notifyChatId) {\n offs.push(\n api.events.on('tool.executed', (event) => {\n if (event.durationMs < cfg.longToolThresholdMs!) return;\n const sec = (event.durationMs / 1000).toFixed(1);\n const status = event.ok ? '✅' : '❌';\n const preview = event.output\n ? truncateForTelegram(event.output, 500)\n : '(no output)';\n\n const msg = [\n `${status} ${event.name} completed in ${sec}s`,\n '',\n preview,\n ].join('\\n');\n\n void bot.sendMessage(cfg.notifyChatId!, msg).catch((err) => {\n log.warn(`Failed to send tool notification: ${(err as Error).message}`);\n });\n }),\n );\n }\n\n // Notify (humanized) when a delegated subagent finishes. The generic\n // `tool.executed` notifier would dump the delegate's truncated JSON\n // result; `delegate.completed` carries readable fields instead.\n if (cfg.notifyOnDelegate && cfg.notifyChatId) {\n offs.push(\n api.events.on('delegate.completed', (event) => {\n const msg = truncateForTelegram(\n formatDelegateCompleted(event),\n cfg.maxMessageLength,\n );\n void bot.sendMessage(cfg.notifyChatId!, msg).catch((err) => {\n log.warn(`Failed to send delegate notification: ${(err as Error).message}`);\n });\n }),\n );\n }\n\n // ---- Start polling ----\n bot.start();\n\n teardownState = { offs, toolNames: [sendTool.name, readTool.name], commandNames, bot };\n\n log.info('Telegram plugin ready');\n },\n\n async teardown(api) {\n const state = teardownState;\n if (!state) return;\n teardownState = null;\n\n state.bot.stop();\n for (const off of state.offs) off();\n for (const name of state.toolNames) api.tools.unregister(name);\n for (const name of state.commandNames) {\n api.slashCommands.unregister(`${PLUGIN_NAME}:${name}`);\n }\n\n api.log.info('Telegram plugin torn down');\n },\n\n async health() {\n const state = teardownState;\n if (!state?.bot) return { ok: false, message: 'Plugin not initialized' };\n const h = await state.bot.health();\n return h;\n },\n};\n\nexport default plugin;\n\n// Re-export the types consumers may want\nexport type { TelegramIncomingMessage } from './bot.js';\nexport type { TelegramPluginConfig } from './config.js';\n"]}
1
+ {"version":3,"sources":["../src/bot.ts","../src/config.ts","../src/format.ts","../src/slash-commands/index.ts","../src/tools/telegram-read.ts","../src/tools/telegram-send.ts","../src/index.ts"],"names":["expectDefined"],"mappings":"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","file":"index.js","sourcesContent":["import type { Logger } from '@wrongstack/core';\nimport { sleep } from '@wrongstack/core/utils';\n\n// ---------------------------------------------------------------------------\n// Redaction helpers\n// ---------------------------------------------------------------------------\n/** Redact the bot token from a URL for safe logging. */\nfunction redactToken(url: string, token: string): string {\n return url.replace(token, '[REDACTED]');\n}\n\n// ---------------------------------------------------------------------------\n// Telegram Bot API types (subset used by this plugin)\n// ---------------------------------------------------------------------------\n\ninterface TgUser {\n id: number;\n is_bot: boolean;\n first_name: string;\n username?: string | undefined;\n}\n\ninterface TgChat {\n id: number;\n type: 'private' | 'group' | 'supergroup' | 'channel';\n title?: string | undefined;\n username?: string | undefined;\n}\n\ninterface TgMessage {\n message_id: number;\n from?: TgUser | undefined;\n chat: TgChat;\n date: number;\n text?: string | undefined;\n}\n\ninterface TgUpdate {\n update_id: number;\n message?: TgMessage | undefined;\n edited_message?: TgMessage | undefined;\n}\n\ninterface TgResponse<T> {\n ok: boolean;\n result?: T | undefined;\n description?: string | undefined;\n error_code?: number | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Incoming message shape emitted as a custom event\n// ---------------------------------------------------------------------------\n\nexport interface TelegramIncomingMessage {\n messageId: number;\n chatId: number;\n chatType: string;\n userId?: number | undefined;\n userName?: string | undefined;\n text: string;\n timestamp: number;\n}\n\n// ---------------------------------------------------------------------------\n// Bot options\n// ---------------------------------------------------------------------------\n\nexport interface TelegramBotOptions {\n token: string;\n pollIntervalSec: number;\n allowedUsers: Set<string>;\n allowedChats: Set<string>;\n /** Max messages to buffer for the agent to read. Default: 50. */\n bufferSize: number;\n log: Logger;\n /** Called for each incoming message that passes allowlist checks. */\n onMessage(msg: TelegramIncomingMessage): void;\n /**\n * Optional path to a file that stores the polling offset. When provided,\n * the offset is persisted on every successful poll and restored on startup,\n * preventing message replay after crashes or restarts.\n */\n offsetStoragePath?: string | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Bot\n// ---------------------------------------------------------------------------\n\nexport class TelegramBot {\n private readonly baseUrl: string;\n /** Base URL with token redacted, safe to use in log calls. */\n private readonly safeBaseUrl: string;\n private readonly pollIntervalMs: number;\n private readonly allowedUsers: Set<string>;\n private readonly allowedChats: Set<string>;\n private readonly log: Logger;\n private readonly onMessage: (msg: TelegramIncomingMessage) => void;\n private readonly controller = new AbortController();\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private pollActive = false;\n private offset = 0;\n private _startedAt: number | null = null;\n /** If set, the offset is persisted here after each successful poll. */\n private readonly offsetStoragePath?: string | undefined;\n\n // Circular buffer for incoming messages\n private readonly bufferMax: number;\n private readonly buffer: TelegramIncomingMessage[] = [];\n\n constructor(opts: TelegramBotOptions) {\n this.baseUrl = `https://api.telegram.org/bot${opts.token}`;\n this.safeBaseUrl = redactToken(this.baseUrl, opts.token);\n this.pollIntervalMs = opts.pollIntervalSec * 1000;\n this.allowedUsers = opts.allowedUsers;\n this.allowedChats = opts.allowedChats;\n this.bufferMax = opts.bufferSize;\n this.log = opts.log;\n this.onMessage = opts.onMessage;\n this.offsetStoragePath = opts.offsetStoragePath;\n\n // Restore persisted offset so a crash/restart doesn't cause message replay.\n if (this.offsetStoragePath) {\n void this.loadOffset();\n }\n }\n\n // ------------------------------------------------------------------\n // Lifecycle\n // ------------------------------------------------------------------\n\n /** Start polling for updates. Idempotent. */\n start(): void {\n if (this.pollActive) return;\n this.pollActive = true;\n this._startedAt = Date.now();\n this.log.info(`Telegram bot polling started (${this.safeBaseUrl})`);\n this.schedulePoll();\n }\n\n /** Stop polling and cancel all in-flight requests. */\n stop(): void {\n this.pollActive = false;\n this.controller.abort();\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n this.log.info('Telegram bot stopped');\n }\n\n get startedAt(): number | null {\n return this._startedAt;\n }\n\n get running(): boolean {\n return this.pollActive;\n }\n\n // ------------------------------------------------------------------\n // Buffer — incoming messages the agent can read\n // ------------------------------------------------------------------\n\n /** Return buffered messages, newest first. Optionally filter by chat. */\n getMessages(opts?: { chatId?: string | number | undefined; limit?: number | undefined }): TelegramIncomingMessage[] {\n let msgs = [...this.buffer].reverse();\n if (opts?.chatId) {\n const cid = String(opts.chatId);\n msgs = msgs.filter((m) => String(m.chatId) === cid);\n }\n const limit = opts?.limit ?? 20;\n return msgs.slice(0, limit);\n }\n\n /** Drop messages older than the given message ID from the buffer. */\n acknowledge(lastMessageId: number): number {\n const before = this.buffer.length;\n let i = this.buffer.length;\n while (i-- > 0) {\n const buffered = this.buffer[i];\n if (buffered && buffered.messageId <= lastMessageId) {\n this.buffer.splice(0, i + 1);\n break;\n }\n }\n return before - this.buffer.length;\n }\n\n get bufferCount(): number {\n return this.buffer.length;\n }\n\n // ------------------------------------------------------------------\n // Outgoing — send a message\n // ------------------------------------------------------------------\n\n async sendMessage(chatId: string | number, text: string): Promise<TgResponse<TgMessage>> {\n const url = `${this.baseUrl}/sendMessage`;\n const body = JSON.stringify({\n chat_id: String(chatId),\n text,\n disable_web_page_preview: true,\n });\n\n this.log.debug(`Sending Telegram message to ${chatId} (${text.length} chars)`);\n\n let lastErr: unknown;\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n signal: AbortSignal.timeout(10_000),\n });\n const data = (await res.json()) as TgResponse<TgMessage>;\n if (!data.ok) {\n throw new Error(`Telegram API error ${data.error_code}: ${data.description}`);\n }\n return data;\n } catch (err) {\n lastErr = err;\n if (attempt < 3) {\n this.log.warn(`Telegram sendMessage attempt ${attempt} failed, retrying in 1s...`);\n await sleep(1000);\n }\n }\n }\n throw lastErr;\n }\n\n // ------------------------------------------------------------------\n // Health\n // ------------------------------------------------------------------\n\n async health(): Promise<{ ok: boolean; username?: string | undefined; error?: string | undefined }> {\n try {\n const url = `${this.baseUrl}/getMe`;\n const res = await fetch(url, { signal: AbortSignal.timeout(5000) });\n const data = (await res.json()) as TgResponse<TgUser>;\n if (!data.ok || !data.result) {\n return { ok: false, error: data.description ?? 'Unknown error' };\n }\n return { ok: true, username: data.result.username };\n } catch (err) {\n return { ok: false, error: (err as Error).message };\n }\n }\n\n // ------------------------------------------------------------------\n // Polling\n // ------------------------------------------------------------------\n\n private schedulePoll(): void {\n if (!this.pollActive) return;\n this.pollTimer = setTimeout(() => {\n void this.poll().finally(() => this.schedulePoll());\n }, this.pollIntervalMs);\n }\n\n private async poll(): Promise<void> {\n try {\n const url = `${this.baseUrl}/getUpdates?offset=${this.offset}&timeout=10`;\n const res = await fetch(url, { signal: this.controller.signal });\n const data = (await res.json()) as TgResponse<TgUpdate[]>;\n\n if (!data.ok) {\n this.log.warn(`Telegram getUpdates failed: ${data.description}`);\n return;\n }\n\n const updates = data.result ?? [];\n for (const upd of updates) {\n this.offset = upd.update_id + 1;\n const raw = upd.message ?? upd.edited_message;\n if (!raw?.text) continue;\n const msg = { ...raw, text: raw.text };\n this.processMessage(msg);\n }\n\n // Persist offset after each successful poll to prevent message replay\n // after crashes or restarts.\n if (this.offsetStoragePath && this.offset > 0) {\n void this.saveOffset();\n }\n } catch (err) {\n if ((err as Error).name === 'AbortError') return;\n this.log.warn(`Telegram poll error: ${(err as Error).message}`);\n }\n }\n\n private processMessage(msg: TgMessage & { text: string }): void {\n const chatId = String(msg.chat.id);\n const userId = msg.from ? String(msg.from.id) : undefined;\n\n // Allowlist checks\n if (this.allowedUsers.size > 0 && userId && !this.allowedUsers.has(userId)) {\n this.log.debug(`Ignoring message from user ${userId} (not in allowedUsers)`);\n void this.sendMessage(chatId, '⛔ You are not authorized to interact with this bot.');\n return;\n }\n if (this.allowedChats.size > 0 && !this.allowedChats.has(chatId)) {\n this.log.debug(`Ignoring message from chat ${chatId} (not in allowedChats)`);\n return;\n }\n\n const incoming: TelegramIncomingMessage = {\n messageId: msg.message_id,\n chatId: msg.chat.id,\n chatType: msg.chat.type,\n userId: msg.from?.id,\n userName: msg.from?.username ?? msg.from?.first_name,\n text: msg.text,\n timestamp: msg.date * 1000,\n };\n\n // Push to circular buffer\n this.buffer.push(incoming);\n while (this.buffer.length > this.bufferMax) this.buffer.shift();\n\n this.onMessage(incoming);\n }\n\n private async loadOffset(): Promise<void> {\n if (!this.offsetStoragePath) return;\n try {\n const { readFileSync } = await import('node:fs');\n const raw = readFileSync(this.offsetStoragePath, 'utf8').trim();\n const n = Number.parseInt(raw, 10);\n if (Number.isFinite(n) && n >= 0) {\n this.offset = n;\n this.log.debug(`Telegram polling offset restored: ${this.offset}`);\n }\n } catch {\n // File doesn't exist yet — start from 0, which is correct.\n }\n }\n\n private async saveOffset(): Promise<void> {\n if (!this.offsetStoragePath) return;\n try {\n const { writeFileSync } = await import('node:fs');\n // Write atomically so a crash mid-write can't leave a corrupt file.\n writeFileSync(this.offsetStoragePath, String(this.offset), 'utf8');\n } catch (err) {\n this.log.warn(`Failed to persist Telegram offset: ${err}`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Truncate text to fit Telegram's 4096-char message limit.\n * Splits on a newline when possible; otherwise hard-cuts with \"…\" suffix.\n */\nexport function truncateForTelegram(text: string, maxLen = 4000): string {\n if (text.length <= maxLen) return text;\n const cut = text.lastIndexOf('\\n', maxLen - 20);\n const idx = cut > maxLen / 2 ? cut : maxLen - 20;\n return `${text.slice(0, idx)}\\n\\n…[truncated ${text.length - idx} chars]`;\n}\n\n/**\n * Escape HTML special chars for Telegram's HTML parse mode.\n */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n","import type { PluginAPI } from '@wrongstack/core';\r\n\r\nexport const PLUGIN_NAME = 'telegram';\r\n\r\nexport interface TelegramPluginConfig {\r\n /** Telegram Bot API token (from @BotFather). */\r\n botToken: string;\r\n /**\r\n * Default chat ID for outgoing notifications.\r\n * The agent's `telegram_send` tool can override per-call.\r\n */\r\n notifyChatId?: string | number | undefined;\r\n /**\r\n * List of user/chat IDs allowed to interact with the bot.\r\n * Empty = allow all. Recommended to set in production.\r\n */\r\n allowedUsers?: Array<string | number> | undefined;\r\n /**\r\n * List of group/chat IDs the bot is allowed to read from.\r\n * Empty = allow all. Narrow this to prevent noise.\r\n */\r\n allowedChats?: Array<string | number> | undefined;\r\n /** Polling interval in seconds (default: 2). */\r\n pollIntervalSec?: number | undefined;\r\n /** Notify on Telegram when a session ends. */\r\n notifyOnSessionEnd?: boolean | undefined;\r\n /** Notify when a tool runs longer than this threshold (ms). Set 0 to disable. */\r\n longToolThresholdMs?: number | undefined;\r\n /** Notify (humanized) when a `delegate` subagent finishes. Default: true. */\r\n notifyOnDelegate?: boolean | undefined;\r\n /** Maximum message length for Telegram (Telegram caps at 4096). */\r\n maxMessageLength?: number | undefined;\r\n /**\r\n * Path to a file that stores the Telegram polling offset. When set,\r\n * the offset is persisted on every successful poll and restored on startup,\r\n * preventing message replay after crashes or restarts.\r\n * The directory must already exist and be writable.\r\n */\r\n offsetStoragePath?: string | undefined;\r\n}\r\n\r\nexport const DEFAULT_CONFIG: Required<Omit<TelegramPluginConfig, 'botToken' | 'notifyChatId' | 'offsetStoragePath'>> = {\r\n allowedUsers: [],\r\n allowedChats: [],\r\n pollIntervalSec: 2,\r\n notifyOnSessionEnd: false,\r\n longToolThresholdMs: 30_000,\r\n notifyOnDelegate: true,\r\n maxMessageLength: 4000,\r\n};\r\n\r\nexport const telegramConfigSchema = {\r\n type: 'object',\r\n properties: {\r\n botToken: { type: 'string', description: 'Telegram Bot API token from @BotFather' },\r\n notifyChatId: {\r\n oneOf: [{ type: 'string' }, { type: 'integer' }],\r\n description: 'Default chat ID for outgoing notifications',\r\n },\r\n allowedUsers: {\r\n type: 'array',\r\n items: { oneOf: [{ type: 'string' }, { type: 'integer' }] },\r\n description: 'User IDs allowed to interact with the bot',\r\n },\r\n allowedChats: {\r\n type: 'array',\r\n items: { oneOf: [{ type: 'string' }, { type: 'integer' }] },\r\n description: 'Chat IDs the bot is allowed to read from',\r\n },\r\n pollIntervalSec: {\r\n type: 'integer',\r\n minimum: 1,\r\n maximum: 60,\r\n description: 'Polling interval in seconds',\r\n },\r\n notifyOnSessionEnd: { type: 'boolean' },\r\n longToolThresholdMs: { type: 'integer', minimum: 0 },\r\n notifyOnDelegate: { type: 'boolean' },\r\n maxMessageLength: { type: 'integer', minimum: 100, maximum: 4096 },\r\n },\r\n required: ['botToken'],\r\n};\r\n\r\nexport function readTelegramConfig(\r\n api: Pick<PluginAPI, 'config'>,\r\n): Required<Omit<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'>> &\r\n Pick<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'> {\r\n const config = api.config as unknown as Record<string, unknown>;\r\n const extensions = config.extensions as Record<string, unknown> | undefined;\r\n const pluginEntries = config.plugins;\r\n const legacyPlugins = pluginEntries as Record<string, unknown> | undefined;\r\n const legacyOpts =\r\n legacyPlugins && !Array.isArray(legacyPlugins) ? legacyPlugins[PLUGIN_NAME] : undefined;\r\n const entryOpts = pluginOptionsFromEntries(pluginEntries);\r\n const opts = {\r\n ...((legacyOpts ?? entryOpts) as TelegramPluginConfig),\r\n ...((extensions?.[PLUGIN_NAME] ?? {}) as TelegramPluginConfig),\r\n };\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...opts,\r\n };\r\n}\r\n\r\nfunction pluginOptionsFromEntries(entries: unknown): TelegramPluginConfig | undefined {\r\n if (!Array.isArray(entries)) return undefined;\r\n const found = entries.find(\r\n (entry) =>\r\n typeof entry === 'object' &&\r\n entry !== null &&\r\n 'name' in entry &&\r\n ((entry as { name?: unknown | undefined }).name === '@wrongstack/telegram' ||\r\n (entry as { name?: unknown | undefined }).name === PLUGIN_NAME),\r\n ) as { name?: unknown | undefined; options?: unknown | undefined } | undefined;\r\n return found?.options && typeof found.options === 'object'\r\n ? (found.options as TelegramPluginConfig)\r\n : undefined;\r\n}\r\n","// ---------------------------------------------------------------------------\n// Humanizers for agent events forwarded to Telegram.\n//\n// The host emits rich structured events; this module turns them into short,\n// readable chat messages. Kept pure (no bot / IO) so it's trivially testable.\n// ---------------------------------------------------------------------------\n\n/** Subset of the core `delegate.completed` event payload we render. */\nexport interface DelegateCompletedLike {\n target: string;\n task: string;\n ok: boolean;\n status?: string | undefined;\n summary: string;\n durationMs: number;\n iterations: number;\n toolCalls: number;\n costUsd?: number | undefined;\n subagentId?: string | undefined;\n}\n\n/** Compact human duration: `42s`, `3m`, `1.5h`. */\nexport function fmtDuration(ms: number): string {\n if (ms < 60_000) return `${Math.round(ms / 1000)}s`;\n if (ms < 3_600_000) return `${Math.round(ms / 60_000)}m`;\n return `${(ms / 3_600_000).toFixed(1)}h`;\n}\n\n/**\n * Render a finished delegation as a readable Telegram message instead of the\n * raw, truncated JSON the generic `tool.executed` notifier would produce.\n *\n * Example:\n * ✅ Delegate → bug-hunter · success\n * Found 3 null-deref risks in auth.ts and patched the worst one…\n * ⏱ 3m · 4 iter · 37 tools · 💲0.0820\n */\nexport function formatDelegateCompleted(e: DelegateCompletedLike): string {\n const icon = e.ok ? '✅' : '❌';\n const status = e.status ?? (e.ok ? 'success' : 'failed');\n const task = e.task.length > 160 ? `${e.task.slice(0, 159)}…` : e.task;\n\n // Prefer the host's one-line summary; fall back to echoing the task when a\n // failure produced no summary.\n const body = e.summary?.trim() || `(no summary) — ${task}`;\n\n const stats = [\n `⏱ ${fmtDuration(e.durationMs)}`,\n `${e.iterations} iter`,\n `${e.toolCalls} tools`,\n ];\n if (typeof e.costUsd === 'number' && e.costUsd > 0) {\n stats.push(`💲${e.costUsd.toFixed(4)}`);\n }\n\n return [`${icon} Delegate → ${e.target} · ${status}`, body, stats.join(' · ')].join('\\n');\n}\n","import type { PluginAPI, SlashCommand } from '@wrongstack/core';\r\nimport type { TelegramBot } from '../bot.js';\r\nimport type { TelegramPluginConfig } from '../config.js';\r\n\r\n\n\nfunction expectDefined<T>(value: T | null | undefined): T {\n if (value === null || value === undefined) {\n throw new Error('Expected value to be defined');\n }\n return value;\n}\n\n// ---------------------------------------------------------------------------\r\n// /telegram:status\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgStatusCommand(bot: TelegramBot, cfg: TelegramPluginConfig): SlashCommand {\r\n return {\r\n name: 'status',\r\n aliases: ['tgstat', 'tgs'],\r\n description: 'Show Telegram bot connection status and config',\r\n help: `Usage: /telegram:status\r\n\r\nShows whether the bot is connected, its username, polling interval,\r\nallowlist status, and notification settings.`,\r\n async run(_args, _ctx) {\r\n const health = await bot.health();\r\n const lines = [\r\n '═══ Telegram Plugin Status ═══',\r\n '',\r\n `Bot: ${health.ok ? `✅ @${health.username ?? 'connected'}` : `❌ ${health.error ?? 'offline'}`}`,\r\n `Running: ${bot.running ? 'yes' : 'no'}`,\r\n `Started: ${bot.startedAt ? new Date(bot.startedAt).toLocaleTimeString() : 'N/A'}`,\r\n `Poll: every ${cfg.pollIntervalSec ?? 2}s`,\r\n `Allowed: ${(cfg.allowedUsers?.length ?? 0) > 0 ? `${cfg.allowedUsers?.length} users` : 'everyone (users)'} / ${(cfg.allowedChats?.length ?? 0) > 0 ? `${cfg.allowedChats?.length} chats` : 'everyone (chats)'}`,\r\n `Notify: sessionEnd=${cfg.notifyOnSessionEnd ?? false}, longTool=${cfg.longToolThresholdMs ? `${cfg.longToolThresholdMs}ms` : 'off'}`,\r\n ];\r\n\r\n return { message: lines.join('\\n') };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:send\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgSendCommand(\r\n bot: TelegramBot,\r\n defaultChatId: string | number | undefined,\r\n): SlashCommand {\r\n return {\r\n name: 'send',\r\n description: 'Send a message to a Telegram chat',\r\n help: `Usage: /telegram:send [chat_id] <message>\r\n\r\nSend a message to a Telegram chat.\r\n- First argument (optional): chat or user ID. Uses notifyChatId from config when omitted.\r\n- Everything else: the message text.\r\n\r\nExamples:\r\n /telegram:send 123456789 Build completed successfully ✓\r\n /telegram:send Deploy finished — check staging`,\r\n async run(args, _ctx) {\r\n if (!args.trim()) {\r\n return { message: 'Usage: /telegram:send [chat_id] <message>' };\r\n }\r\n\r\n let chatId: string | number;\r\n let text: string;\r\n\r\n // First token might be a numeric chat_id\r\n const parts = args.trim().split(/\\s+/);\r\n const maybeId = parts[0];\r\n if (/^\\d+$/.test(expectDefined(maybeId)) && parts.length > 1) {\r\n chatId = expectDefined(maybeId);\r\n text = parts.slice(1).join(' ');\r\n } else if (defaultChatId) {\r\n chatId = defaultChatId;\r\n text = args.trim();\r\n } else {\r\n return {\r\n message:\r\n 'No chat_id provided and no default notifyChatId configured.\\nUsage: /telegram:send <chat_id> <message>',\r\n };\r\n }\r\n\r\n try {\r\n const res = await bot.sendMessage(chatId, text);\r\n return {\r\n message: `✅ Message sent to ${chatId} (msg_id=${res.result?.message_id ?? '?'})`,\r\n };\r\n } catch (err) {\r\n return { message: `❌ Failed to send: ${(err as Error).message}` };\r\n }\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// /telegram:chatid\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function tgChatIdCommand(defaultChatId?: string | number): SlashCommand {\r\n const chatIdStr = defaultChatId ? String(defaultChatId) : null;\r\n return {\r\n name: 'chatid',\r\n description: 'Show the configured default chat ID',\r\n help: `Usage: /telegram:chatid\r\n\r\nShows the current default notifyChatId used for notifications\r\nand the \\`telegram_send\\` tool when no chat_id is specified.`,\r\n async run(_args, _ctx) {\r\n if (chatIdStr) {\r\n return { message: `Configured notifyChatId: ${chatIdStr}` };\r\n }\r\n return { message: 'No notifyChatId configured. Set it in the plugin config or pass chat_id explicitly to telegram_send.' };\r\n },\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Register all\r\n// ---------------------------------------------------------------------------\r\n\r\nexport function registerSlashCommands(\r\n api: PluginAPI,\r\n bot: TelegramBot,\r\n cfg: TelegramPluginConfig,\r\n): string[] {\r\n const cmds = [\r\n tgStatusCommand(bot, cfg),\r\n tgSendCommand(bot, cfg.notifyChatId),\r\n tgChatIdCommand(cfg.notifyChatId),\r\n ];\r\n for (const cmd of cmds) api.slashCommands.register(cmd);\r\n return cmds.map((c) => c.name);\r\n}\r\n","import type { Tool } from '@wrongstack/core';\nimport type { TelegramBot } from '../bot.js';\n\ninterface TelegramReadInput {\n /** Filter to messages from a specific chat/user ID. Omit to see all chats. */\n chat_id?: string | number | undefined;\n /** Max messages to return (default: 10, max: 50). */\n limit?: number | undefined;\n /**\n * If a message_id is provided, acknowledge all messages up to and\n * including this ID (mark them as processed / remove from buffer).\n */\n ack_last?: number | undefined;\n}\n\nexport function makeTelegramReadTool(opts: {\n bot: TelegramBot;\n}): Tool<TelegramReadInput> {\n return {\n name: 'telegram_read',\n description:\n 'Read incoming Telegram messages from the bot. Returns recent messages the bot received, newest first. Use this to check if anyone sent instructions, questions, or feedback via Telegram. After processing messages, pass the last message_id to ack_last to clear them from the inbox.',\n usageHint: 'telegram_read(chat_id: \"123456789\", limit: 5)',\n category: 'Telegram',\n inputSchema: {\n type: 'object',\n properties: {\n chat_id: {\n oneOf: [{ type: 'string' }, { type: 'integer' }],\n description: 'Read messages only from this chat/user.',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n description: 'Max messages to return (default: 10).',\n },\n ack_last: {\n type: 'integer',\n description:\n 'After processing messages, pass the highest message_id to clear them from the buffer.',\n },\n },\n },\n permission: 'auto',\n mutating: false,\n timeoutMs: 5_000,\n async execute(input) {\n const msgs = opts.bot.getMessages({\n chatId: input.chat_id,\n limit: input.limit ?? 10,\n });\n\n let acked = 0;\n if (input.ack_last !== undefined && input.ack_last > 0) {\n acked = opts.bot.acknowledge(input.ack_last);\n }\n\n return {\n buffer_total: opts.bot.bufferCount,\n messages: msgs.map((m) => ({\n message_id: m.messageId,\n chat_id: m.chatId,\n chat_type: m.chatType,\n from: m.userName ?? `user_${m.userId ?? 'unknown'}`,\n text: m.text,\n ts: new Date(m.timestamp).toISOString(),\n })),\n acked,\n hint: acked > 0\n ? undefined\n : 'Use ack_last with the highest message_id to clear processed messages.',\n };\n },\n };\n}\n","import type { Tool } from '@wrongstack/core';\nimport type { Logger } from '@wrongstack/core';\nimport type { TelegramBot } from '../bot.js';\nimport { truncateForTelegram } from '../bot.js';\n\ninterface TelegramSendInput {\n /** Chat or user ID to send the message to. Falls back to config.notifyChatId when omitted. */\n chat_id?: string | number | undefined;\n /** Message text. */\n message: string;\n}\n\nexport function makeTelegramSendTool(opts: {\n bot: TelegramBot;\n defaultChatId?: string | number | undefined;\n maxMessageLength: number;\n log: Logger;\n}): Tool<TelegramSendInput> {\n return {\n name: 'telegram_send',\n description:\n 'Send a message via Telegram to a specified chat. Use this to notify users, report results, or communicate through Telegram.',\n usageHint: 'telegram_send(chat_id: \"123456789\", message: \"Task completed ✓\")',\n category: 'Telegram',\n inputSchema: {\n type: 'object',\n properties: {\n chat_id: {\n oneOf: [{ type: 'string' }, { type: 'integer' }],\n description: 'Target chat or user ID. Uses the plugin default when omitted.',\n },\n message: {\n type: 'string',\n description:\n 'Message text.',\n },\n },\n required: ['message'],\n },\n permission: 'confirm',\n mutating: true,\n timeoutMs: 15_000,\n async execute(input, _ctx, _opts) {\n const chatId = input.chat_id ?? opts.defaultChatId;\n if (!chatId) {\n throw new Error(\n 'No chat_id provided and no default notifyChatId configured. Set notifyChatId in plugin config or pass chat_id.',\n );\n }\n\n // Truncate message to fit Telegram's 4096 char limit\n const truncated = truncateForTelegram(input.message, opts.maxMessageLength);\n\n opts.log.info(`telegram_send → chat_id=${chatId} (${truncated.length} chars)`);\n\n const res = await opts.bot.sendMessage(chatId, truncated);\n\n return {\n ok: res.ok,\n message_id: res.result?.message_id,\n chat: res.result?.chat\n ? {\n id: res.result.chat.id,\n type: res.result.chat.type,\n title: res.result.chat.title,\n }\n : undefined,\n };\n },\n };\n}\n","import type { Plugin } from '@wrongstack/core';\nimport { TelegramBot } from './bot.js';\nimport type { TelegramIncomingMessage } from './bot.js';\nimport { truncateForTelegram } from './bot.js';\nimport { PLUGIN_NAME, readTelegramConfig, telegramConfigSchema } from './config.js';\nimport { formatDelegateCompleted } from './format.js';\nimport { registerSlashCommands } from './slash-commands/index.js';\nimport { makeTelegramReadTool } from './tools/telegram-read.js';\nimport { makeTelegramSendTool } from './tools/telegram-send.js';\n\n\n\nfunction expectDefined<T>(value: T | null | undefined): T {\n if (value === null || value === undefined) {\n throw new Error('Expected value to be defined');\n }\n return value;\n}\n\n// ---------------------------------------------------------------------------\n// Teardown state\n// ---------------------------------------------------------------------------\n\nlet teardownState: {\n offs: Array<() => void>;\n toolNames: string[];\n commandNames: string[];\n bot: TelegramBot;\n} | null = null;\n\n// ---------------------------------------------------------------------------\n// Plugin\n// ---------------------------------------------------------------------------\n\nconst plugin: Plugin = {\n name: PLUGIN_NAME,\n version: '0.3.4',\n description: 'Telegram bridge — send/receive messages, get agent notifications.',\n apiVersion: '^0.1.10',\n capabilities: {\n tools: true,\n slashCommands: true,\n pipelines: [],\n },\n configSchema: telegramConfigSchema,\n defaultConfig: {\n pollIntervalSec: 2,\n notifyOnSessionEnd: false,\n longToolThresholdMs: 30_000,\n maxMessageLength: 4000,\n },\n\n async setup(api) {\n const cfg = readTelegramConfig(api);\n const log = api.log;\n\n log.info('Starting Telegram plugin...');\n\n // ---- Bot ----\n const bot = new TelegramBot({\n token: cfg.botToken,\n pollIntervalSec: cfg.pollIntervalSec ?? 2,\n allowedUsers: new Set((cfg.allowedUsers ?? []).map(String)),\n allowedChats: new Set((cfg.allowedChats ?? []).map(String)),\n bufferSize: 50,\n log,\n offsetStoragePath: cfg.offsetStoragePath,\n onMessage(msg: TelegramIncomingMessage) {\n // Emit custom event so other plugins or the host can react.\n // The TUI can subscribe and surface it (future hook).\n api.emitCustom('telegram:message_received', msg);\n\n // Log it for the user in the TUI\n const who = msg.userName ?? msg.userId ?? 'unknown';\n log.info(`📨 Telegram: ${who} (chat=${msg.chatId}): ${msg.text.slice(0, 200)}`);\n },\n });\n\n // ---- Register tools ----\n const sendTool = makeTelegramSendTool({\n bot,\n defaultChatId: cfg.notifyChatId,\n maxMessageLength: cfg.maxMessageLength ?? 4000,\n log,\n });\n const readTool = makeTelegramReadTool({ bot });\n api.tools.register(sendTool);\n api.tools.register(readTool);\n\n // ---- Event subscriptions ----\n const offs: Array<() => void> = [];\n\n // System prompt contributor — inject unread Telegram messages\n const unregisterPrompt = api.registerSystemPromptContributor(async () => {\n const msgs = bot.getMessages({ limit: 5 });\n if (msgs.length === 0) return [];\n\n const blocks: Array<{ type: 'text'; text: string }> = [\n {\n type: 'text',\n text: [\n '## Telegram Inbox',\n `You have ${bot.bufferCount} unread Telegram message(s).`,\n 'Read them with `telegram_read` and reply with `telegram_send`.',\n '',\n 'Recent messages:',\n ...msgs.map((m) => {\n const who = m.userName ?? `user_${m.userId ?? 'unknown'}`;\n const ts = new Date(m.timestamp).toLocaleTimeString();\n return `- [${ts}] **${who}** (chat=${m.chatId}): ${m.text.slice(0, 200)}`;\n }),\n '',\n ].join('\\n'),\n },\n ];\n return blocks;\n });\n offs.push(unregisterPrompt);\n\n // Register slash commands\n const commandNames = registerSlashCommands(api, bot, cfg);\n\n // Notify on session end\n if (cfg.notifyOnSessionEnd && cfg.notifyChatId) {\n offs.push(\n api.events.on('session.ended', (event) => {\n const inputTokens = event.usage.input ?? 0;\n const outputTokens = event.usage.output ?? 0;\n const totalTokens = inputTokens + outputTokens;\n const msg = [\n `✅ Session ended`,\n '',\n `Session: ${event.id.slice(0, 8)}`,\n `Input: ${inputTokens} tokens`,\n `Output: ${outputTokens} tokens`,\n `Total: ${totalTokens} tokens`,\n ].join('\\n');\n\n void bot.sendMessage(expectDefined(cfg.notifyChatId), msg).catch((err) => {\n log.warn(`Failed to send session end notification: ${(err as Error).message}`);\n });\n }),\n );\n }\n\n // Notify for long-running tools\n if (cfg.longToolThresholdMs && cfg.longToolThresholdMs > 0 && cfg.notifyChatId) {\n offs.push(\n api.events.on('tool.executed', (event) => {\n if (event.durationMs < expectDefined(cfg.longToolThresholdMs)) return;\n const sec = (event.durationMs / 1000).toFixed(1);\n const status = event.ok ? '✅' : '❌';\n const preview = event.output\n ? truncateForTelegram(event.output, 500)\n : '(no output)';\n\n const msg = [\n `${status} ${event.name} completed in ${sec}s`,\n '',\n preview,\n ].join('\\n');\n\n void bot.sendMessage(expectDefined(cfg.notifyChatId), msg).catch((err) => {\n log.warn(`Failed to send tool notification: ${(err as Error).message}`);\n });\n }),\n );\n }\n\n // Notify (humanized) when a delegated subagent finishes. The generic\n // `tool.executed` notifier would dump the delegate's truncated JSON\n // result; `delegate.completed` carries readable fields instead.\n if (cfg.notifyOnDelegate && cfg.notifyChatId) {\n offs.push(\n api.events.on('delegate.completed', (event) => {\n const msg = truncateForTelegram(\n formatDelegateCompleted(event),\n cfg.maxMessageLength,\n );\n void bot.sendMessage(expectDefined(cfg.notifyChatId), msg).catch((err) => {\n log.warn(`Failed to send delegate notification: ${(err as Error).message}`);\n });\n }),\n );\n }\n\n // ---- Start polling ----\n bot.start();\n\n teardownState = { offs, toolNames: [sendTool.name, readTool.name], commandNames, bot };\n\n log.info('Telegram plugin ready');\n },\n\n async teardown(api) {\n const state = teardownState;\n if (!state) return;\n teardownState = null;\n\n state.bot.stop();\n for (const off of state.offs) off();\n for (const name of state.toolNames) api.tools.unregister(name);\n for (const name of state.commandNames) {\n api.slashCommands.unregister(`${PLUGIN_NAME}:${name}`);\n }\n\n api.log.info('Telegram plugin torn down');\n },\n\n async health() {\n const state = teardownState;\n if (!state?.bot) return { ok: false, message: 'Plugin not initialized' };\n const h = await state.bot.health();\n return h;\n },\n};\n\nexport default plugin;\n\n// Re-export the types consumers may want\nexport type { TelegramIncomingMessage } from './bot.js';\nexport type { TelegramPluginConfig } from './config.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/telegram",
3
- "version": "0.77.0",
3
+ "version": "0.84.1",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack plugin — Telegram bridge: send messages, receive prompts, get notified.",
6
6
  "repository": {
@@ -25,13 +25,13 @@
25
25
  "dist"
26
26
  ],
27
27
  "peerDependencies": {
28
- "@wrongstack/core": "0.77.0"
28
+ "@wrongstack/core": "0.84.1"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^22.19.19",
32
32
  "tsup": "^8.5.1",
33
33
  "typescript": "^5.9.3",
34
- "@wrongstack/core": "0.77.0"
34
+ "@wrongstack/core": "0.84.1"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"