[aidan] fix/browser-early-close: let agent keep browser open when result lives on the page (#88)

This commit is contained in:
Aidan
2026-06-15 19:00:40 -07:00
committed by GitHub
parent 3bf1b0da79
commit de2e70ca8f
5 changed files with 63 additions and 6 deletions
@@ -59,6 +59,7 @@ export interface BrowserCardPosition {
zOrder: number;
/** Agent session that spawned this browser; auto-removed when its owner reaches terminal state. */
spawned_by?: string | null;
keep_open?: boolean;
/** Dashboard this card belongs to; cards render and persist only on their owning dashboard. */
dashboard_id?: string;
}
@@ -663,6 +664,14 @@ const dashboardLayoutSlice = createSlice({
delete state.endingBrowserCards[action.payload];
},
keepBrowserCardOpen(state, action: PayloadAction<string>) {
const card = state.browserCards[action.payload];
if (!card) return;
card.keep_open = true;
// Undo any in-flight ending mark in case a close path raced ahead.
delete state.endingBrowserCards[action.payload];
},
suspendBrowserCard(state, action: PayloadAction<{ browserId: string; dataUrl: string }>) {
if (!state.browserCards[action.payload.browserId]) return;
state.suspendedBrowserCards[action.payload.browserId] = {
@@ -1098,6 +1107,7 @@ export const {
resumeBrowserCard,
markBrowserCardEnding,
cancelBrowserCardEnding,
keepBrowserCardOpen,
pasteBrowserCard,
updateBrowserCardUrl,
addBrowserTab,
+9 -3
View File
@@ -23,7 +23,7 @@ import {
clearTurnLabel,
} from '../state/agentsSlice';
import { streamStart, streamDelta, streamEnd, clearStreamingForSession } from '../state/streamingSlice';
import { addBrowserCardFromBackend, markBrowserCardEnding, setBrowserCardPosition, setGlowingBrowserCards, GRID_GAP } from '../state/dashboardLayoutSlice';
import { addBrowserCardFromBackend, markBrowserCardEnding, keepBrowserCardOpen, setBrowserCardPosition, setGlowingBrowserCards, GRID_GAP } from '../state/dashboardLayoutSlice';
import { upsertOutput } from '../state/outputsSlice';
import { displaySessionName } from '../state/sessionDisplay';
import { getAuthToken } from '../config';
@@ -510,7 +510,7 @@ class WebSocketManager {
) {
const browserCards = store.getState().dashboardLayout.browserCards;
for (const card of Object.values(browserCards)) {
if (card.spawned_by === session_id) {
if (card.spawned_by === session_id && !card.keep_open) {
store.dispatch(markBrowserCardEnding({
browserId: card.browser_id, status: data.status,
}));
@@ -733,7 +733,7 @@ class WebSocketManager {
if (closedStatus === 'completed' || closedStatus === 'error') {
const browserCards = store.getState().dashboardLayout.browserCards;
for (const card of Object.values(browserCards)) {
if (card.spawned_by === session_id) {
if (card.spawned_by === session_id && !card.keep_open) {
store.dispatch(markBrowserCardEnding({
browserId: card.browser_id, status: closedStatus,
}));
@@ -743,6 +743,12 @@ class WebSocketManager {
}
break;
case 'dashboard:browser_card_keep':
if (data.browser_id) {
store.dispatch(keepBrowserCardOpen(data.browser_id));
}
break;
case 'dashboard:browser_card_added':
if (data.browser_card) {
// Tag with origin dashboard so the card renders only on the dashboard