Skip to content

Commit 34dd7c4

Browse files
committed
fix banner
1 parent f8e5f9e commit 34dd7c4

4 files changed

Lines changed: 47 additions & 45 deletions

File tree

  • apps
    • desktop/src/main/browser-agent
    • sim/app/workspace/[workspaceId]/home/components
  • packages/browser-protocol/src

apps/desktop/src/main/browser-agent/driver.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@ function recordNotice(notice: string): void {
7676
}
7777

7878
/**
79-
* Takeover state lives here (session-level, not in the page) so the panel's
80-
* takeover strip survives navigations and tab switches. The reason rides
81-
* every page-state push; the panel's Done chip sends `takeover-done`.
79+
* True while browser_request_takeover waits on the user. The Done chip on the
80+
* chat's takeover tool row completes it via the `takeover-done` panel action;
81+
* the state lives here (session-level, not in the page) so it survives
82+
* navigations and tab switches.
8283
*/
83-
let takeoverReason: string | null = null
84+
let takeoverActive = false
8485
let takeoverDone = false
8586

8687
function pageStateFor(contents: WebContents): BrowserPageState {
@@ -90,7 +91,6 @@ function pageStateFor(contents: WebContents): BrowserPageState {
9091
loading: contents.isLoading(),
9192
canGoBack: contents.navigationHistory.canGoBack(),
9293
canGoForward: contents.navigationHistory.canGoForward(),
93-
...(takeoverReason !== null ? { takeoverReason } : {}),
9494
}
9595
}
9696

@@ -449,19 +449,17 @@ async function activeElementState(contents: WebContents): Promise<Record<string,
449449
// ---------------------------------------------------------------------------
450450

