Repository navigation
feat(desktop,host): agents drive the Mac's Browser tabs over CDP - #4319
Merged
Merged
Conversation
An agent on a task drives that task's Browser tabs on the Mac through a
Mac command of kind `browser`, while "Let agents run commands on this
Mac" and the Browser tab experiment are both on.
- features: the browser command's params (`macBrowserParamsSchema`,
`{ taskId, actions }`), and the CDP action runner moved here from the
service with its snapshot formatter and session types, so the Mac and
the cloud run the same actions with the same results.
- host: browser joins the kinds a read names only with the experiment on;
params that do not read are refused in words; the actions run through a
new node capability, `browser.act`, over a task's tabs spoken to as one
CDP session, each tab through its own view's debugger
(`mac-browser-tabs.ts`); the run is the result's output and its last
screenshot the image.
- desktop: views are kept by task, an agent's open draws a tab on the
task through the "+"'s path without taking the keyboard, a debugger is
attached while an agent drives and let go after, and a screenshot is
caught while hidden; not-run in words with the experiment off, the
window closed, or the task neither shown nor holding a tab.
- panel: the bar says "An agent is using this tab" with a Stop that keeps
agents off the tab until the developer clicks into it.
Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…and the gate - An agent's Browser tab is heard where the side panel is owned, so one opened while the panel is shut is still there when it opens. - A chosen tab an agent closes gives way to the tab viewed before it. - A run whose lists are too long has their addresses and titles cut, and one still too long is answered in words. - A browser run stops within a second of commands on this Mac or the experiment being switched off. Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
…eatures Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com>
Co-authored-by: luke-agent[bot] <339870089+luke-agent[bot]@users.noreply.github.com> # Conflicts: # packages/host/src/compose-mac-commands.test.ts # packages/host/src/compose-mac-commands.ts # packages/panel/src/tasks/use-tasks-tab.ts
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
MD of the "Browser agent tab automation setup" rollout (plan). With this, an agent on a task can drive that task's Browser tabs on the developer's Mac, through a Mac command of kind
browser. It only works while both of these are on: the Mac's "Let agents run commands on this Mac" switch (macCommands, Dean's call on 2026-10-20) and the Browser tab experiment. The service's routing of thebrowsertool to Mac tabs is ME's job and is not in this PR.What changed
packages/featuresmacBrowserParamsSchema/MacBrowserParamsin@luke/features/mac-command-wire:{ taskId: uuid, actions: browserAgentActionsSchema }. ME queues these as the command'sparams. There is also anisMacBrowserCommandguard.git mv) into@luke/features/browser-agent-cdp, along with its snapshot formatter (browser-agent-snapshot.ts) and the session types (@luke/features/browser-cdp:CdpSession,CdpFailure, …). The Mac and the cloud now run the samerunBrowserActions, so the vocabulary and the results match by construction.CDP_FAILURE.DENIEDkind, whose detail is the reason the agent reads. A refusal's words don't get rewritten as "there is no tab …", so the Mac uses it for Stop.BrowserPage.agent?: boolean.browser.opennow carriestaskId. There is a newBrowserOpenedpush payload.packages/hostcompose-mac-commands.ts(now on main's live follow, feat(host): the Mac follows its commands over Neon Realtime and says it listens on a slow timer #3285):browseronly while the Browser tab experiment is on; when the experiment changes, the follow ends and the next one names the kinds that run now;timeoutSeconds, and is cut off within a second oncemacCommands, the sign-in, or the experiment goes off, answering "Stopped: … switched off";settleMacCommandResult, as MC set up.browser.act(node-capabilities.ts).client/gateway-wiring.tsperformdecodes the params and hands them to the node'sbrowserAct. A client without one, such as the web tab, answers unavailable.mac-browser-tabs.ts, the logic:CdpSession, each tab through its own view'swebContents.debugger. A view's debugger reaches only its own page, so the few browser-level commands the runner sends (attach, detach, activate, close) are answered here over the task's tabs.macBrowserTabsOverdecidesnot-run: experiment off, window closed, or a task the window neither shows nor holds a tab on.runMacBrowserCommandbuilds theMacBrowserResult.apps/desktopbrowser-views.tskeeps each view with the task its tab was opened on.agentTabs(panel, taskId):opencreates a tab on the task, gives it a default 1280×800 size until it's placed, and pushesapp:browser-openedto the panel, which draws it through the "+"'s path (use-side-panel.ts,withBrowserChosen) without moving keyboard focus;closepushesapp:browser-closed.Page.captureScreenshotis intercepted and served bycapturePage(…, { stayHidden: true }), resized to the clip's scale and encoded at the runner's quality. This means a hidden or never-placed view can still be captured.browser.stop. Stop detaches the debugger at once and refuses every later agent action on that tab until the page gets focus again, meaning the developer clicked into it.packages/panelbrowser-pane.tsx: whileagentis true, an@luke/uiBannerstrip under the bar says "An agent is using this tab", with a Stop agent button. It's named so it doesn't clash with the bar's own Stop step while a page loads.useAgentBrowserTabs(browser-pane.tsx), called fromuse-tasks-tab.ts, listens for the two new pushes while the experiment is on, whether or not the side panel is drawn. When an agent closes the chosen tab, the tab viewed before it is chosen, as a tab's own close does.The result ME reads
params:macBrowserParamsSchemafrom@luke/features/mac-command-wire.output: aBrowserAgentRun(browserAgentRunSchemafrom@luke/features/browser-agent-actions), the same shape as a cloud run, with two differences:mime/width/heightbut itsimage.datais"";imageis the last screenshot of the run, as JPEG base64.MAX_BROWSER_OUTPUT_CHARS, the text of the earliest snapshot and console steps is dropped first (text: ""ormessages: [], withtruncated: true), so the latest snapshot arrives whole. If that isn't enough, each list's addresses and titles are cut to 200 characters. A run that is still too long is answered as a refusal in words.listandopenreturn them.not-runcomes back as{ refused: "Not run: …" }, in words the agent can act on.Decisions
BROWSER_SCREENSHOTlimits apply (longest side 1280, JPEG, ≤180 KB, qualities 70/50/30), so a Mac screenshot is the same size as a cloud one. MC'smacScreenshotEncoding(1568 px, quality 80) disagrees; it's left as it was and isn't used.tasks.activeTaskId. A task with a tab already open is reachable even while the window shows another task.Privacy
This stays within the line amended in #4257. The address, title, snapshot and screenshot reach only the command's result, which goes to the service. Nothing new goes into a trace, a counted event, a fixture, app state, or the voice window:
Tests
packages/host/src/mac-browser-tabs.test.ts(fake tabs and debuggers):openfollowed by later actions in the new tab;compose-mac-commands.test.ts:browseris advertised only with the experiment on, and the switch gates everything; unreadable params are refused; a good command runs and its result is posted.browser-acts.test.ts: Stop reaches only the panel's own page.browser-pane.test.ts: the strip and its Stop.use-side-panel.test.ts: an agent's tab is chosen on the open plan, only held on another, and goes when closed.Not checked on a Mac
CI runs on Linux only, and I couldn't run Electron against a real page here. These are untested:
webContents.debuggerinput on a real page;capturePagewithstayHiddenon a hidden or never-placedWebContentsView;focusevent (which lifts Stop) fires only on the developer's click and never on CDP input;These belong in the plan's manual Mac pass (step 12).
Made with Luke