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 <details> collapsible to formatReasoningDisplay - Matrix adapter handles parseMode='HTML' to skip double-escaping - Reasoning now flows through the standard display path like all adapters
This commit is contained in:
@@ -21,7 +21,7 @@ export interface ChannelAdapter {
|
|||||||
|
|
||||||
// Messaging
|
// Messaging
|
||||||
sendMessage(msg: OutboundMessage): Promise<{ messageId: string }>;
|
sendMessage(msg: OutboundMessage): Promise<{ messageId: string }>;
|
||||||
editMessage(chatId: string, messageId: string, text: string, htmlPrefix?: string): Promise<void>;
|
editMessage(chatId: string, messageId: string, text: string): Promise<void>;
|
||||||
sendTypingIndicator(chatId: string): Promise<void>;
|
sendTypingIndicator(chatId: string): Promise<void>;
|
||||||
stopTypingIndicator?(chatId: string): Promise<void>;
|
stopTypingIndicator?(chatId: string): Promise<void>;
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,19 @@ export function formatReasoningDisplay(
|
|||||||
// Signal: no blockquote support, use italic
|
// Signal: no blockquote support, use italic
|
||||||
return { text: `**Thinking**\n_${truncated}_` };
|
return { text: `**Thinking**\n_${truncated}_` };
|
||||||
}
|
}
|
||||||
|
if (channelId === 'matrix') {
|
||||||
|
// Matrix: collapsible <details> block — rendered natively by Element Web,
|
||||||
|
// falls back to visible block on mobile clients.
|
||||||
|
const escaped = truncated
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/\n/g, '<br>');
|
||||||
|
return {
|
||||||
|
text: `<details><summary>🧠 Thinking</summary>${escaped}</details>`,
|
||||||
|
parseMode: 'HTML',
|
||||||
|
};
|
||||||
|
}
|
||||||
if (channelId === 'telegram' || channelId === 'telegram-mtproto') {
|
if (channelId === 'telegram' || channelId === 'telegram-mtproto') {
|
||||||
// Telegram: use HTML blockquote to bypass telegramify-markdown spacing.
|
// Telegram: use HTML blockquote to bypass telegramify-markdown spacing.
|
||||||
// Convert basic markdown inline formatting to HTML tags so bold/italic
|
// Convert basic markdown inline formatting to HTML tags so bold/italic
|
||||||
@@ -255,36 +268,6 @@ export function formatReasoningDisplay(
|
|||||||
return { text: `> **Thinking**\n${quoted}` };
|
return { text: `> **Thinking**\n${quoted}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Format reasoning as a collapsible <details> 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 <br>
|
|
||||||
const escaped = truncated
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/\n/g, '<br>');
|
|
||||||
|
|
||||||
return {
|
|
||||||
text: `<details><summary>🧠 Thinking</summary><br>${escaped}</details><br>`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format AskUserQuestion options for channel display.
|
* Format AskUserQuestion options for channel display.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -132,9 +132,6 @@ export interface OutboundMessage {
|
|||||||
* 'HTML') and to skip its default markdown conversion. Adapters that don't
|
* 'HTML') and to skip its default markdown conversion. Adapters that don't
|
||||||
* support the specified mode ignore this and fall back to default. */
|
* support the specified mode ignore this and fall back to default. */
|
||||||
parseMode?: string;
|
parseMode?: string;
|
||||||
/** Pre-escaped HTML to prepend to formatted_body only (bypasses markdown conversion).
|
|
||||||
* Used for reasoning blocks with <details> tags that would be double-escaped. */
|
|
||||||
htmlPrefix?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user