diff --git a/apps/desktop/src/lib/chat-messages.test.ts b/apps/desktop/src/lib/chat-messages.test.ts index 22fcebd25a..5bac8a4fd3 100644 --- a/apps/desktop/src/lib/chat-messages.test.ts +++ b/apps/desktop/src/lib/chat-messages.test.ts @@ -183,6 +183,22 @@ describe('toChatMessages', () => { expect(chatMessageText(message)).toBe('@file:tsconfig.tsbuildinfo\n\nwhat is this file') }) + it('hides a persisted Discord triggering-message note but keeps the reply pointer (#114719)', () => { + const note = + '[Triggering message id: `1550380365858865156` — use as `message_id` for reply/react/pin via the discord tools.]' + + const [plain, , replied, assistant] = toChatMessages([ + { role: 'user', content: `${note}\n\nCreate a project plan for Q4`, timestamp: 1 }, + { role: 'assistant', content: 'ok', timestamp: 2 }, + { role: 'user', content: `[Replying to: "Create a project plan for Q4"]\n\n${note}\n\nyes do that`, timestamp: 3 }, + { role: 'assistant', content: note, timestamp: 4 } + ]) + + expect(chatMessageText(plain)).toBe('Create a project plan for Q4') + expect(chatMessageText(replied)).toBe('[Replying to: "Create a project plan for Q4"]\n\nyes do that') + expect(chatMessageText(assistant)).toBe(note) + }) + it('renders MEDIA tags as assistant attachment links', () => { const [message] = toChatMessages([ { diff --git a/apps/desktop/src/lib/chat-messages/hydration.ts b/apps/desktop/src/lib/chat-messages/hydration.ts index f6bd0da5dd..59e539130f 100644 --- a/apps/desktop/src/lib/chat-messages/hydration.ts +++ b/apps/desktop/src/lib/chat-messages/hydration.ts @@ -19,6 +19,12 @@ const ATTACHED_CONTEXT_MARKER_RE = /(?:^|\n)--- Attached Context ---\s*\n/ const CONTEXT_WARNINGS_MARKER_RE = /(?:^|\n)--- Context Warnings ---[\s\S]*$/ const CONTEXT_REF_RE = /@(file|folder|url|image|tool|terminal):(?:"[^"\n]+"|'[^'\n]+'|`[^`\n]+`|\S+)/g +// Gateway routing note for Discord turns (gateway/run_inbound.py::discord_triggering_note). +// Current gateways persist the authored text; this heals rows written before that fix. Only +// the note is model-facing — the `[Replying to: …]` pointer next to it is kept. +const DISCORD_TRIGGERING_NOTE_RE = + /(^|\n)\[Triggering message id: `[^`\n]*` — use as `message_id` for reply\/react\/pin via the discord tools\.\]\n*/ + /** * Reply text from a Responses-API `codex_message_items` sidecar (#68321), for rows * whose `content` persisted empty. `commentary` / `analysis` items are mid-turn @@ -88,12 +94,14 @@ function codexMessageItemText(message: SessionMessage): string { } function displayContentForMessage(role: SessionMessage['role'], content: unknown): string { - const textContent = textFromUnknown(content) + const rawText = textFromUnknown(content) if (role !== 'user') { - return textContent + return rawText } + const textContent = rawText.replace(DISCORD_TRIGGERING_NOTE_RE, '$1') + // A `/skill` turn is stored expanded (the whole skill body). Current // gateways project it to the invocation before it ever reaches us; this is // the fallback for an older backend that still ships the raw payload.