Files
fusion/docs/solutions/ui-bugs/chat-streamed-source-link-integrity.md
Fusion Agent e80fef3fe0 FN-040: fix chat source-link integrity and contrast
Harden chat source links across streaming providers while improving new-tab behavior and visual contrast.

- Preserve source-link metadata through streamed delta and event bridges.
- Open chat source links safely in new tabs across desktop and mobile surfaces.
- Improve source-link contrast and add regression coverage plus user-facing guidance.

Files changed:
 .changeset/fn-040-chat-source-links.md             |  7 +++
 docs/dashboard-guide.md                            |  3 +
 .../ui-bugs/chat-streamed-source-link-integrity.md | 53 ++++++++++++++++
 packages/dashboard/app/components/ChatView.css     | 24 ++++++++
 .../app/components/StandardChatSurface.tsx         |  8 ++-
 .../components/__tests__/ChatView.core.test.tsx    | 38 ++++++++++++
 .../components/__tests__/ChatView.mobile.test.tsx  | 70 ++++++++++++++++++++--
 .../__tests__/ChatView.sessions-rooms.test.tsx     | 49 +++++++++++++++
 ...andardChatSurface.nativeStructureEmbed.test.tsx | 52 +++++++++++++++-
 .../__tests__/TaskPlannerChatTab.test.tsx          | 48 +++++++++++++++
 .../engine/src/__tests__/streaming-delta.test.ts   | 36 +++++++++++
 packages/engine/src/execution/streaming-delta.ts   | 17 +++++-
 .../src/__tests__/event-bridge.test.ts             | 30 +++++++++-
 packages/pi-claude-cli/src/event-bridge.ts         | 14 +++--
 .../src/__tests__/event-bridge.test.ts             | 42 +++++++++++++
 .../fusion-plugin-acp-runtime/src/event-bridge.ts  |  8 ++-
 .../src/__tests__/provider.test.ts                 | 34 +++++++++++
 .../src/acp/event-bridge.ts                        |  8 ++-
 .../src/__tests__/event-bridge.test.ts             | 28 +++++++++
 .../src/event-bridge.ts                            | 14 +++--
 .../src/__tests__/provider.test.ts                 | 34 +++++++++++
 .../src/acp/event-bridge.ts                        |  8 ++-
 .../src/__tests__/runtime-adapter.test.ts          | 34 +++++++++++
 .../src/acp/event-bridge.ts                        |  8 ++-
 24 files changed, 642 insertions(+), 25 deletions(-)

Fusion-Task-Id: FN-040

Fusion-Task-Lineage: 364ad678-df3e-4fb2-b19c-29eb44d8a429

Co-authored-by: Fusion <noreply@runfusion.ai>
2026-08-20 03:57:23 +00:00

3.7 KiB

title, date, category, module, problem_type, component, symptoms, root_cause, resolution_type, severity, related_components, tags
title date category module problem_type component symptoms root_cause resolution_type severity related_components tags
Chat streamed source-link integrity 2026-08-19 ui-bugs packages/engine/src/execution/streaming-delta.ts and packages/dashboard/app/components/StandardChatSurface.tsx data_integrity chat_streaming
Streaming Chat source labels changed numeric dotted tokens such as GPT-5.6 into GPT-5. 6
The inserted space also corrupted dotted URL path segments and source destinations
Chat Markdown source links opened in the current tab and had weak/default contrast
overly_broad_stream_boundary_repair code_fix medium
runtime_bridge
markdown_renderer
url_sanitization
chat_persistence
responsive_ui
chat
streaming
source-links
markdown
new-tab
contrast
runtime-bridges

Chat streamed source-link integrity

Problem

Some providers split a response immediately after a numeric period. The shared sentence-boundary repair treated every punctuation-plus-digit boundary as a missing sentence space, so chunks such as GPT-5. + 6 Luna and /gpt-5. + 6-luna were persisted as GPT-5. 6 and /gpt-5. 6-luna. The Chat renderer then produced incomplete destinations, opened ordinary links in the current tab, and inherited a low-contrast browser link color.

Solution

Classify a digit-period-digit boundary before applying the existing sentence repair. This narrow exception covers versions, decimals, IP addresses, and dotted URL path segments while preserving legitimate repairs such as Done. + Next and Phase complete. + 2 more items. The engine normalizer and every runtime bridge apply the rule before their accumulators or callbacks emit deltas, so live SSE, in-flight recovery, final PostgreSQL messages, and resumed history share one corrected value. Text and thinking accumulators remain independent.

The shared standardChatMarkdownComponents anchor preserves ReactMarkdown's sanitized href, adds semantic target="_blank" and rel="noopener noreferrer", and removes an unsafe sanitized anchor shell without bypassing defaultUrlTransform. Native structure references still become preview cards and file-path controls retain their in-app behavior. Chat-scoped CSS uses var(--text) against assistant var(--surface-1), keeps normal and visited links underlined, and uses existing transition and focus tokens; no global anchor rule or render-time content rewrite was added.

Regression matrix

  • Engine normalizer: exact three-link Markdown split after every numeric period, decimals/IPs, sentence-plus-number repair, whitespace idempotence, lowercase/property continuation, cross-block fallback, and independent text/thinking tails.
  • pi-Claude, generic ACP, Droid, Claude ACP, Grok ACP, and OMP ACP bridges: exact source links at the accumulator/callback boundary plus thinking-token coverage where the bridge owns a separate accumulator.
  • Shared Markdown renderer: persisted and streaming links, exact href/target/rel, repeated links, unsafe schemes, native structure references, file paths, force-plain/empty content, and no nested or empty interactive shell.
  • Production hosts: desktop Direct Chat persisted/live bubbles, Chat Room transcript, task-detail Planner Chat history and reattached stream, and narrow/mobile Direct Chat.
  • Verification: targeted runtime/dashboard Vitest suites, changed-only pnpm test, pnpm lint, pnpm typecheck, and pnpm build.

Non-goals

Historical persisted messages are not rewritten. Terminal-only CLI output, Task Activity/AgentLog, Mailbox, Documents, Workflow Results, and other separate Markdown renderers retain their existing contracts unless they independently adopt the shared Chat renderer.