@0xmaxma/claude-gateway 1.7.4 → 1.7.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/telegram/markdown.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYjD;AAuFD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsInD"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/telegram/markdown.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mGAAmG;AACnG,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhE;AAkBD,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAK1D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYjD;AAiGD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmJnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,cAAc,CAAC,EAAE,MAAM,GACtB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CASrD"}
@@ -4,8 +4,38 @@
4
4
  * Used by AgentRunner to auto-format agent output before forwarding.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.normalizeTelegramLineBreaks = normalizeTelegramLineBreaks;
8
+ exports.containsTelegramHtml = containsTelegramHtml;
7
9
  exports.hasMarkdown = hasMarkdown;
8
10
  exports.toTelegramHtml = toTelegramHtml;
11
+ exports.resolveTelegramReplyFormat = resolveTelegramReplyFormat;
12
+ /** Telegram does not support HTML <br> tags; normalize raw and escaped variants to line breaks. */
13
+ function normalizeTelegramLineBreaks(text) {
14
+ return text.replace(/(?:<|&lt;)br\s*\/?(?:>|&gt;)/gi, '\n');
15
+ }
16
+ // Opening (with optional attributes) or closing form of every tag Telegram's
17
+ // HTML parse mode honours. Sticky so it can be tested at a given offset without
18
+ // slicing. `[^<>]*` keeps an attribute run from swallowing past the tag. `span`
19
+ // is intentionally excluded: only `<span class="tg-spoiler">` is valid Telegram
20
+ // HTML and pairing a class-bearing open with a bare `</span>` close is
21
+ // error-prone — agents should use the canonical `<tg-spoiler>` element instead.
22
+ const TELEGRAM_HTML_TAG = /<\/?(?:a|b|blockquote|code|del|em|i|ins|pre|s|strike|strong|tg-spoiler|u)(?:\s[^<>]*)?>/iy;
23
+ /** Length of a valid Telegram HTML tag starting at `i` (where text[i] === '<'), or 0 if none. */
24
+ function matchTelegramTag(text, i) {
25
+ if (text[i] !== '<')
26
+ return 0;
27
+ TELEGRAM_HTML_TAG.lastIndex = i;
28
+ const m = TELEGRAM_HTML_TAG.exec(text);
29
+ return m ? m[0].length : 0;
30
+ }
31
+ /** Whether text already contains at least one Telegram-whitelist HTML tag written directly. */
32
+ function containsTelegramHtml(text) {
33
+ for (let i = text.indexOf('<'); i !== -1; i = text.indexOf('<', i + 1)) {
34
+ if (matchTelegramTag(text, i) > 0)
35
+ return true;
36
+ }
37
+ return false;
38
+ }
9
39
  /**
10
40
  * Detects whether text contains markdown formatting patterns
11
41
  * that warrant HTML rendering in Telegram.
@@ -28,6 +58,15 @@ function hasMarkdown(text) {
28
58
  function escapeHtml(text) {
29
59
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
30
60
  }
61
+ /**
62
+ * Escapes a bare `&` (one not already opening a character entity) inside a
63
+ * passed-through Telegram HTML tag — Telegram rejects a raw `&` in e.g. an
64
+ * <a href> query string ("...?a=1&b=2"). `<`/`>` cannot occur here because the
65
+ * tag matcher forbids them in the attribute run, so only `&` needs escaping.
66
+ */
67
+ function escapeTagAmp(tag) {
68
+ return tag.replace(/&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);)/g, '&amp;');
69
+ }
31
70
  /**
32
71
  * Escapes HTML special characters in a URL attribute value.
33
72
  */
@@ -214,6 +253,17 @@ function toTelegramHtml(text) {
214
253
  continue;
215
254
  }
216
255
  }
