import { describe, expect, it } from 'vitest'; import { formatVisibleChatTranscript, getToolVisibilityId, } from '@/pages/Chat/chat-transcript'; import { mergeChatCommandCatalog } from '@/pages/Chat/chat-command-registry'; import type { RawMessage } from '@/types/chat'; const messages: RawMessage[] = [ { id: 'user-1', role: 'user', timestamp: Date.UTC(2026, 6, 18, 8, 0, 0), content: [ { type: 'text', text: 'Inspect the attachment.' }, { type: 'image', mimeType: 'image/png', data: 'VERY_SECRET_BASE64' }, ], _attachedFiles: [ { fileName: 'diagram.png', mimeType: 'image/png', fileSize: 10, preview: 'data:image/png;base64,ALSO_SECRET', filePath: 'C:\\Users\\alice\\secret\\diagram.png', runtimeUrl: 'http://127.0.0.1:43123/api/files?path=C%3A%5CUsers%5Calice%5Csecret%5Cdiagram.png', }, ], }, { id: 'assistant-1', role: 'assistant', content: [ { type: 'thinking', thinking: 'Private chain fragment' }, { type: 'tool_use', id: 'tool-1', name: 'read', input: { path: '/Users/alice/private/notes.txt', screenshot: 'data:image/png;base64,TOOL_SECRET', rawBytes: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', }, }, { type: 'text', text: 'Finished.' }, ], }, ]; describe('formatVisibleChatTranscript', () => { it('writes binary placeholders and never emits data, base64, loopback URLs, or absolute paths', () => { const markdown = formatVisibleChatTranscript(messages, { showThinking: true, showTimestamps: true, expandedToolIds: new Set([getToolVisibilityId('assistant-1', 'tool-1', 0)]), }); expect(markdown).toContain('[Attached image/png: diagram.png]'); expect(markdown).toContain('[Attached image/png: attachment]'); expect(markdown).toContain('Private chain fragment'); expect(markdown).toContain('Tool: read'); expect(markdown).toContain('[Local path: notes.txt]'); expect(markdown).not.toMatch(/VERY_SECRET|ALSO_SECRET|TOOL_SECRET|A{64}|base64/i); expect(markdown).not.toMatch(/127\.0\.0\.1|C:\\Users|\/Users\/alice/); }); it('omits thinking and collapsed tool details while retaining visible answers and tool names', () => { const markdown = formatVisibleChatTranscript(messages, { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('Inspect the attachment.'); expect(markdown).toContain('Finished.'); expect(markdown).toContain('Tool: read'); expect(markdown).not.toContain('Private chain fragment'); expect(markdown).not.toContain('notes.txt'); expect(markdown).not.toContain('2026-07-18'); }); it('defaults the explicit answer-only mode to user messages and the final answer', () => { const markdown = formatVisibleChatTranscript(messages, { showThinking: true, showTimestamps: false, includeExecution: false, expandedToolIds: new Set(), }); expect(markdown).toContain('Inspect the attachment.'); expect(markdown).toContain('Finished.'); expect(markdown).not.toContain('Private chain fragment'); expect(markdown).not.toContain('Tool: read'); }); it('redacts alternate binary encodings, file URLs, IPv6 loopback, and additional absolute roots', () => { const base64Url = `${'a'.repeat(32)}-${'b'.repeat(32)}_${'c'.repeat(8)}`; const wrappedBase64 = `${'A'.repeat(40)}\n${'B'.repeat(40)}`; const markdown = formatVisibleChatTranscript([{ id: 'assistant-safety', role: 'assistant', content: [{ type: 'tool_use', id: 'tool-safety', name: 'inspect', input: { dataUrl: `data:image/png;charset=utf-8;name=secret.png;base64,${wrappedBase64}`, base64Url, ipv6Loopback: 'http://[::1]:43123/private/file.png', fileUrl: 'file:///Users/alice/private/file.txt', hostedFileUrl: 'file://localhost/Users/alice/private/hosted.txt', remoteHostedFileUrl: 'file://server/share/remote-hosted.txt', rootPath: '/root/.ssh/id_ed25519', usrPath: '/usr/local/share/private.txt', srvPath: '/srv/internal/secret.txt', customPath: '/company/internal/private/roadmap.txt', rootFile: '/secret.txt', forwardWindowsPath: 'C:/secret.txt', windowsPath: 'C:\\Users\\alice\\private\\windows.txt', spacedWindowsPath: 'C:\\Users\\Alice\\Private Notes\\secret.txt', uncPath: '\\\\fileserver\\private\\unc.txt', }, }], }], { showThinking: true, showTimestamps: true, expandedToolIds: new Set([ getToolVisibilityId('assistant-safety', 'tool-safety', 0), ]), }); expect(markdown).toContain('[Binary data omitted]'); expect(markdown).toContain('[Local path: file.txt]'); expect(markdown).toContain('[Local path: id_ed25519]'); expect(markdown).not.toContain(base64Url); expect(markdown).not.toContain('A'.repeat(40)); expect(markdown).not.toContain('B'.repeat(40)); expect(markdown).not.toMatch( /data:image|base64|http:\/\/\[::1\]|file:\/\/|\/root\/|\/usr\/|\/srv\/|\/company\/|\/secret\.txt|Private Notes/i, ); expect(markdown).not.toMatch(/C:[/\\]|\\\\fileserver/); }); it('preserves an extensionless slash token because it may be a dynamic project command', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ '/single', '/review.v2', 'Use /base64 now', '[the guide](/docs/getting-started)', 'Call /api/v1/users', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toMatch(/\n\/single\n/); expect(markdown).toContain('/review.v2'); expect(markdown).toContain('Use /base64 now'); expect(markdown).toContain('[the guide](/docs/getting-started)'); expect(markdown).toContain('Call /api/v1/users'); }); it('preserves common root-relative routes without merging adjacent paths', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'GET /v1/users now', 'Load /assets/js/app.js and /health/check', 'Paths /Volumes/Private/file.txt and /data/private/cache.bin', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('GET /v1/users now'); expect(markdown).toContain('Load /assets/js/app.js and /health/check'); expect(markdown).toContain( 'Paths [Local path: file.txt] and [Local path: cache.bin]', ); }); it('does not exempt an absolute local path merely because it is a Markdown link', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'See [guide](/docs/getting-started).', 'See [secret](/Users/alice/private/secret.txt).', 'See [volume](/Volumes/Private/volume.txt).', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('See [guide](/docs/getting-started).'); expect(markdown).toContain('See [secret]([Local path: secret.txt]).'); expect(markdown).toContain('See [volume]([Local path: volume.txt]).'); expect(markdown).not.toMatch(/\/Users\/alice|\/Volumes\/Private/); }); it('preserves a known nested project command while sanitizing path arguments', () => { const commandCatalog = mergeChatCommandCatalog([{ name: 'workspace/build', hints: ['$ARGUMENTS'], }]); const slashCommandNames = new Set(commandCatalog.flatMap( (command) => [command.inputName, ...command.aliases], )); const markdown = formatVisibleChatTranscript([{ role: 'user', content: '/workspace/build inspect /Users/alice/private/input.txt', }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), slashCommandNames, }); expect(markdown).toContain('/workspace/build inspect'); expect(markdown).toContain('[Local path: input.txt]'); expect(markdown).not.toContain('/Users/alice/private'); }); it('redacts forward-slash UNC paths and backtick-wrapped custom-root paths', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: '//server/share/private.txt\nGenerated at `/company/internal/secret.txt`', }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('[Local path: private.txt]'); expect(markdown).toContain('`[Local path: secret.txt]`'); expect(markdown).not.toMatch(/\/\/server\/share|\/company\/internal/); }); it('redacts encoded and non-base64 data URLs, shorthand loopback, and spaced POSIX paths', () => { const oversizedDataHeader = 'a'.repeat(2_050); const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'data:;base64,U0VDUkVUX1BBWUxPQUQ=', 'data:;base64,U0VD UkVU', 'data:image/png;base64,%2FSECRET_PAYLOAD_12345%3D', 'data:text/plain,PLAIN_TEXT_SECRET', 'data:text/plain;name="foo bar";base64,U0VDUkVU', 'data:foo,HEADER_FOO_SECRET', 'data:text,HEADER_TEXT_SECRET', 'data:@,HEADER_SYMBOL_SECRET', 'metadata:text/plain,hello', 'The data: are stored, safely.', 'data:image/png;base64,U0VDUkVU Done safely.', `data:text/plain;name=${oversizedDataHeader},HEADER_LIMIT_SECRET`, 'data:text/plain;name="foo,QUOTE_SECRET', 'http://127.1:43123/private/file.png', 'http://localhost.:43123/private/dotted.png', 'Open `/Users/Alice/Private Notes/secret.txt` safely.', 'Use /copy now', '/copy', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('[Binary data omitted]'); expect(markdown).toContain('[Runtime attachment: file.png]'); expect(markdown).toContain('[Runtime attachment: dotted.png]'); expect(markdown).toContain('Open `[Local path: secret.txt]` safely.'); expect(markdown).toContain('Use /copy now'); expect(markdown).toMatch(/\n\/copy\n/); expect(markdown).not.toMatch( /127\.1|localhost\.|\/Users\/Alice|Private Notes\/secret\.txt/i, ); expect(markdown).not.toMatch(/(^|\n)data:/im); expect(markdown).not.toContain('U0VDUkVUX1BBWUxPQUQ='); expect(markdown).not.toMatch(/U0VD|UkVU/); expect(markdown).not.toContain('%2FSECRET_PAYLOAD_12345%3D'); expect(markdown).not.toContain('PLAIN_TEXT_SECRET'); expect(markdown).toContain('metadata:text/plain,hello'); expect(markdown).not.toMatch( /HEADER_FOO_SECRET|HEADER_TEXT_SECRET|HEADER_SYMBOL_SECRET|are stored, safely/, ); expect(markdown).toContain('[Binary data omitted] Done safely.'); expect(markdown).not.toMatch(/HEADER_LIMIT_SECRET|QUOTE_SECRET/); }); it('redacts complete quoted data and loopback URL payloads with legal delimiters', () => { const markdown = formatVisibleChatTranscript([{ id: 'assistant-delimited-urls', role: 'assistant', content: [{ type: 'tool_use', id: 'tool-delimited-urls', name: 'inspect', input: { parenthesized: 'data:text/plain,HEAD_SECRET)TAIL_SECRET', apostrophe: 'data:text/plain,ONE_SECRET\'TWO_SECRET', backtick: 'data:text/plain,LEFT_SECRET`RIGHT_SECRET', spaced: 'data:text/plain,SEC RET', loopbackParen: 'http://127.0.0.1:43123/private)TOP_SECRET_DIR/public.png', loopbackApostrophe: 'http://localhost:43123/private\'SECOND_SECRET_DIR/public.png', loopbackQuote: 'http://localhost:43123/private"HTTP_SECRET_DIR/public.png', fileParen: 'file:///Users/alice/private)FILE_PAREN_SECRET/public.txt', fileQuote: 'file:///Users/alice/private"FILE_SECRET_DIR/public.txt', }, }], }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set([ getToolVisibilityId( 'assistant-delimited-urls', 'tool-delimited-urls', 0, ), ]), }); expect(markdown).toContain('[Binary data omitted]'); expect(markdown).toContain('[Runtime attachment: public.png]'); expect(markdown).toContain('[Local path: public.txt]'); expect(markdown).not.toMatch( /HEAD_SECRET|TAIL_SECRET|ONE_SECRET|TWO_SECRET|LEFT_SECRET|RIGHT_SECRET|SEC RET|TOP_SECRET_DIR|SECOND_SECRET_DIR|HTTP_SECRET_DIR|FILE_PAREN_SECRET|FILE_SECRET_DIR/, ); }); it('redacts malformed data headers and direct unquoted local URL suffixes', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'data: text/plain,LEADING_SPACE_SECRET', 'data:\ttext/plain,TAB_HEADER_SECRET', 'data:\ftext/plain,FORM_FEED_HEADER_SECRET', 'data:text/\nplain,NEWLINE_HEADER_SECRET', 'data:text/plain;name=foo`bar,HEADER_BACKTICK_SECRET', 'data:text/plain;name=foo\\,BACKSLASH_COMMA_SECRET', 'da\nta:text/plain,DATA_SCHEME_SECRET', 'ht\ntp://localhost:43123/private/HTTP_SCHEME_SECRET/public.png', 'fi\nle:///Users/Alice/private/FILE_SCHEME_SECRET/public.txt', 'http://127.0.0.1:43123/private)TOP_SECRET_DIR/public.png', 'file:///Users/alice/private)FILE_SECRET_DIR/public.txt', 'http://local\nhost:43123/private/NEWLINE_LOOP_SECRET/public.png', 'file:///Users/ali\nce/private/NEWLINE_FILE_SECRET/public.txt', 'http://localhost:43123/private/\nHTTP_FINAL_FOLD_SECRET.png', 'file:///Users/Alice/private/\r\nFILE_FINAL_FOLD_SECRET.txt', '`http://localhost:43123/private/\nHTTP_WRAPPED_FOLD_SECRET.png`', '`file:///Users/Alice/private/\nFILE_WRAPPED_FOLD_SECRET.txt`', 'http://localhost:43123/Private Notes/HTTP_SPACE_SECRET/public.png', 'file:///Users/Alice/Private Notes/FILE_SPACE_SECRET/public.txt', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('[Runtime attachment: public.png]'); expect(markdown).toContain('[Local path: public.txt]'); expect(markdown).toContain( '[Runtime attachment: HTTP_FINAL_FOLD_SECRET.png]', ); expect(markdown).toContain('[Local path: FILE_FINAL_FOLD_SECRET.txt]'); expect(markdown).toContain( '`[Runtime attachment: HTTP_WRAPPED_FOLD_SECRET.png]`', ); expect(markdown).toContain( '`[Local path: FILE_WRAPPED_FOLD_SECRET.txt]`', ); expect(markdown).not.toMatch(/private\/\r?\n(?:HTTP|FILE)_(?:FINAL|WRAPPED)_FOLD_SECRET/); expect(markdown).not.toMatch( /LEADING_SPACE_SECRET|TAB_HEADER_SECRET|FORM_FEED_HEADER_SECRET|NEWLINE_HEADER_SECRET|HEADER_BACKTICK_SECRET|BACKSLASH_COMMA_SECRET|DATA_SCHEME_SECRET|HTTP_SCHEME_SECRET|FILE_SCHEME_SECRET|TOP_SECRET_DIR|FILE_SECRET_DIR|NEWLINE_LOOP_SECRET|NEWLINE_FILE_SECRET|HTTP_SPACE_SECRET|FILE_SPACE_SECRET/, ); }); it('stops a quoted data URL at its closing wrapper', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: 'Open "data:text/plain,secret" and then "click Save".', }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain( 'Open "[Binary data omitted]" and then "click Save".', ); expect(markdown).not.toContain('secret'); }); it.each(['\n', '\r\n'])( 'stops an unclosed quoted data URL at the current line for %j', (lineBreak) => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: `Open "data:text/plain,secret${lineBreak}Keep this visible.`, }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('Open "[Binary data omitted]'); expect(markdown).toContain('Keep this visible.'); expect(markdown).not.toContain('secret'); }, ); it.each([ ['"', '\n'], ['"', '\r\n'], ['\'', '\n'], ['\'', '\r\n'], ['`', '\n'], ['`', '\r\n'], ])( 'does not consume visible text after a line-ended Data URL (%s, %j)', (wrapper, lineBreak) => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: `Open ${wrapper}data:text/plain,FIRST_SECRET${lineBreak}` + `Keep this visible ${wrapper}quoted text${wrapper}.`, }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain(`Open ${wrapper}[Binary data omitted]`); expect(markdown).toContain( `Keep this visible ${wrapper}quoted text${wrapper}.`, ); expect(markdown).not.toContain('FIRST_SECRET'); }, ); it.each([ ['http://localhost:43123/private/', '[Runtime attachment: private]', '', '\n'], ['http://localhost:43123/private/', '[Runtime attachment: private]', '"', '\r\n'], ['file:///Users/Alice/private/', '[Local path: private]', '', '\n'], ['file:///Users/Alice/private/', '[Local path: private]', '`', '\r\n'], ])( 'does not fold ordinary prose into a line-ended local URL (%s, %s, %j)', (url, placeholder, wrapper, lineBreak) => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: `Open ${wrapper}${url}${lineBreak}` + `Keep this visible.${wrapper}`, }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain(`Open ${wrapper}${placeholder}`); expect(markdown).toContain(`Keep this visible.${wrapper}`); }, ); it('preserves surrounding Markdown and prose when redacting Windows and file URL paths', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'Open `C:\\Users\\alice\\secret\\file.txt` and retry.', 'See `file:///Users/Alice/Private Notes/file.txt` safely.', 'See file:///Users/alice/private/file.txt and retry.', 'Open /Users/alice/file.txt and retry.', 'Open /Users/Alice/Private Notes/direct-posix.txt safely.', 'Open /Volumes/Private/volume.txt safely.', 'Open /data/private/cache.bin safely.', 'Open /company/internal/roadmap.txt safely.', 'Open /acme/Private Notes/acme.txt safely.', 'Open /project/My Folder/project.txt safely.', 'Open /Custom Root/private/custom.txt safely.', 'Open C:\\Users\\alice\\file.txt and retry.', 'Open C:\\Users\\Alice\\Private Notes\\direct-windows.txt safely.', 'Open C:/Users/alice/file.txt and retry.', 'Open C:/Users/Alice/Private Notes/direct-forward.txt safely.', 'Open \\\\server\\share\\file.txt and retry.', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('Open `[Local path: file.txt]` and retry.'); expect(markdown).toContain('See `[Local path: file.txt]` safely.'); expect(markdown).toContain('See [Local path: file.txt] and retry.'); expect(markdown.match(/Open \[Local path: file\.txt\] and retry\./g)) .toHaveLength(4); expect(markdown).toContain('Open [Local path: direct-posix.txt] safely.'); expect(markdown).toContain('Open [Local path: direct-windows.txt] safely.'); expect(markdown).toContain('Open [Local path: direct-forward.txt] safely.'); expect(markdown).toContain('Open [Local path: volume.txt] safely.'); expect(markdown).toContain('Open [Local path: cache.bin] safely.'); expect(markdown).toContain('Open [Local path: roadmap.txt] safely.'); expect(markdown).toContain('Open [Local path: acme.txt] safely.'); expect(markdown).toContain('Open [Local path: project.txt] safely.'); expect(markdown).toContain('Open [Local path: custom.txt] safely.'); expect(markdown).not.toMatch( /Private Notes|My Folder|Custom Root|\/Volumes\/|\/data\/private|\/company\/internal/, ); }); it('redacts a short Base64 payload in visible prose without changing the command token', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'Image base64=U0VDUkVU', 'Wrapped base64: `U0VDUkVU`', 'Spaced base64: U0VD UkVU', 'Folded base64: U0VD\nUkVU', 'CRLF base64: U0VD\r\nUkVU', 'Triple folded base64: QUJD\nREVG\nR0hJ', 'The data: analysis is complete.', 'Screenshot: attached below.', 'Preview: available tomorrow.', 'Use /base64 now', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('Image base64=[Binary data omitted]'); expect(markdown).toContain('Wrapped base64: `[Binary data omitted]`'); expect(markdown).toContain('Spaced base64: [Binary data omitted]'); expect(markdown).toContain('Folded base64: [Binary data omitted]'); expect(markdown).toContain('CRLF base64: [Binary data omitted]'); expect(markdown).toContain('Triple folded base64: [Binary data omitted]'); expect(markdown).toContain('The data: analysis is complete.'); expect(markdown).toContain('Screenshot: attached below.'); expect(markdown).toContain('Preview: available tomorrow.'); expect(markdown).toContain('Use /base64 now'); expect(markdown).not.toContain('U0VDUkVU'); }); it('keeps multiple local paths on one line as separate placeholders', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'Paths C:\\Users\\Alice\\one.txt and C:\\data\\two.txt done', 'Paths C:/Users/Alice/one.txt and C:/data/two.txt done', 'Paths \\\\server\\share\\one.txt and \\\\server\\share\\two.txt done', 'Paths //server/share/one.txt and //server/share/two.txt done', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown.match(/\[Local path: one\.txt\]/g)).toHaveLength(4); expect(markdown.match(/\[Local path: two\.txt\]/g)).toHaveLength(4); expect(markdown.match(/ and /g)).toHaveLength(4); expect(markdown).not.toMatch(/C:[/\\]|\/\/server|\\\\server/); }); it('stops spaced paths and URLs at punctuation or a following absolute value', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'POSIX /Volumes/one.txt, /data/two.txt done', 'POSIX /Volumes/one.txt; /data/two.txt done', 'POSIX /Volumes/one.txt /data/two.txt done', 'POSIX /Volumes/one.txt,然后 /data/two.txt done', 'POSIX /Users/Alice/Private Notes/one.txt. Next /data/two.txt done', 'WIN C:\\Users\\A\\one.txt, C:\\Users\\B\\two.txt done', 'URL http://localhost:43123/Private Notes/one.png, http://localhost:43123/Private Notes/two.png done', 'URL file:///Users/Alice/Private Notes/one.txt; file:///Users/Alice/Private Notes/two.txt done', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown.match(/\[Local path: one\.txt\]/g)).toHaveLength(7); expect(markdown.match(/\[Local path: two\.txt\]/g)).toHaveLength(7); expect(markdown.match(/\[Runtime attachment: one\.png\]/g)).toHaveLength(1); expect(markdown.match(/\[Runtime attachment: two\.png\]/g)).toHaveLength(1); expect(markdown).toContain( '[Local path: one.txt]. Next [Local path: two.txt] done', ); expect(markdown).not.toMatch( /\/Volumes\/|\/data\/|\/Users\/|C:\\|localhost:43123|Private Notes/, ); }); it('keeps punctuation inside one directory while redacting its full path', () => { const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [ 'Open /Users/Alice/Smith, John/POSIX_COMMA_SECRET.txt safely.', 'Open C:\\Users\\Alice\\Smith, John\\WIN_COMMA_SECRET.txt safely.', 'Open C:/Users/Alice/R&D/WIN_AMP_SECRET.txt safely.', 'Open file:///Users/Alice/Smith, John/FILE_COMMA_SECRET.txt safely.', 'Open http://localhost:43123/Smith, John/HTTP_COMMA_SECRET.png safely.', ].join('\n'), }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('[Local path: POSIX_COMMA_SECRET.txt]'); expect(markdown).toContain('[Local path: WIN_COMMA_SECRET.txt]'); expect(markdown).toContain('[Local path: WIN_AMP_SECRET.txt]'); expect(markdown).toContain('[Local path: FILE_COMMA_SECRET.txt]'); expect(markdown).toContain('[Runtime attachment: HTTP_COMMA_SECRET.png]'); expect(markdown).not.toMatch(/Smith, John|R&D|\/Users\/|C:[/\\]|localhost/); }); it('bounds punctuation-heavy path scanning for long visible messages', () => { const startedAt = performance.now(); const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: `Open /Custom Root/${'a,'.repeat(100_000)}tail/file.txt safely.`, }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); const elapsedMilliseconds = performance.now() - startedAt; expect(markdown).toContain('[Local path: file.txt]'); expect(elapsedMilliseconds).toBeLessThan(1_500); }); it.each([ ['circular', (() => { const value: Record = {}; value.self = value; return value; })()], ['BigInt', { value: 1n }], ['throwing toJSON', { toJSON: () => { throw new Error('secret serializer failure'); } }], ])('uses a fixed placeholder when %s tool input cannot be serialized', (_label, input) => { const markdown = formatVisibleChatTranscript([{ id: 'assistant-unserializable', role: 'assistant', content: [{ type: 'tool_use', id: 'tool-unserializable', name: 'inspect', input, }], }], { showThinking: true, showTimestamps: false, expandedToolIds: new Set([ getToolVisibilityId('assistant-unserializable', 'tool-unserializable', 0), ]), }); expect(markdown).toContain('[Tool details unavailable]'); }); it('redacts short Base64 under explicit binary tool keys', () => { const shortBase64 = 'U0VDUkVU'; const markdown = formatVisibleChatTranscript([{ id: 'assistant-short-binary', role: 'assistant', content: [{ type: 'tool_use', id: 'tool-short-binary', name: 'inspect', input: { screenshotBase64: shortBase64, rawBytes: shortBase64, label: 'ordinary short text', }, }], }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set([ getToolVisibilityId('assistant-short-binary', 'tool-short-binary', 0), ]), }); expect(markdown).not.toContain(shortBase64); expect(markdown.match(/\[Binary data omitted\]/g)).toHaveLength(2); expect(markdown).toContain('ordinary short text'); }); it('redacts short Base64 from JSON-encoded string tool input', () => { const shortBase64 = 'U0VDUkVU'; const markdown = formatVisibleChatTranscript([{ id: 'assistant-string-binary', role: 'assistant', content: [{ type: 'tool_use', id: 'tool-string-binary', name: 'inspect', input: JSON.stringify({ screenshotBase64: shortBase64, ordinary: 'visible text', }), }], }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set([ getToolVisibilityId( 'assistant-string-binary', 'tool-string-binary', 0, ), ]), }); expect(markdown).not.toContain(shortBase64); expect(markdown).toContain('[Binary data omitted]'); expect(markdown).toContain('visible text'); }); it.each([ ['plain labelled input', 'screenshotBase64: U0VDUkVU'], ['partial JSON input', '{"screenshotBase64":"U0VDUkVU"'], ['folded partial JSON input', '{"screenshotBase64":"U0VD\\nUkVU"'], ])('redacts short Base64 from %s', (_label, input) => { const markdown = formatVisibleChatTranscript([{ id: 'assistant-partial-binary', role: 'assistant', content: [{ type: 'tool_use', id: 'tool-partial-binary', name: 'inspect', input, }], }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set([ getToolVisibilityId( 'assistant-partial-binary', 'tool-partial-binary', 0, ), ]), }); expect(markdown).not.toContain('U0VDUkVU'); expect(markdown).toContain('[Binary data omitted]'); }); it('redacts single-segment absolute paths in structured tool details', () => { const markdown = formatVisibleChatTranscript([{ id: 'assistant-structured-paths', role: 'assistant', content: [{ type: 'tool_use', id: 'tool-structured-paths', name: 'inspect', input: { dotEnv: '/.env', extensionlessPath: '/secret', }, }], }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set([ getToolVisibilityId( 'assistant-structured-paths', 'tool-structured-paths', 0, ), ]), }); expect(markdown).toContain('[Local path: .env]'); expect(markdown).toContain('[Local path: secret]'); expect(markdown).not.toMatch(/"\/(?:\.env|secret)"/); }); it('keeps every unnamed binary placeholder and deduplicates only a matching named attachment', () => { const markdown = formatVisibleChatTranscript([{ id: 'user-binary', role: 'user', content: [ { type: 'image', mimeType: 'image/png', data: 'FIRST' }, { type: 'image', mimeType: 'image/png', data: 'SECOND' }, { type: 'image', mimeType: 'image/png', data: 'THIRD', fileName: '/tmp/diagram.png' }, ], _attachedFiles: [{ fileName: 'diagram.png', mimeType: 'image/png', fileSize: 10, preview: null, }], }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown.match(/\[Attached image\/png: attachment\]/g)).toHaveLength(2); expect(markdown.match(/\[Attached image\/png: diagram\.png\]/g)).toHaveLength(1); }); it('sanitizes untrusted attachment names and MIME metadata before formatting placeholders', () => { const encodedSecret = 'A'.repeat(80); const shortDataUrlSecret = 'SHORT_SECRET_PAYLOAD_12345'; const fileUrlToken = 'TOP_SECRET_TOKEN'; const markdown = formatVisibleChatTranscript([{ id: 'user-attachment-metadata', role: 'user', content: [{ type: 'image', mimeType: `data:image/png;base64,${encodedSecret}`, data: 'BINARY', fileName: encodedSecret, }], _attachedFiles: [ { fileName: encodedSecret, mimeType: 'image/png', fileSize: 10, preview: null, }, { fileName: 'safe.png', mimeType: `data:image/png;base64,${encodedSecret}`, fileSize: 10, preview: null, }, { fileName: `data:image/png;base64,${shortDataUrlSecret}`, mimeType: 'image/png', fileSize: 10, preview: null, }, { fileName: 'http://127.0.0.1:43123/api/files?path=C%3A%5CUsers%5CAlice%5Cprivate%5Csecret.png', mimeType: 'image/png', fileSize: 10, preview: null, }, { fileName: `file://server/share/private.txt?token=${fileUrlToken}`, mimeType: 'image/png', fileSize: 10, preview: null, }, ], }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set(), }); expect(markdown).toContain('[Attached image/png: [Binary data omitted]]'); expect(markdown).toContain('[Attached application/octet-stream: safe.png]'); expect(markdown).not.toContain(encodedSecret); expect(markdown).not.toContain(shortDataUrlSecret); expect(markdown).not.toContain(fileUrlToken); expect(markdown).not.toMatch(/127\.0\.0\.1|%5CUsers|file:\/\/server/i); expect(markdown).not.toMatch(/data:image|base64/i); }); it('uses stable fallbacks for empty message and tool IDs', () => { expect(getToolVisibilityId('', '', 3)).toBe('message:tool:3'); const markdown = formatVisibleChatTranscript([{ role: 'assistant', content: [{ type: 'tool_use', id: '', name: 'read', input: { file: 'README.md' }, }], }], { showThinking: false, showTimestamps: false, expandedToolIds: new Set([ getToolVisibilityId('assistant-0', undefined, 0), ]), }); expect(markdown).toContain('README.md'); }); });