451451
/**
452-
* Hands control to the user IN THE PANEL: the page is already natively
453-
* interactive there, so the panel chrome shows a takeover strip (driven by
454-
* `takeoverReason` on page-state pushes) with the Done chip; the tool
455-
* resolves when that chip sends the `takeover-done` panel action. Nothing is
456-
* injected into the page, so the strip never covers page content and
457-
* survives navigations.
452+
* Hands control to the user: the page is already natively interactive in the
453+
* panel, and the chat's takeover tool row shows the reason with a Done chip.
454+
* The tool resolves when that chip sends the `takeover-done` panel action.
455+
* Nothing is injected into the page, so nothing covers page content and the
456+
* pending state survives navigations.
458457
*/
459-
async function runTakeover(reason: string): Promise<unknown> {
458+
async function runTakeover(): Promise<unknown> {
460459
const tab = session.ensureTab()
461460
const contents = tab.view.webContents
462-
takeoverReason = reason
461+
takeoverActive = true
463462
takeoverDone = false
464-
pushPageState(contents)
465463

466464
const startedAt = Date.now()
467465
try {
@@ -478,9 +476,8 @@ async function runTakeover(reason: string): Promise<unknown> {
478476
}
479477
throw new ToolError('Takeover timed out after 12 hours without the user finishing.')
480478
} finally {
481-
takeoverReason = null
479+
takeoverActive = false
482480
takeoverDone = false
483-
if (!contents.isDestroyed()) pushPageState(contents)
484481
}
485482
}
486483

@@ -698,7 +695,10 @@ async function executeToolInner(
698695
}
699696

700697
case 'browser_request_takeover': {
701-
return await runTakeover(requireStr(params, 'reason'))
698+
// The reason renders in the chat's tool row, not here — but require it
699+
// so the model always tells the user why control was handed over.
700+
requireStr(params, 'reason')
701+
return await runTakeover()
702702
}
703703

704704
default: {
@@ -764,10 +764,10 @@ export async function executeTool(
764764

765765
/** Browser-chrome commands from the panel header; fire-and-forget. */
766766
export async function handlePanelAction(action: BrowserPanelAction): Promise<void> {
767-
// The Done chip on the panel's takeover strip: hands control back to the
767+
// The Done chip on the chat's takeover tool row: hands control back to the
768768
// agent. Meaningful only while a takeover is actually waiting.
769769
if (action.action === 'takeover-done') {
770-
if (takeoverReason !== null) takeoverDone = true
770+
if (takeoverActive) takeoverDone = true
771771
return
772772
}
773773
// Navigate bootstraps the session: the user can open the panel manually

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { useMemo } from 'react'
1+
import { useMemo, useState } from 'react'
2+
import { Chip } from '@sim/emcn'
23
import { ShimmerText } from '@/components/ui'
4+
import { isBrowserAgentAvailable, sendBrowserPanelAction } from '@/lib/browser-agent/transport'
35
import {
6+
BrowserRequestTakeover,
47
CallIntegrationTool,
58
Read as ReadTool,
69
WorkspaceFile,
@@ -46,6 +49,9 @@ interface ToolCallItemProps {
4649
* inline next to its display name (e.g. the Gmail logo before "Read Gmail").
4750
* The status-aware rewrite is repeated at this final rendering boundary so
4851
* live, replayed, and directly-constructed rows cannot bypass completed verbs.
52+
* An executing `browser_request_takeover` row carries the Done chip that
53+
* hands control back to Sim — the takeover tool only resolves when it is
54+
* clicked (desktop only; a replayed row in the web app renders as plain text).
4955
*/
5056
export function ToolCallItem({
5157
toolName,
@@ -54,6 +60,7 @@ export function ToolCallItem({
5460
params,
5561
streamingArgs,
5662
}: ToolCallItemProps) {
63+
const [handedBack, setHandedBack] = useState(false)
5764
const readBlock = useMemo(() => {
5865
if (toolName !== ReadTool.id) return undefined
5966
const path = params?.path
@@ -102,6 +109,9 @@ export function ToolCallItem({
102109
const liveTitle = liveWorkspaceFileTitle || displayTitle
103110
const title = getToolStatusDisplayTitle(liveTitle, status)
104111

112+
const showTakeoverAction =
113+
toolName === BrowserRequestTakeover.id && isExecuting && isBrowserAgentAvailable()
114+
105115
const BlockIcon = (readBlock ?? gatewayBlock ?? getBlockByToolName(toolName))?.icon
106116

107117
return (
@@ -122,6 +132,18 @@ export function ToolCallItem({
122132
) : (
123133
<span className='text-[13px] text-[var(--text-secondary)]'>{title}</span>
124134
)}
135+
{showTakeoverAction && (
136+
<Chip
137+
variant='primary'
138+
disabled={handedBack}
139+
onClick={() => {
140+
setHandedBack(true)
141+
sendBrowserPanelAction('takeover-done')
142+
}}
143+
>
144+
Done
145+
</Chip>
146+
)}
125147
</div>
126148
)
127149
}

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-session.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { useCallback, useEffect, useRef, useState } from 'react'
4-
import { Chip, Tooltip } from '@sim/emcn'
4+
import { Tooltip } from '@sim/emcn'
55
import { ArrowLeft, ArrowRight, Cursor, RefreshCw } from '@sim/emcn/icons'
66
import { reportBrowserPanelBounds, sendBrowserPanelAction } from '@/lib/browser-agent/transport'
77
import { useBrowserSessionStore } from '@/stores/browser-session/store'
@@ -215,20 +215,6 @@ export function BrowserSession() {
215215
}}
216216
/>
217217
</div>
218-
{/* Takeover strip: sits in the panel chrome ABOVE the page rect, so it
219-
never covers page content. Done hands control back to Sim. */}
220-
{typeof pageState?.takeoverReason === 'string' && (
221-
<div className='flex items-center gap-2 border-[var(--border)] border-b px-3 py-1.5'>
222-
<p className='min-w-0 flex-1 truncate text-[var(--text-muted)] text-caption'>
223-
{pageState.takeoverReason.trim()
224-
? `Sim is waiting for you — ${pageState.takeoverReason.trim()}`
225-
: 'Sim is waiting for you'}
226-
</p>
227-
<Chip variant='primary' onClick={() => sendBrowserPanelAction('takeover-done')}>
228-
Done
229-
</Chip>
230-
</div>
231-
)}
232218
{/* Host area: the real page is overlaid exactly on this rect. */}
233219
<div ref={hostRef} className='relative flex-1 overflow-hidden bg-[var(--surface-secondary)]'>
234220
{(!pageState || !sessionAlive) && (

packages/browser-protocol/src/index.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,10 @@ export interface BrowserPanelBounds {
6969

7070
/**
7171
* Browser-chrome commands from the panel header (URL bar, back/forward,
72-
* reload) plus `takeover-done`, sent by the panel's takeover strip when the
73-
* user finishes a hand-control-back request. Page interactions need no
74-
* protocol — the user acts on the real embedded page directly.
72+
* reload) plus `takeover-done`, sent by the Done chip on the chat's
73+
* `browser_request_takeover` tool row when the user finishes a
74+
* hand-control-back request. Page interactions need no protocol — the user
75+
* acts on the real embedded page directly.
7576
*/
7677
export interface BrowserPanelAction {
7778
action: 'navigate' | 'reload' | 'back' | 'forward' | 'takeover-done'
@@ -86,13 +87,6 @@ export interface BrowserPageState {
8687
loading: boolean
8788
canGoBack: boolean
8889
canGoForward: boolean
89-
/**
90-
* Set while `browser_request_takeover` is waiting on the user: the model's
91-
* reason for handing over control (e.g. "Log in to your account"). The
92-
* panel renders its takeover strip from this; it is session-level state,
93-
* so it survives page navigations and tab switches.
94-
*/
95-
takeoverReason?: string
9690
}
9791

9892
/**

0 commit comments

Comments
 (0)