Skip to content

Commit d0f8948

Browse files
committed
fix(search): clarify activity result states and labels
1 parent e5f7161 commit d0f8948

5 files changed

Lines changed: 64 additions & 13 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/activity-viewport.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { cn, scrollFadeAttributes, scrollFadeClass, useScrollEdges } from '@sim/
66
interface ActivityViewportProps {
77
children: ReactNode
88
isStreaming: boolean
9-
/** A nested blocking interaction must not be clipped by this ancestor's log viewport. */
9+
/** Keeps nested interactions or independently scrolling detail lists from being clipped. */
1010
unbounded?: boolean
1111
}
1212

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/search-activity-details.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export function getSearchActivitySources(tool: ToolCallData): SourceTagData[] |
2020
tool.status === ToolCallStatus.success &&
2121
tool.result?.success &&
2222
output?.success !== false &&
23+
parseCitationRecord(data?.retrieval)?.status !== 'partial' &&
2324
Array.isArray(data?.results) &&
2425
data.results.length === 0
2526
)
@@ -29,12 +30,13 @@ export function getSearchActivitySources(tool: ToolCallData): SourceTagData[] |
2930

3031
interface SearchActivityDetailsProps {
3132
sources: SourceTagData[]
33+
label: string
3234
}
3335

3436
/** Per-call evidence stays in the shared activity history, never in the live header. */
35-
export function SearchActivityDetails({ sources }: SearchActivityDetailsProps) {
37+
export function SearchActivityDetails({ sources, label }: SearchActivityDetailsProps) {
3638
return sources.length > 0 ? (
37-
<SearchActivityResults sources={sources} />
39+
<SearchActivityResults sources={sources} label={label} />
3840
) : (
3941
<p className='text-[var(--text-muted)] text-caption'>No results</p>
4042
)

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/search-activity-results.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@ import type { SourceTagData } from '@/app/workspace/[workspaceId]/home/component
2222

2323
interface SearchActivityResultsProps {
2424
sources: SourceTagData[]
25+
label: string
2526
}
2627

2728
/**
2829
* Bounded results keep every match available without growing the activity
2930
* transcript. The list shows four and a half 32px rows, so a clipped row signals
3031
* that it scrolls.
3132
*/
32-
export function SearchActivityResults({ sources }: SearchActivityResultsProps) {
33+
export function SearchActivityResults({ sources, label }: SearchActivityResultsProps) {
3334
const scrollRef = useRef<HTMLDivElement>(null)
3435
const edges = useScrollEdges(scrollRef)
3536

@@ -38,7 +39,7 @@ export function SearchActivityResults({ sources }: SearchActivityResultsProps) {
3839
<div
3940
ref={scrollRef}
4041
role='region'
41-
aria-label='Search results'
42+
aria-label={label}
4243
className={cn('max-h-[152px] overflow-y-auto overscroll-contain p-1', scrollFadeClass)}
4344
{...scrollFadeAttributes(edges)}
4445
>

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/search-activity.test.tsx‎

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ describe('search in shared tool activity', () => {
115115
expand()
116116
expect(container.querySelectorAll('a')).toHaveLength(3)
117117
expect(container.querySelector('[role="region"]')?.getAttribute('aria-label')).toBe(
118-
'Search results'
118+
'Search results for step 1: Searched documents'
119119
)
120120
expect(container.innerHTML).not.toContain('in:launch review')
121121
expect(container.innerHTML).not.toContain('raw-provider-query-sentinel')
@@ -176,6 +176,14 @@ describe('search in shared tool activity', () => {
176176
expect(header().getAttribute('aria-expanded')).toBe('true')
177177
expect(container.querySelectorAll('a')).toHaveLength(6)
178178
expect(container.querySelectorAll('[role="region"]')).toHaveLength(2)
179+
expect(
180+
[...container.querySelectorAll('[role="region"]')].map((region) =>
181+
region.getAttribute('aria-label')
182+
)
183+
).toEqual([
184+
'Search results for step 1: Searched documents',
185+
'Search results for step 2: Searched documents',
186+
])
179187
act(() => header().dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })))
180188
expect(header().getAttribute('aria-expanded')).toBe('false')
181189
})
@@ -232,14 +240,49 @@ describe('search in shared tool activity', () => {
232240
expect(container.querySelectorAll('a')).toHaveLength(0)
233241
})
234242

235-
it('shows an empty result only in its history, without a header count', () => {
243+
it.each([undefined, { status: 'complete', timedOutLegs: [] }])(
244+
'shows a complete or legacy empty result only in its history, without a header count',
245+
(retrieval) => {
246+
render([
247+
{
248+
...tool,
249+
status: 'success',
250+
result: { success: true, output: { data: { results: [], retrieval } } },
251+
},
252+
])
253+
expect(headerText()).toBe('Searched documents')
254+
expect(container.textContent).not.toContain('results')
255+
expand()
256+
expect(container.textContent).toContain('No results')
257+
}
258+
)
259+
260+
it('does not claim no results or expose an empty disclosure when retrieval is partial', () => {
236261
render([
237-
{ ...tool, status: 'success', result: { success: true, output: { data: { results: [] } } } },
262+
{
263+
...tool,
264+
status: 'success',
265+
result: {
266+
success: true,
267+
output: {
268+
data: { results: [], retrieval: { status: 'partial', timedOutLegs: ['vector'] } },
269+
},
270+
},
271+
},
238272
])
239273
expect(headerText()).toBe('Searched documents')
240-
expect(container.textContent).not.toContain('results')
274+
expect(header()).toBeNull()
275+
expect(container.textContent).not.toContain('No results')
276+
})
277+
278+
it('preserves available source matches when retrieval is partial', () => {
279+
const search = completedSearch('partial', 'Available matches')
280+
const output = search.result!.output as { data: Record<string, unknown> }
281+
output.data.retrieval = { status: 'partial', timedOutLegs: ['vector'] }
282+
render([search])
241283
expand()
242-
expect(container.textContent).toContain('No results')
284+
expect(container.querySelectorAll('a')).toHaveLength(3)
285+
expect(container.textContent).not.toContain('No results')
243286
})
244287

245288
it.each([

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.tsx‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,17 +240,22 @@ export function ToolActivityGroup({
240240
expanded={expanded}
241241
onToggle={() => setExpanded(!expanded)}
242242
isStreaming={working && autoScrollActivity}
243-
unbounded={hasSearchDetails}
243+
unbounded={entries.some(({ sources }) => (sources?.length ?? 0) > 0)}
244244
>
245245
<div className='flex min-w-0 flex-col gap-1.5 py-0.5'>
246-
{entries.map(({ tool, sources }) => (
246+
{entries.map(({ tool, sources }, index) => (
247247
<Fragment key={tool.id}>
248248
{tools.length === 1 ? null : tool.id === headerTool.id ? (
249249
<ActivityStatus {...status} />
250250
) : (
251251
<ToolCallComponent {...tool} toolCallId={tool.id} />
252252
)}
253-
{sources && <SearchActivityDetails sources={sources} />}
253+
{sources && (
254+
<SearchActivityDetails
255+
sources={sources}
256+
label={`Search results for step ${index + 1}: ${getToolTitle(tool)}`}
257+
/>
258+
)}
254259
</Fragment>
255260
))}
256261
</div>

0 commit comments

Comments
 (0)