ced900a57d
Assistant messages that link a file the agent wrote — [report](/home/user/report.md), file://…, ~/…, C:\… — rendered as dead anchors: file:// is blocked in the renderer, Streamdown's URL hardening turns file:/~/ hrefs into "[blocked]" spans, and on a remote gateway the path isn't on the viewer's disk at all. Issue #82140 proposed exposing the Desktop connection mode to skills/MCP/plugins so EXTENSIONS could emit different output per viewer; this fixes the symptom at the right layer instead — the viewer surface resolves paths at VIEW time, so extension output stays surface-agnostic and the same transcript works from every machine that opens it. - markdown-preprocess: routeFileLinksToPreview() rewrites filesystem-path links in prose to the renderer's existing hash-href doors — #preview/… (PreviewAttachment) for documents, #media:… for audio/video/image extensions. These pass URL hardening by design and resolve through normalizeOrLocalPreviewTarget / resolveMedia*Src: local connections read the file directly, remote connections fetch over the authenticated /api/fs bridge. Image syntax, fences, inline code, anchors, relative and http(s) links untouched. - markdown-text: MarkdownLink routes any filesystem href that still reaches it (bypassing preprocess) to PreviewAttachment/MediaAttachment instead of a bare dead <a>. - media.ts: export isFileMediaPath. Live E2E (built app, CDP-driven, fixture session with links to a real gateway-side file): - BEFORE: [report.md] = dead <a href="/home/…"> (click: nothing), [notes](file://…) = "notes [blocked]" span, 0 preview affordances. - AFTER: both render as attachment rows; Open preview shows the file's content in the preview pane; zero blocked spans; screenshots verified. Closes #82140. Supersedes PR #82187 (connection-mode API): with view-time resolution the extension layer no longer needs to know where the viewer sits.