From 2debcf421c1a7f2150f2c22462e09e6ee3e93770 Mon Sep 17 00:00:00 2001 From: LofiSu Date: Wed, 4 Feb 2026 16:34:12 +0800 Subject: [PATCH] fix(citations): improve citation link rendering and copy behavior - Use citation.title for display text in CitationLink to ensure correct titles show during streaming (instead of generic "Source" text) - Render all external links as CitationLink badges for consistent styling during streaming output - Add removeAllCitations when copying message content to clipboard - Simplify citations_format prompt for cleaner AI output Co-authored-by: Cursor --- backend/src/agents/lead_agent/prompt.py | 26 ++++++------------- .../ai-elements/inline-citation.tsx | 7 ++++- .../workspace/messages/message-list-item.tsx | 19 +++++++++----- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/backend/src/agents/lead_agent/prompt.py b/backend/src/agents/lead_agent/prompt.py index 2076374..b97d6f5 100644 --- a/backend/src/agents/lead_agent/prompt.py +++ b/backend/src/agents/lead_agent/prompt.py @@ -123,35 +123,25 @@ You have access to skills that provide optimized workflows for specific tasks. E -**FORMAT** - After web_search, ALWAYS include citations in your output: -**For chat responses:** -Your visible response MUST start with citations block, then content with inline links: +After web_search, ALWAYS include citations in your output and MUST start with a `` block in JSONL format: -{{"id": "cite-1", "title": "Page Title", "url": "https://example.com/page", "snippet": "Brief description"}} +{{"id": "cite-1", "title": "Source Title 1", "url": "https://example.com/page1", "snippet": "Brief description of source 1"}} +... -Content with inline links... -**For files (write_file):** -File content MUST start with citations block, then content with inline links: - -{{"id": "cite-1", "title": "Page Title", "url": "https://example.com/page", "snippet": "Brief description"}} - -# Document Title -Content with inline [Source Name](full_url) links... - -**RULES:** -- `` block MUST be FIRST (in both chat response AND file content) -- Write full content naturally, add [Source Name](full_url) at end of sentence/paragraph +**Rules:** +- Write content naturally, add [Source Name](full_url) at end of sentence/paragraph - NEVER use "According to [Source]" format - write content first, then add citation link at end -- Example: "AI agents will transform digital work ([Microsoft](url))" NOT "According to [Microsoft](url), AI agents will..." **Example:** {{"id": "cite-1", "title": "AI Trends 2026", "url": "https://techcrunch.com/ai-trends", "snippet": "Tech industry predictions"}} +{{"id": "cite-2", "title": "OpenAI Research", "url": "https://openai.com/research", "snippet": "Latest AI research developments"}} -The key AI trends for 2026 include enhanced reasoning capabilities, multimodal integration, and improved efficiency [TechCrunch](https://techcrunch.com/ai-trends). +The key AI trends for 2026 include enhanced reasoning capabilities and multimodal integration [TechCrunch](https://techcrunch.com/ai-trends). Recent breakthroughs in language models have also accelerated progress [OpenAI](https://openai.com/research). + - **Clarification First**: ALWAYS clarify unclear/missing/ambiguous requirements BEFORE starting work - never assume or guess - Skill First: Always load the relevant skill before starting **complex** tasks. diff --git a/frontend/src/components/ai-elements/inline-citation.tsx b/frontend/src/components/ai-elements/inline-citation.tsx index be6c651..b9e206e 100644 --- a/frontend/src/components/ai-elements/inline-citation.tsx +++ b/frontend/src/components/ai-elements/inline-citation.tsx @@ -309,6 +309,11 @@ export const CitationLink = ({ children, }: CitationLinkProps) => { const domain = extractDomainFromUrl(href); + + // Priority: citation.title > domain + // When citation has title, use it for consistent display + // This ensures correct title shows even during streaming when children might be generic + const displayText = citation?.title || domain; return ( @@ -324,7 +329,7 @@ export const CitationLink = ({ variant="secondary" className="hover:bg-secondary/80 mx-0.5 cursor-pointer gap-1 rounded-full px-2 py-0.5 text-xs font-normal" > - {children ?? domain} + {displayText} diff --git a/frontend/src/components/workspace/messages/message-list-item.tsx b/frontend/src/components/workspace/messages/message-list-item.tsx index 28927aa..6336e0f 100644 --- a/frontend/src/components/workspace/messages/message-list-item.tsx +++ b/frontend/src/components/workspace/messages/message-list-item.tsx @@ -21,6 +21,7 @@ import { buildCitationMap, isCitationsBlockIncomplete, parseCitations, + removeAllCitations, } from "@/core/citations"; import { extractContentFromMessage, @@ -62,11 +63,11 @@ export function MessageListItem({ >
@@ -76,19 +77,25 @@ export function MessageListItem({ /** * Custom link component that handles citations and external links + * All external links (http/https) are rendered as CitationLink badges + * to ensure consistent styling during streaming */ function MessageLink({ href, children, citationMap, - ...props }: React.AnchorHTMLAttributes & { citationMap: Map; }) { if (!href) return {children}; const citation = citationMap.get(href); - if (citation) { + + // Check if it's an external link (http/https) + const isExternalLink = href.startsWith("http://") || href.startsWith("https://"); + + // All external links use CitationLink for consistent styling during streaming + if (isExternalLink) { return ( {children} @@ -96,13 +103,11 @@ function MessageLink({ ); } + // Internal/anchor links use simple anchor tag return ( {children}