From 8b3ccd53ea66214f77c187ed12f696ab2d4009db Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Tue, 29 Sep 2026 11:02:06 -0700 Subject: [PATCH 1/2] improvement(chat): show live search sources by default --- .../components/agent-group/tool-activity-group.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.tsx index 1baf4c81d87..6e5b6880956 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.tsx @@ -220,7 +220,7 @@ export function ToolActivityGroup({ autoScrollActivity = true, isLive = false, }: ToolActivityGroupProps) { - const [expanded, setExpanded] = useState(false) + const [manualExpanded, setManualExpanded] = useState(null) const tools = withoutRetriedSearchFailures(calls) const statusTool = getActivityStatusTool(tools) if (!statusTool) return null @@ -236,6 +236,7 @@ export function ToolActivityGroup({ const attentionKey = getActivityAttentionKey(tools) const entries = tools.map((tool) => ({ tool, sources: getSearchActivitySources(tool) })) const hasSearchDetails = entries.some(({ sources }) => sources !== undefined) + const expanded = manualExpanded ?? (isLive && hasSearchDetails) return ( 1 ? groupedActivity?.title : undefined} collapsible={tools.length > 1 || hasSearchDetails} expanded={expanded} - onToggle={() => setExpanded(!expanded)} + onToggle={() => setManualExpanded(!expanded)} isStreaming={working && autoScrollActivity} unbounded={entries.some(({ sources }) => (sources?.length ?? 0) > 0)} > From 8688354ad4c84bd6ae2668857eb471799efb47b6 Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Tue, 29 Sep 2026 11:11:22 -0700 Subject: [PATCH 2/2] test(chat): cover live search disclosure transitions --- .../agent-group/tool-activity-group.test.tsx | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.test.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.test.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.test.tsx new file mode 100644 index 00000000000..80fb8472a62 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.test.tsx @@ -0,0 +1,96 @@ +/** + * @vitest-environment jsdom + */ +import { act } from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import { ActivityStatus } from '@/components/ui/activity-status' +import { ToolActivityGroup } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group' +import type { ToolCallItemProps } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item' +import type { ToolCallData } from '@/app/workspace/[workspaceId]/home/types' + +const executingSearch: ToolCallData = { + id: 'search-1', + toolName: 'search_workspace', + displayTitle: 'Searching workspace', + status: 'executing', +} + +const completedSearch: ToolCallData = { + ...executingSearch, + status: 'success', + result: { + success: true, + output: { + success: true, + data: { + results: [ + { + citationId: 'document:guide', + citationUrl: 'https://example.test/guide', + documentName: 'Guide', + }, + ], + }, + }, + }, +} + +function TestToolCall({ displayTitle, renderStatus }: ToolCallItemProps) { + const status = { label: displayTitle, activeLabel: displayTitle, isActive: false, icon: null } + return renderStatus ? renderStatus(status) : +} + +describe('ToolActivityGroup search disclosure', () => { + let container: HTMLDivElement + let root: Root + + beforeEach(() => { + container = document.createElement('div') + document.body.append(container) + root = createRoot(container) + }) + + afterEach(() => { + act(() => root.unmount()) + container.remove() + }) + + function render(tool: ToolCallData, isLive: boolean) { + act(() => + root.render( + + ) + ) + } + + function disclosure() { + const button = container.querySelector('[role="button"][aria-expanded]') + if (!button) throw new Error('Expected a search activity disclosure') + return button + } + + it('opens when live results arrive, closes for the answer, and respects manual choices', () => { + render(executingSearch, true) + expect(container.querySelector('[role="button"][aria-expanded]')).toBeNull() + + render(completedSearch, true) + expect(disclosure().getAttribute('aria-expanded')).toBe('true') + expect(container.querySelector('a[href="https://example.test/guide"]')).not.toBeNull() + + render(completedSearch, false) + expect(disclosure().getAttribute('aria-expanded')).toBe('false') + + act(() => disclosure().click()) + expect(disclosure().getAttribute('aria-expanded')).toBe('true') + render(completedSearch, false) + expect(disclosure().getAttribute('aria-expanded')).toBe('true') + render(completedSearch, true) + expect(disclosure().getAttribute('aria-expanded')).toBe('true') + + act(() => disclosure().click()) + render(completedSearch, false) + render(completedSearch, true) + expect(disclosure().getAttribute('aria-expanded')).toBe('false') + }) +})