256
+ // Pass through a valid Telegram HTML tag the agent wrote directly, verbatim.
257
+ // Non-whitelist tags (e.g. <script>) fail the match and are escaped as plain
258
+ // text below, so this is a controlled pass-through, not raw HTML injection.
259
+ if (text[i] === '<') {
260
+ const tagLen = matchTelegramTag(text, i);
261
+ if (tagLen > 0) {
262
+ out.push(escapeTagAmp(text.slice(i, i + tagLen)));
263
+ i += tagLen;
264
+ continue;
265
+ }
266
+ }
217
267
  // Accumulate plain text until next markdown token
218
268
  let j = i + 1;
219
269
  while (j < len) {
@@ -224,6 +274,7 @@ function toTelegramHtml(text) {
224
274
  (c === '*' && text[j + 1] !== '*' && text[j + 1] !== ' ') ||
225
275
  (c === '_' && text[j + 1] !== '_' && text[j + 1] !== ' ') ||
226
276
  c === '[' ||
277
+ (c === '<' && matchTelegramTag(text, j) > 0) ||
227
278
  (c === '#' && (j === 0 || text[j - 1] === '\n')) ||
228
279
  (c === '|' && (j === 0 || text[j - 1] === '\n')))
229
280
  break;
@@ -234,4 +285,24 @@ function toTelegramHtml(text) {
234
285
  }
235
286
  return out.join('');
236
287
  }
288
+ /**
289
+ * Resolve how a channel reply should be sent given an optional explicit format.
290
+ * Mirrors the auto-forward decision in src/agent/runner.ts: agent-authored
291
+ * Telegram HTML tags must trigger HTML mode even when the text contains no
292
+ * markdown, otherwise a pure-HTML reply falls through to raw text with no
293
+ * parse_mode and the tags render literally (e.g. a visible "<b>").
294
+ * - explicit 'html' + already-valid Telegram HTML -> send verbatim under HTML
295
+ * - explicit 'html' (not yet valid) -> convert via toTelegramHtml under HTML
296
+ * - explicit 'text' -> always raw, no parse_mode (caller asked for plain text)
297
+ * - auto (no format) -> HTML when markdown OR Telegram tags are present
298
+ */
299
+ function resolveTelegramReplyFormat(text, explicitFormat) {
300
+ const inputIsHtml = explicitFormat === 'html' && containsTelegramHtml(text);
301
+ const useHtml = inputIsHtml ||
302
+ explicitFormat === 'html' ||
303
+ (!explicitFormat && (hasMarkdown(text) || containsTelegramHtml(text)));
304
+ const sendText = useHtml && !inputIsHtml ? toTelegramHtml(text) : text;
305
+ const parseMode = useHtml ? 'HTML' : undefined;
306
+ return { sendText, parseMode };
307
+ }
237
308
  //# sourceMappingURL=markdown.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/telegram/markdown.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/telegram/markdown.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,8 @@ export function buildChannelInstructions(imageEnabled: boolean): string {
25
25
  '',
26
26
  'Messages from Telegram arrive as <channel source="telegram" chat_id="..." message_id="..." user="..." ts="...">. If the tag has an image_path attribute, Read that file — it is a photo the sender attached. If the tag has attachment_file_id, call download_attachment with that file_id to fetch the file, then Read the returned path. Reply with the reply tool — pass chat_id back. Use reply_to (set to a message_id) only when replying to an earlier message; the latest message doesn\'t need a quote-reply, omit reply_to for normal responses.',
27
27
  '',
28
+ 'Use the current channel\'s reply tool only when you have a user-facing answer. Do not put tool-by-tool progress narration in final text output: use that channel\'s edit-message tool for interim status, then send one concise final reply so automatic forwarding cannot expose an internal worklog.',
29
+ '',
28
30
  'reply accepts file paths (files: ["/abs/path.png"]) for attachments. Use react to add emoji reactions, and edit_message for interim progress updates. Edits don\'t trigger push notifications — when a long task completes, send a new reply so the user\'s device pings.',
29
31
  '',
