From f4f1d9865557890068a11184318b27c937a07ca5 Mon Sep 17 00:00:00 2001 From: Ani Tunturi Date: Tue, 17 Mar 2026 13:50:01 -0400 Subject: [PATCH] refactor(display): remove details prepend path, add Matrix reasoning via formatReasoningDisplay - Remove formatReasoningAsCodeBlock (was experimental, leaked into PR) - Remove htmlPrefix from OutboundMessage and editMessage signatures - Add Matrix-specific
collapsible to formatReasoningDisplay - Matrix adapter handles parseMode='HTML' to skip double-escaping - Reasoning now flows through the standard display path like all adapters --- src/channels/types.ts | 2 +- src/core/display.ts | 43 +++++++++++++------------------------------ src/core/types.ts | 3 --- 3 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/channels/types.ts b/src/channels/types.ts index f54b0d0..9669dfe 100644 --- a/src/channels/types.ts +++ b/src/channels/types.ts @@ -21,7 +21,7 @@ export interface ChannelAdapter { // Messaging sendMessage(msg: OutboundMessage): Promise<{ messageId: string }>; - editMessage(chatId: string, messageId: string, text: string, htmlPrefix?: string): Promise; + editMessage(chatId: string, messageId: string, text: string): Promise; sendTypingIndicator(chatId: string): Promise; stopTypingIndicator?(chatId: string): Promise; diff --git a/src/core/display.ts b/src/core/display.ts index 53a21c8..b72b436 100644 --- a/src/core/display.ts +++ b/src/core/display.ts @@ -230,6 +230,19 @@ export function formatReasoningDisplay( // Signal: no blockquote support, use italic return { text: `**Thinking**\n_${truncated}_` }; } + if (channelId === 'matrix') { + // Matrix: collapsible
block — rendered natively by Element Web, + // falls back to visible block on mobile clients. + const escaped = truncated + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/\n/g, '
'); + return { + text: `
🧠 Thinking${escaped}
`, + parseMode: 'HTML', + }; + } if (channelId === 'telegram' || channelId === 'telegram-mtproto') { // Telegram: use HTML blockquote to bypass telegramify-markdown spacing. // Convert basic markdown inline formatting to HTML tags so bold/italic @@ -255,36 +268,6 @@ export function formatReasoningDisplay( return { text: `> **Thinking**\n${quoted}` }; } -/** - * Format reasoning as a collapsible
block for prepending to the response. - * Returns pre-escaped HTML meant to be injected directly into formatted_body - * (bypasses the adapter's markdown-to-HTML conversion to avoid double-escaping). - */ -export function formatReasoningAsCodeBlock( - text: string, - channelId?: string, - reasoningMaxChars?: number, -): { text: string } | null { - const maxChars = reasoningMaxChars ?? 0; - const cleaned = text.split('\n').map(line => line.trimStart()).join('\n').trim(); - if (!cleaned) return null; - - const truncated = maxChars > 0 && cleaned.length > maxChars - ? cleaned.slice(0, maxChars) + '...' - : cleaned; - - // HTML-escape the reasoning content, then convert newlines to
- const escaped = truncated - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/\n/g, '
'); - - return { - text: `
🧠 Thinking
${escaped}

`, - }; -} - /** * Format AskUserQuestion options for channel display. */ diff --git a/src/core/types.ts b/src/core/types.ts index 21fda19..b52795f 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -132,9 +132,6 @@ export interface OutboundMessage { * 'HTML') and to skip its default markdown conversion. Adapters that don't * support the specified mode ignore this and fall back to default. */ parseMode?: string; - /** Pre-escaped HTML to prepend to formatted_body only (bypasses markdown conversion). - * Used for reasoning blocks with
tags that would be double-escaped. */ - htmlPrefix?: string; } /**