Harden chat stream transition handling

This commit is contained in:
Siddharth Ganesan
2026-08-14 18:22:48 -07:00
parent 63f48c7967
commit 4536100069
3 changed files with 24 additions and 5 deletions
@@ -218,8 +218,9 @@ export function Home({ chatId, userName, userId, tableViewsEnabled }: HomeProps)
activeResourceParamRef.current = activeResourceParam
function handleResourceEvent(resourceId: string, options?: ResourceEventOptions) {
// Agent work makes the resource surface available without replacing an
// existing selection. Explicit user navigation can request activation.
// Agent work surfaces the resource and switches to it as it is created or
// edited; only the browser session stays in the background behind an
// existing selection (see shouldActivateResourceEvent).
if (isResourceCollapsedRef.current) setIsResourceCollapsed(false)
const activeResourceId = activeResourceParamRef.current
@@ -33,17 +33,27 @@ vi.mock('next/navigation', () => ({
}))
describe('shouldActivateResourceEvent', () => {
it('keeps background agent activity from replacing another selected resource', () => {
it('keeps background browser activity from replacing another selected resource', () => {
expect(shouldActivateResourceEvent('file-1', 'browser-session')).toBe(false)
})
it('allows an explicit user action to replace another selected resource', () => {
it('allows an explicit user action to surface the browser over another selection', () => {
expect(
shouldActivateResourceEvent('file-1', 'browser-session', {
activate: true,
})
).toBe(true)
})
it('activates the browser when nothing else is selected', () => {
expect(shouldActivateResourceEvent(null, 'browser-session')).toBe(true)
expect(shouldActivateResourceEvent('browser-session', 'browser-session')).toBe(true)
})
it('activates a non-browser resource even when another resource is selected', () => {
expect(shouldActivateResourceEvent('file-1', 'workflow-1')).toBe(true)
expect(shouldActivateResourceEvent('browser-session', 'terminal-session')).toBe(true)
})
})
describe('shouldQueueOutgoingMessage', () => {
@@ -1195,12 +1195,20 @@ export interface ResourceEventOptions {
export type ResourceEventHandler = (resourceId: string, options?: ResourceEventOptions) => void
/**
* Whether a streamed resource event should activate its tab. Resources switch
* into view as the agent creates or edits them; only the background browser
* session declines to replace an existing selection (it gets an attention
* marker instead), unless the event explicitly requests activation.
*/
export function shouldActivateResourceEvent(
activeResourceId: string | null,
resourceId: string,
options?: ResourceEventOptions
): boolean {
return options?.activate === true || !activeResourceId || activeResourceId === resourceId
if (options?.activate === true) return true
if (resourceId !== BROWSER_SESSION_RESOURCE_ID) return true
return !activeResourceId || activeResourceId === resourceId
}
/**