30
32
  "Telegram's Bot API exposes no history or search — you only see messages as they arrive. If you need earlier context, ask the user to paste it or summarize.",
@@ -3,6 +3,35 @@
3
3
  * Used by AgentRunner to auto-format agent output before forwarding.
4
4
  */
5
5
 
6
+ /** Telegram does not support HTML <br> tags; normalize raw and escaped variants to line breaks. */
7
+ export function normalizeTelegramLineBreaks(text: string): string {
8
+ return text.replace(/(?:<|&lt;)br\s*\/?(?:>|&gt;)/gi, '\n')
9
+ }
10
+
11
+ // Opening (with optional attributes) or closing form of every tag Telegram's
12
+ // HTML parse mode honours. Sticky so it can be tested at a given offset without
13
+ // slicing. `[^<>]*` keeps an attribute run from swallowing past the tag. `span`
14
+ // is intentionally excluded: only `<span class="tg-spoiler">` is valid Telegram
15
+ // HTML and pairing a class-bearing open with a bare `</span>` close is
16
+ // error-prone — agents should use the canonical `<tg-spoiler>` element instead.
17
+ const TELEGRAM_HTML_TAG = /<\/?(?:a|b|blockquote|code|del|em|i|ins|pre|s|strike|strong|tg-spoiler|u)(?:\s[^<>]*)?>/iy
18
+
19
+ /** Length of a valid Telegram HTML tag starting at `i` (where text[i] === '<'), or 0 if none. */
20
+ function matchTelegramTag(text: string, i: number): number {
21
+ if (text[i] !== '<') return 0
22
+ TELEGRAM_HTML_TAG.lastIndex = i
23
+ const m = TELEGRAM_HTML_TAG.exec(text)
24
+ return m ? m[0].length : 0
25
+ }
26
+
27
+ /** Whether text already contains at least one Telegram-whitelist HTML tag written directly. */
28
+ export function containsTelegramHtml(text: string): boolean {
29
+ for (let i = text.indexOf('<'); i !== -1; i = text.indexOf('<', i + 1)) {
30
+ if (matchTelegramTag(text, i) > 0) return true
31
+ }
32
+ return false
33
+ }
34
+
6
35
  /**
7
36
  * Detects whether text contains markdown formatting patterns
8
37
  * that warrant HTML rendering in Telegram.
@@ -28,6 +57,16 @@ function escapeHtml(text: string): string {
28
57
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
29
58
  }
30
59
 
60
+ /**
61
+ * Escapes a bare `&` (one not already opening a character entity) inside a
62
+ * passed-through Telegram HTML tag — Telegram rejects a raw `&` in e.g. an
63
+ * <a href> query string ("...?a=1&b=2"). `<`/`>` cannot occur here because the
64
+ * tag matcher forbids them in the attribute run, so only `&` needs escaping.
65
+ */
66
+ function escapeTagAmp(tag: string): string {
67
+ return tag.replace(/&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);)/g, '&amp;')
68
+ }
69
+
31
70
  /**
32
71
  * Escapes HTML special characters in a URL attribute value.
33
72
  */
@@ -233,6 +272,18 @@ export function toTelegramHtml(text: string): string {
233
272
  }
234
273
  }
235
274
 
275
+ // Pass through a valid Telegram HTML tag the agent wrote directly, verbatim.
276
+ // Non-whitelist tags (e.g. <script>) fail the match and are escaped as plain
277
+ // text below, so this is a controlled pass-through, not raw HTML injection.
278
+ if (text[i] === '<') {
279
+ const tagLen = matchTelegramTag(text, i)
280
+ if (tagLen > 0) {
281
+ out.push(escapeTagAmp(text.slice(i, i + tagLen)))
282
+ i += tagLen
283
+ continue
284
+ }
285
+ }
286
+
236
287
  // Accumulate plain text until next markdown token
237
288
  let j = i + 1
