ui: copy the displayed text of grouped agentic responses (#27832)

* ui: copy the displayed text of grouped agentic responses

Agentic sessions render as a single entry anchored on the first
assistant turn, whose content is typically just the first tool call,
so the copy button wrote an empty string to the clipboard. Derive the
text sections of the whole session and copy them joined, matching the
visible response. Plain messages keep the previous behavior.

* const
This commit is contained in:
Pascal
2026-08-31 17:48:43 +02:00
committed by GitHub
parent 8e53fcefd2
commit 774ee0e200
4 changed files with 31 additions and 4 deletions
@@ -7,7 +7,12 @@
ChatMessageSystem,
ChatMessageUser
} from '$lib/components/app/chat';
import { REASONING_TAGS, ROUTES, SYSTEM_MESSAGE_PLACEHOLDER } from '$lib/constants';
import {
AGENTIC_TEXT_COPY_SEPARATOR,
REASONING_TAGS,
ROUTES,
SYSTEM_MESSAGE_PLACEHOLDER
} from '$lib/constants';
import { setChatMessageActionsContext, setChatMessageEditContext } from '$lib/contexts';
import { AgenticSectionType, AttachmentType, MessageRole } from '$lib/enums';
import { DatabaseService } from '$lib/services/database.service';
@@ -237,6 +242,24 @@
}
function handleCopy() {
// Agentic sessions render as a single entry anchored on the first assistant
// turn, whose own content is typically just the first tool call. Copy the
// text sections of the whole session so the clipboard matches the visible
// response instead of the anchor turn.
if (message.role === MessageRole.ASSISTANT) {
const sections = deriveAgenticSections(message, toolMessages, [], false);
const text = sections
.filter((section) => section.type === AgenticSectionType.TEXT)
.map((section) => section.content)
.join(AGENTIC_TEXT_COPY_SEPARATOR);
if (text) {
chatActions.copy(message, text);
return;
}
}
chatActions.copy(message);
}
@@ -29,10 +29,10 @@
refreshAllMessages();
},
copy: async (message: DatabaseMessage) => {
copy: async (message: DatabaseMessage, contentOverride?: string) => {
const asPlainText = Boolean(currentConfig.copyTextAttachmentsAsPlainText);
const clipboardContent = formatMessageForClipboard(
message.content,
contentOverride ?? message.content,
message.extra,
asPlainText
);
@@ -20,6 +20,10 @@ export const SEARCH_SUMMARY = {
// wraps mid-paragraph.
export const RESULT_STAT_SEPARATOR = ' - ';
// Separator between the assistant text sections of a grouped agentic
// session when they are joined for the clipboard.
export const AGENTIC_TEXT_COPY_SEPARATOR = '\n\n';
export const DEFAULT_AGENTIC_CONFIG: AgenticConfig = {
enabled: true,
maxTurns: 100
+1 -1
View File
@@ -249,7 +249,7 @@ export interface ChatMessageDeletionInfo {
* refresh + user-action notification), passed to each ChatMessage as a prop.
*/
export interface ChatMessageActions {
copy: (message: DatabaseMessage) => void;
copy: (message: DatabaseMessage, contentOverride?: string) => void;
delete: (message: DatabaseMessage) => void;
navigateToSibling: (siblingId: string) => void;
editWithBranching: (