fix(router): escape Discord markdown delimiters instead of stripping
Previously formatOutbound stripped `*`, `_`, `~`, `|`, `` ` ``, `#`, `>`
from prose so they wouldn't trigger Discord formatting. That worked but
silently lost information — `**DONE**`, `STEP_DONE`, ~~strike~~ all
arrived in Discord with the source characters missing.
Switch to backslash-escape so the literal source text shows up while
still suppressing the formatting interpretation. Triple-backtick fenced
code blocks are still preserved verbatim.
Escape is non-idempotent (running it twice double-escapes backslashes),
so split the pipeline:
- sanitizeForOutbound: strip internal tags + tool-call leaks + redact
secrets. Use this for intermediate text that will pass through
another formatOutbound call downstream (work-item storage, channel
wrappers, session-command output).
- formatOutbound: sanitize + neutralizeStrayMarkdown. Reserved for the
single Discord-send boundary in channels/discord.ts.
Internal callers (message-turn-controller, session-commands, the
sendFormattedChannelMessage / sendFormattedTrackedChannelMessage /
editFormattedTrackedChannelMessage wrappers in index.ts, and the cron
reviewer-bot path) now use sanitizeForOutbound so the markdown escape
runs exactly once at the channel boundary.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@ import {
|
||||
getAgentOutputText,
|
||||
} from './agent-output.js';
|
||||
import { createScopedLogger, logger } from './logger.js';
|
||||
import { formatOutbound } from './router.js';
|
||||
import { sanitizeForOutbound } from './router.js';
|
||||
import { shouldResetSessionOnAgentFailure } from './session-recovery.js';
|
||||
import { TASK_STATUS_MESSAGE_PREFIX } from './task-watch-status.js';
|
||||
import { formatElapsedKorean } from './utils.js';
|
||||
@@ -166,7 +166,10 @@ export class MessageTurnController {
|
||||
}
|
||||
|
||||
const raw = getAgentOutputText(result);
|
||||
const text = raw ? formatOutbound(raw) : null;
|
||||
// Use sanitize (no markdown escape) — the Discord channel boundary
|
||||
// applies the final escape pass, and double-escaping would produce
|
||||
// visible backslash garbage.
|
||||
const text = raw ? sanitizeForOutbound(raw) : null;
|
||||
const attachments = getAgentOutputAttachments(result);
|
||||
|
||||
if (raw) {
|
||||
|
||||
Reference in New Issue
Block a user