238
289
  while (j < len) {
@@ -244,6 +295,7 @@ export function toTelegramHtml(text: string): string {
244
295
  (c === '*' && text[j + 1] !== '*' && text[j + 1] !== ' ') ||
245
296
  (c === '_' && text[j + 1] !== '_' && text[j + 1] !== ' ') ||
246
297
  c === '[' ||
298
+ (c === '<' && matchTelegramTag(text, j) > 0) ||
247
299
  (c === '#' && (j === 0 || text[j - 1] === '\n')) ||
248
300
  (c === '|' && (j === 0 || text[j - 1] === '\n'))
249
301
  ) break
@@ -255,3 +307,28 @@ export function toTelegramHtml(text: string): string {
255
307
 
256
308
  return out.join('')
257
309
  }
310
+
311
+ /**
312
+ * Resolve how a channel reply should be sent given an optional explicit format.
313
+ * Mirrors the auto-forward decision in src/agent/runner.ts: agent-authored
314
+ * Telegram HTML tags must trigger HTML mode even when the text contains no
315
+ * markdown, otherwise a pure-HTML reply falls through to raw text with no
316
+ * parse_mode and the tags render literally (e.g. a visible "<b>").
317
+ * - explicit 'html' + already-valid Telegram HTML -> send verbatim under HTML
318
+ * - explicit 'html' (not yet valid) -> convert via toTelegramHtml under HTML
319
+ * - explicit 'text' -> always raw, no parse_mode (caller asked for plain text)
320
+ * - auto (no format) -> HTML when markdown OR Telegram tags are present
321
+ */
322
+ export function resolveTelegramReplyFormat(
323
+ text: string,
324
+ explicitFormat?: string,
325
+ ): { sendText: string; parseMode: 'HTML' | undefined } {
326
+ const inputIsHtml = explicitFormat === 'html' && containsTelegramHtml(text)
327
+ const useHtml =
328
+ inputIsHtml ||
329
+ explicitFormat === 'html' ||
330
+ (!explicitFormat && (hasMarkdown(text) || containsTelegramHtml(text)))
331
+ const sendText = useHtml && !inputIsHtml ? toTelegramHtml(text) : text
332
+ const parseMode: 'HTML' | undefined = useHtml ? 'HTML' : undefined
333
+ return { sendText, parseMode }
334
+ }
@@ -7,6 +7,7 @@ import * as path from 'path';
7
7
  import * as fs from 'fs';
8
8
  import * as os from 'os';
9
9
  import { migrateAccess, defaultAccess } from './pure';
10
+ import { chunkText, htmlToPlain } from './typing';
10
11
  import type {
11
12
  ChannelModule,
12
13
  ChannelCapabilities,
@@ -47,6 +48,12 @@ export class TelegramModule implements ChannelModule {
47
48
  private lastError?: string;
48
49
  private _hasMarkdown?: (text: string) => boolean;
49
50
  private _toTelegramHtml?: (text: string) => string;
51
+ private _normalizeTelegramLineBreaks?: (text: string) => string;
52
+ private _containsTelegramHtml?: (text: string) => boolean;
53
+ private _resolveTelegramReplyFormat?: (
54
+ text: string,
55
+ explicitFormat?: string,
56
+ ) => { sendText: string; parseMode: 'HTML' | undefined };
50
57
  private _InputFile?: any;
51
58
  private _typingManager?: any;
52
59
 
@@ -97,7 +104,7 @@ export class TelegramModule implements ChannelModule {
97
104
  format: {
98
105
  type: 'string',
99
106
  enum: ['text', 'html'],
100
- description: "Rendering mode. 'html' enables Telegram formatting (bold, italic, code, links). Caller must escape special chars per HTML rules (&amp; &lt; &gt;). Default: 'text' (plain, no escaping needed).",
107
+ description: "Rendering mode. 'html' accepts pre-rendered Telegram HTML. Raw Markdown is detected and converted automatically. Default: auto-detect Markdown or send plain text.",
101
108
  },
102
109
  },
103
110
  required: ['chat_id', 'text'],
@@ -139,7 +146,7 @@ export class TelegramModule implements ChannelModule {
139
146
  format: {
140
147
  type: 'string',
141
148
  enum: ['text', 'html'],
142
- description: "Rendering mode. 'html' enables Telegram formatting (bold, italic, code, links). Caller must escape special chars per HTML rules (&amp; &lt; &gt;). Default: 'text' (plain, no escaping needed).",
149
+ description: "Rendering mode. 'html' accepts pre-rendered Telegram HTML. Raw Markdown is detected and converted automatically. Default: auto-detect Markdown or send plain text.",
143
150
  },
144
151
  },
145
152
  required: ['chat_id', 'message_id', 'text'],
@@ -188,7 +195,7 @@ export class TelegramModule implements ChannelModule {
188
195
  });
189
196
 
190
197
  // Import pure functions and typing manager
191
- const { hasMarkdown, toTelegramHtml } = await import('./pure');
198
+ const { hasMarkdown, toTelegramHtml, normalizeTelegramLineBreaks, containsTelegramHtml, resolveTelegramReplyFormat } = await import('./pure');
192
199
  const { createWorkingStateManager } = await import('./typing');
193
200
 
194
201
  const typingManager = createWorkingStateManager(
@@ -208,6 +215,9 @@ export class TelegramModule implements ChannelModule {
208
215
 
209
216
  this._hasMarkdown = hasMarkdown;
210
217
  this._toTelegramHtml = toTelegramHtml;
218
+ this._normalizeTelegramLineBreaks = normalizeTelegramLineBreaks;
219
+ this._containsTelegramHtml = containsTelegramHtml;
220
+ this._resolveTelegramReplyFormat = resolveTelegramReplyFormat;
211
221
  this._InputFile = InputFile;
212
222
  this._typingManager = typingManager;
213
223
 
@@ -285,39 +295,18 @@ export class TelegramModule implements ChannelModule {
285
295
  }
286
296
  }
287
297
 
288
- private chunk(text: string, limit: number, mode: 'length' | 'newline'): string[] {
289
- if (text.length <= limit) return [text];
290
- const out: string[] = [];
291
- let rest = text;
292
- while (rest.length > limit) {
293
- let cut = limit;
294
- if (mode === 'newline') {
295
- const para = rest.lastIndexOf('\n\n', limit);
296
- const line = rest.lastIndexOf('\n', limit);
297
- const space = rest.lastIndexOf(' ', limit);
298
- cut = para > limit / 2 ? para : line > limit / 2 ? line : space > 0 ? space : limit;
299
- }
300
- out.push(rest.slice(0, cut));
301
- rest = rest.slice(cut).replace(/^\n+/, '');
302
- }
303
- if (rest) out.push(rest);
304
- return out;
305
- }
306
-
307
298
  private async handleReply(args: Record<string, unknown>): Promise<McpToolResult> {
308
299
  const chat_id = args.chat_id as string;
309
- const text = args.text as string;
300
+ const text = this._normalizeTelegramLineBreaks!(args.text as string);
310
301
  const reply_to = args.reply_to != null ? Number(args.reply_to) : undefined;
311
302
  const files = (args.files as string[] | undefined) ?? [];
312
303
  const explicitFormat = args.format as string | undefined;
313
-
314
- const hasMarkdown = this._hasMarkdown!;
315
- const toTelegramHtml = this._toTelegramHtml!;
316
304
  const InputFile = this._InputFile;
317
305
 
318
- const useHtml = explicitFormat === 'html' || (!explicitFormat && hasMarkdown(text));
319
- const sendText = useHtml && !explicitFormat ? toTelegramHtml(text) : text;
320
- const parseMode = useHtml ? 'HTML' as const : undefined;
306
+ // Single source of truth for reply format detection (shared with receiver-server).
307
+ // Auto mode uses HTML when the text has markdown OR agent-authored Telegram HTML
308
+ // tags the latter fixes HTML-only replies being sent raw (no parse_mode).
309
+ const { sendText, parseMode } = this._resolveTelegramReplyFormat!(text, explicitFormat);
321
310
 
322
311
  this.assertAllowedChat(chat_id);
323
312
 
@@ -331,9 +320,8 @@ export class TelegramModule implements ChannelModule {
331
320
 
332
321
  const access = this.readAccessFile();
333
322
  const limit = Math.max(1, Math.min(access.textChunkLimit ?? MAX_CHUNK_LIMIT, MAX_CHUNK_LIMIT));
334
- const mode = access.chunkMode ?? 'length';
335
323
  const replyMode = access.replyToMode ?? 'first';
336
- const chunks = this.chunk(sendText, limit, mode);
324
+ const chunks = chunkText(sendText, limit, parseMode === 'HTML');
337
325
  const sentIds: number[] = [];
338
326
 
339
327
  try {
@@ -342,11 +330,18 @@ export class TelegramModule implements ChannelModule {
342
330
  reply_to != null &&
343
331
  replyMode !== 'off' &&
344
332
  (replyMode === 'all' || i === 0);
345
- const sent = await this.bot.api.sendMessage(chat_id, chunks[i], {
333
+ const opts = {
346
334
  ...(shouldReplyTo ? { reply_parameters: { message_id: reply_to } } : {}),
347
335
  ...(parseMode ? { parse_mode: parseMode } : {}),
348
- });
349
- sentIds.push(sent.message_id);
336
+ };
337
+ try {
338
+ const sent = await this.bot.api.sendMessage(chat_id, chunks[i], opts);
339
+ sentIds.push(sent.message_id);
340
+ } catch (err) {
341
+ if (!parseMode) throw err;
342
+ const sent = await this.bot.api.sendMessage(chat_id, htmlToPlain(chunks[i]), shouldReplyTo ? { reply_parameters: { message_id: reply_to } } : undefined);
343
+ sentIds.push(sent.message_id);
344
+ }
350
345
  }
351
346
  } catch (err) {
352
347
  const msg = err instanceof Error ? err.message : String(err);
@@ -332,7 +332,12 @@ function countPending(access: Access, kind: 'dm' | 'group'): number {
332
332
  return n
333
333
  }
334
334
 
335
- export { hasMarkdown, toTelegramHtml } from './lib/markdown'
335
+ export { hasMarkdown, toTelegramHtml, containsTelegramHtml, resolveTelegramReplyFormat } from './lib/markdown'
336
+
337
+ /** Telegram does not support HTML <br> tags; normalize raw and escaped variants to line breaks. */
338
+ export function normalizeTelegramLineBreaks(text: string): string {
339
+ return text.replace(/(?:<|&lt;)br\s*\/?(?:>|&gt;)/gi, '\n')
340
+ }
336
341
 
337
342
  export function isMentionedPure(input: GateInput, extraPatterns?: string[]): boolean {
338
343
  const entities = input.messageEntities ?? input.captionEntities ?? []
@@ -29,7 +29,7 @@ import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync,
29
29
  import { homedir } from 'os'
30
30
  import { join, extname, sep } from 'path'
31
31
  import { execFileSync } from 'child_process'
32
- import { createWorkingStateManager, drainOrphanForwards } from './typing'
32
+ import { createWorkingStateManager, drainOrphanForwards, chunkText, htmlToPlain } from './typing'
33
33
  // Import compiled dist/, not raw src/ — src/ is not published (files: ["mcp/"]),
34
34
  // so a src/ import crashes this bun-run receiver on installed packages (the bug
35
35
  // that silenced every bot on systemd installs). Enforced by
@@ -38,7 +38,7 @@ import { formatTurnIncident, type TurnIncident } from '../../../dist/agent/turn-
38
38
  import { createIncidentStore } from '../../../dist/agent/incident-store.js'
39
39
  import type { RecoveryOutcome } from '../../../dist/agent/incident.js'
40
40
  import { initDedupDir, isDuplicate as _isDuplicate, pruneDedup as _pruneDedup } from './dedup'
41
- import { hasMarkdown, toTelegramHtml, migrateAccess, telegramDisplayName } from './pure'
41
+ import { normalizeTelegramLineBreaks, resolveTelegramReplyFormat, migrateAccess, telegramDisplayName } from './pure'
42
42
 
43
43
  // Standalone fallback: default state dir to ~/.claude/channels/telegram
44
44
  const STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join(homedir(), '.claude', 'channels', 'telegram')
@@ -565,29 +565,6 @@ function checkApprovals(): void {
565
565
 
566
566
  setInterval(checkApprovals, 5000).unref()
567
567
 
568
- // Telegram caps messages at 4096 chars. Split long replies, preferring
569
- // paragraph boundaries when chunkMode is 'newline'.
570
- function chunk(text: string, limit: number, mode: 'length' | 'newline'): string[] {
571
- if (text.length <= limit) return [text]
572
- const out: string[] = []
573
- let rest = text
574
- while (rest.length > limit) {
575
- let cut = limit
576
- if (mode === 'newline') {
577
- // Prefer the last double-newline (paragraph), then single newline,
578
- // then space. Fall back to hard cut.
579
- const para = rest.lastIndexOf('\n\n', limit)
580
- const line = rest.lastIndexOf('\n', limit)
581
- const space = rest.lastIndexOf(' ', limit)
582
- cut = para > limit / 2 ? para : line > limit / 2 ? line : space > 0 ? space : limit
583
- }
584
- out.push(rest.slice(0, cut))
585
- rest = rest.slice(cut).replace(/^\n+/, '')
586
- }
587
- if (rest) out.push(rest)
588
- return out
589
- }
590
-
591
568
  // .jpg/.jpeg/.png/.gif/.webp go as photos (Telegram compresses + shows inline);
592
569
  // everything else goes as documents (raw file, no compression).
593
570
  const PHOTO_EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp'])
@@ -678,7 +655,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
678
655
  format: {
679
656
  type: 'string',
680
657
  enum: ['text', 'html'],
681
- description: "Rendering mode. 'html' enables Telegram formatting (bold, italic, code, links). Caller must escape special chars per HTML rules (&amp; &lt; &gt;). Default: 'text' (plain, no escaping needed).",
658
+ description: "Rendering mode. 'html' accepts pre-rendered Telegram HTML. Raw Markdown is detected and converted automatically. Default: auto-detect Markdown or send plain text.",
682
659
  },
683
660
  },
684
661
  required: ['chat_id', 'text'],
@@ -720,7 +697,7 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
720
697
  format: {
721
698
  type: 'string',
722
699
  enum: ['text', 'html'],
723
- description: "Rendering mode. 'html' enables Telegram formatting (bold, italic, code, links). Caller must escape special chars per HTML rules (&amp; &lt; &gt;). Default: 'text' (plain, no escaping needed).",
700
+ description: "Rendering mode. 'html' accepts pre-rendered Telegram HTML. Raw Markdown is detected and converted automatically. Default: auto-detect Markdown or send plain text.",
724
701
  },
725
702
  },
726
703
  required: ['chat_id', 'message_id', 'text'],
@@ -735,14 +712,11 @@ mcp.setRequestHandler(CallToolRequestSchema, async req => {
735
712
  switch (req.params.name) {
736
713
  case 'reply': {
737
714
  const chat_id = args.chat_id as string
738
- const text = args.text as string
715
+ const text = normalizeTelegramLineBreaks(args.text as string)
739
716
  const reply_to = args.reply_to != null ? Number(args.reply_to) : undefined
740
717
  const files = (args.files as string[] | undefined) ?? []
741
718
  const explicitFormat = args.format as string | undefined
742
- // Auto-detect markdown when caller didn't specify format explicitly
743
- const useHtml = explicitFormat === 'html' || (!explicitFormat && hasMarkdown(text))
744
- const sendText = useHtml && !explicitFormat ? toTelegramHtml(text) : text
745
- const parseMode = useHtml ? 'HTML' as const : undefined
719
+ const { sendText, parseMode } = resolveTelegramReplyFormat(text, explicitFormat)
746
720
 
747
721
  assertAllowedChat(chat_id)
748
722
 
@@ -756,9 +730,8 @@ mcp.setRequestHandler(CallToolRequestSchema, async req => {
756
730
 
757
731
  const access = loadAccess()
758
732
  const limit = Math.max(1, Math.min(access.textChunkLimit ?? MAX_CHUNK_LIMIT, MAX_CHUNK_LIMIT))
759
- const mode = access.chunkMode ?? 'length'
760
733
  const replyMode = access.replyToMode ?? 'first'
761
- const chunks = chunk(sendText, limit, mode)
734
+ const chunks = chunkText(sendText, limit, parseMode === 'HTML')
762
735
  const sentIds: number[] = []
763
736
 
764
737
  try {
@@ -767,11 +740,22 @@ mcp.setRequestHandler(CallToolRequestSchema, async req => {
767
740
  reply_to != null &&
768
741
  replyMode !== 'off' &&
769
742
  (replyMode === 'all' || i === 0)
770
- const sent = await bot.api.sendMessage(chat_id, chunks[i], {
743
+ const opts = {
771
744
  ...(shouldReplyTo ? { reply_parameters: { message_id: reply_to } } : {}),
772
745
  ...(parseMode ? { parse_mode: parseMode } : {}),
773
- })
774
- sentIds.push(sent.message_id)
746
+ }
747
+ try {
748
+ const sent = await bot.api.sendMessage(chat_id, chunks[i], opts)
749
+ sentIds.push(sent.message_id)
750
+ } catch (err) {
751
+ if (!parseMode) throw err
752
+ const sent = await bot.api.sendMessage(
753
+ chat_id,
754
+ htmlToPlain(chunks[i]),
755
+ shouldReplyTo ? { reply_parameters: { message_id: reply_to } } : undefined,
756
+ )
757
+ sentIds.push(sent.message_id)
758
+ }
775
759
  }
776
760
  } catch (err) {
777
761
  const msg = err instanceof Error ? err.message : String(err)
@@ -66,6 +66,7 @@ export function openTagStack(html: string): string[] {
66
66
  /** Strip Telegram-HTML tags and unescape entities → plain-text equivalent. */
67
67
  export function htmlToPlain(html: string): string {
68
68
  return html
69
+ .replace(/&lt;br\s*\/?&gt;/gi, '\n')
69
70
  .replace(/<[^>]+>/g, '')
70
71
  .replace(/&lt;/g, '<')
71
72
  .replace(/&gt;/g, '>')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmaxma/claude-gateway",
3
- "version": "1.7.4",
3
+ "version": "1.7.6",
4
4
  "description": "Multi-agent gateway for Claude",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,7 +31,7 @@
31
31
  "start": "node --no-warnings=ExperimentalWarning --env-file-if-exists=.env dist/index.js",
32
32
  "postinstall": "if command -v bun >/dev/null 2>&1; then bun install --cwd mcp; else echo 'Warning: bun not found. Run `cd mcp && bun install` manually to enable MCP tools.'; fi",
33
33
  "pretest": "npm run build",
34
- "test": "jest",
34
+ "test": "NODE_OPTIONS=--max-old-space-size=2560 jest",
35
35
  "pretest:unit": "npm run build",
36
36
  "test:unit": "jest --testPathPattern=unit",
37
37
  "test:e2e": "jest --testPathPattern=tests/e2e --forceExit",