import assert from 'node:assert/strict'; import { mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import test from 'node:test'; import { OUTBOUND_ARTIFACT_TOOL, OutboundArtifactRegistry, createOutboundArtifactTool, releaseOutboundArtifact, } from '../../../src/channels/shared/semantic/artifact.mjs'; import { TelegramApi } from '../../../src/channels/telegram/telegram-api.mjs'; import { TelegramHarnessBridge } from '../../../src/channels/telegram/telegram-bridge.mjs'; import { splitTelegramRegularText, splitTelegramRichMarkdown, toTelegramRichMarkdown, } from '../../../src/channels/telegram/telegram-rich-message.mjs'; import { TelegramBotClient } from '../../../src/channels/telegram/telegram-runtime.mjs'; const TOKEN = '123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef123456'; function jsonResponse(body, status = 200) { return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' }, }); } function rejected(code = 400) { const error = new Error(`Telegram ${code}`); error.code = `telegram-${code}`; error.status = code; error.providerCode = code; return error; } function uncertain(code = 'telegram-timeout') { const error = new Error('Telegram delivery is uncertain'); error.code = code; error.deliveryOutcome = 'unknown'; return error; } function memoryState() { const sessions = new Map(); const seen = new Set(); return { sessionFor: (key) => sessions.get(key) ?? null, setSession: async (key, value) => sessions.set(key, value), clearSession: async (key) => sessions.delete(key), hasSeen: (id) => seen.has(id), markSeen: async (id) => seen.add(id), }; } async function committedArtifact(t) { const workspace = await mkdtemp(join(tmpdir(), 'dsh-im-telegram-rich-artifact-')); t.after(() => rm(workspace, { recursive: true, force: true })); const registry = new OutboundArtifactRegistry({ uuid: () => 'telegram-rich-artifact' }); t.after(() => registry.clear()); await writeFile(join(workspace, 'result.txt'), 'rich result'); const agent = { session: { header: { id: 'session-rich-artifact', cwd: workspace }, events: [ { type: 'turn/start', data: { turn: 1 } }, { type: 'user/message', data: { turn: 1, source: { rpcId: 'rpc-rich-artifact' } } }, ], }, }; const tool = createOutboundArtifactTool({ registry }); const exec = { name: OUTBOUND_ARTIFACT_TOOL, callId: 'call-rich-artifact', rootCallId: 'call-rich-artifact', token: Symbol('call-rich-artifact'), agent, }; await tool.definition.execute({ path: 'result.txt' }, exec); tool.onResult(exec, { isError: false }); const artifact = registry.take('session-rich-artifact', 1)[0]; t.after(() => releaseOutboundArtifact(artifact)); return artifact; } test('Telegram Rich Markdown keeps structure while escaping raw and encoded HTML', () => { const source = [ '# 标题 😀', '', '- 第一项', ' - 嵌套项', '', '```js', 'const tag = "