From 45361000696fc8e360acc3d8a34f3f6963c00477 Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Fri, 14 Aug 2026 18:22:48 -0700 Subject: [PATCH] Harden chat stream transition handling --- apps/sim/app/workspace/[workspaceId]/home/home.tsx | 5 +++-- .../[workspaceId]/home/hooks/use-chat.test.ts | 14 ++++++++++++-- .../workspace/[workspaceId]/home/hooks/use-chat.ts | 10 +++++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx index eaefecea24..4077c2a8c1 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx @@ -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 diff --git a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.test.ts b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.test.ts index ec0fe69942..6caa5a1637 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.test.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.test.ts @@ -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', () => { diff --git a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts index 2f431e3c35..76ef88190c 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts @@ -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 } /**