Skip to content

Complete browser tasks with site tools, page controls and user takeover - #4468

Merged
reachjalil merged 19 commits into
devfrom
feature/browser-task-experience
Sep 9, 2026
Merged

reachjalil merged 19 commits into
devfrom
feature/browser-task-experience

Conversation

@reachjalil

@reachjalil reachjalil commented Sep 5, 2026 •

Copy link
Copy Markdown
Member

A browser task keeps its conversation-owned, signed-in tab across website tools, page controls and visual input. Navigation consent, reading access, action approval and result sharing remain separate. Take over pauses automation until the user resumes; dispatch is not a claim that the task succeeded.

Current candidate

Head: fba1b02aa74f159521f8fc31b72a694844a645e6.
Base: dev@314adea7957c79abae165e9ccb3cf6bde72be08d.

Reconciled onto current dev, preserving explicit suspension/reload, task protection, first-document background emulation, retained-tab limits, cleanup, conversation deletion, login sync, artifact navigation, and managed execution policy. All 19 feature commits have verified signatures.

Review and runtime corrections

  • S9U-NPG: blocked main-window links now enter the same task-host navigation-consent path, capturing the originating conversation before asynchronous work. They no longer load directly into the signed-in browser. Positive and negative native assertions verify zero destination contact before approval, denial cleanup and ownership isolation.
  • Fixed a reproduced Electron SIGSEGV during background task opening by keeping an uninitialized blank consent tab detached until its approved navigation completes. Existing pages continue to use the hidden background host.
  • The discovery fault fixture now distinguishes a canceled HTTP response from a released continuation; both must finish without invoking a website callback after takeover.
  • Preserved current-dev lifecycle protection through consent and first-document emulation, bounded suspension waits, and canonical URLs in returned handles.

Takeover input correction

Addresses HDK-ZNW: webpage key and mouse events no longer enable manual navigation after takeover. Queued CDP/Electron input is not evidence of a human navigation decision, even when the browser marks it trusted.

Only explicit navigation through the app's address bar, Back, Forward, or Reload can enable manual browsing while paused. Those IPC handlers validate the main-window sender and main frame before changing navigation authority. Page/child-frame messages cannot grant it. Resume restores fresh task navigation consent; managed policy remains enforced throughout.

For a sign-in that needs navigation or redirects after takeover, use the address bar or Reload first. Same-page interaction remains available; clicking in the webpage alone does not lift the navigation guard.

Extended the existing native takeover regression for queued key/mouse input, forged toolbar messages, zero destination requests, valid toolbar navigation, and fresh consent on resume. Extended the existing browser journey with injected input, a blocked redirect witness, and explicit address-bar recovery. No new test files or exemptions.

Navigation security boundary

  • Task open/navigation requires approval before contacting a new exact origin; a first-open tab stays blank while approval mounts.
  • The native request hook holds task-controlled main-frame redirects until destination approval, then rechecks policy, ownership and cancellation.
  • Grants belong to one tab and conversation, separate from reading/actions; takeover, cancellation and closure revoke them. Approved localhost previews remain supported.
  • Existing related tabs are guarded when adopted. Popups inherit guards, not grants; parent observations preserve their pending approvals.

This is main-frame navigation consent, not a complete egress or DNS-rebinding sandbox. Frames, images, scripts, fetches and other subresources remain governed by the existing managed request policy. The browser retains its persistent signed-in profile.

Safety boundaries retained

The existing server-owned execution policy remains authoritative, including exact origins, intersected restrictions and upload blocking. No new Den API fields, migration or older host-only policy layer; #4481 remains unnecessary. No accounting branch dependency.

Task tools cannot read external profiles or enroll login-sync sites. Users sign in during takeover or use separately approved login sync. Cancellation precedes website discovery; callbacks require approval and bounded results stay local until Share result. Observations expire and are consumed before input. Popups preserve ownership, partition and sandboxing; cross-origin tools require frame delegation verified outside page-controlled globals.

Verification — Passed (selected browser journey)

Passed on this candidate:

  • node --test apps/desktop/electron/browser-panel.test.mjs: 45 passed, zero failed/skipped, exit 0.
  • pnpm --filter @openwork/desktop typecheck:electron: exit 0.
  • pnpm evals:check-browser: 751 callbacks checked; no captured variables, raw code strings or browser type errors, exit 0.
  • git diff --check: exit 0.
  • Exact-head pnpm evals:e2e webmcp-browser-agent: 1 passed, 0 failed, 0 skipped, exit 0. Printed placement: daytona (daytona CLI authenticated). Both Electron and Den were cold-booted from fba1b02aa74f159521f8fc31b72a694844a645e6 and disposed after the run.
  • Published the saved run 2026-09-09T00-10-21-539Z-a-conversation-signs-in-uses-site-tools-and-page-controls-with-consent-isolation through the existing PR evidence publisher. See the current test-evidence comment for the assertions and trace.

Repaired failures: missing standalone eval dependencies, the discovery-resumption assumption, the reproduced background native crash, and lifecycle integration mismatches during the latest rebase. The final selected run has no failures or skips. Earlier red receipts remain historical, not current verdicts.

Review gates: both reported findings have code corrections and resolved response threads. Required build/core checks, SDK generation and CodeQL passed on this head at the latest check. Fresh Warden review is still in progress; no approval or merge is claimed. The separate viewport, transcript-link and full suspension journey suites were not rerun in this focused pass; their old receipts below remain historical.

Reproduce:

pnpm --dir evals install --frozen-lockfile
OPENWORK_EVAL_REF=fba1b02aa74f159521f8fc31b72a694844a645e6 OPENWORK_EVAL_DAYTONA_REF=fba1b02aa74f159521f8fc31b72a694844a645e6 pnpm evals:e2e webmcp-browser-agent

Historical runtime evidence

On 590b05592f5c1da3429eea9f90a956b2b7c77cb6, three journeys passed five tests with zero failures/skips in Daytona. These are not current-head proof:

No merge, release or deployment. External-browser automation, declarative WebMCP, closed-shadow DOM references, file transfer and restoration of live handles after restart remain unsupported. Controlled fixtures do not establish open-ended task-planning quality on arbitrary sites or providers.

@mintlify

mintlify Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
differentai 🟢 Ready View Preview Sep 5, 2026, 12:19 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

@vercel

vercel Bot commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
openwork-app Ready Ready Preview Sep 9, 2026 12:11am UTC
openwork-den Ready Ready Preview Sep 9, 2026 12:11am UTC
openwork-diagnostics Ready Ready Preview Sep 9, 2026 12:11am UTC
openwork-landing Ready Ready Preview, v0 Sep 9, 2026 12:11am UTC

Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread evals/worlds/browser-webmcp.ts Fixed
Comment thread apps/app/src/react-app/domains/browser-logins/use-browser-login-import.ts Outdated
Comment thread apps/desktop/electron/browser-logins.mjs Outdated
Comment thread apps/desktop/electron/browser-logins.test.mjs Outdated
Comment thread evals/specs/browser-login-import.e2e.test.ts Outdated
Comment thread evals/specs/webmcp-browser-agent.e2e.test.ts Outdated
Comment thread evals/specs/webmcp-browser-agent.e2e.test.ts Outdated
Comment thread evals/specs/webmcp-browser-agent.e2e.test.ts Outdated
Comment thread apps/desktop/electron/browser-logins.mjs Outdated
Comment thread evals/worlds/browser-panel.ts Fixed
Comment thread evals/worlds/browser-panel.ts Fixed
Comment thread apps/desktop/electron/browser-panel.mjs Outdated
Comment thread apps/desktop/electron/webmcp-policy.mjs Outdated
Comment thread apps/desktop/electron/browser-panel.mjs Outdated
Comment thread apps/desktop/electron/browser-panel.mjs
Comment thread packages/browser-logins/index.mjs
@reachjalil

reachjalil commented Sep 7, 2026 •

Copy link
Copy Markdown
Member Author

Test evidence — a-transcript-link-replaces-the-selected-artifact-with-its-own-live-sidebar-tab-n — ✅ passed

SHA 590b055 · engine v1

✅ passed · 0 expectations passed · 0 failed · 0 pending

Assertion and trace evidence; no screenshot attachments were recorded.

[world] desktop(daytona) · workspace(/tmp/openwork-builtin-browser-1788745859559) · session("Browser project") · [seed:raw] evalIn() · desktop(daytona) · workspace(/tmp/openwork-builtin-browser-1788745978829) · session("New task") · [seed:raw] evalIn() · session("Unrelated browser research") · [seed:raw] evalIn() ×4 · desktop(daytona) · workspace(/tmp/openwork-builtin-browser-1788746111451) · session("New task") · [seed:raw] evalIn()
[user] see(label=/^Select tab: .*viewport-probe=other-conversation$/, timeoutMs=30000) · text=Linked research · click(text=Linked research) · see(text=http://127.0.0.1:45269/?viewport-probe=transcript-link&source=chat%20link#working-page, timeoutMs=30000) · label=/^browser-handoff.md\b/ · click(label=/^browser-handoff.md\b/) · see(label=Select tab: browser-handoff.md, timeoutMs=30000) · see(text=Keep these notes open while following the research link., timeoutMs=30000) · text=http://127.0.0.1:45269/?viewport-probe=transcript-link&source=chat%20link#working-page · click(text=http://127.0.0.1:45269/?viewport-probe=transcript-link&source=chat%20link#working-page) · see(label=Select tab: 127.0.0.1:45269/?viewport-probe=transcript-link&source=chat link#working-page) · notSee(text=Keep these notes open while following the research link.) · notSee(label=/^Select tab: .*viewport-probe=other-conversation$/) · label=Close side panel · click(label=Close side panel) · label=Open side panel · click(label=Open side panel) · label=Select tab: browser-handoff.md · click(label=Select tab: browser-handoff.md) · see(text=Keep these notes open while following the research link.) ×2 · see(label=/^Select tab: .*viewport-probe=requested-preview$/) · notSee(text=Keep these notes open while following the research link.) · text=Other conversation · click(text=Other conversation) · see(label=/^Select tab: .*viewport-probe=other-conversation$/) · notSee(text=http://127.0.0.1:45269/?viewport-probe=transcript-link&source=chat%20link#working-page)
steps 1 ✅ Clicking the real transcript link selects exactly one owned sidebar page with the complete URL (7.5s) · 2 ✅ Hiding and showing the sidebar keeps the same CDP page and its live input (9.4s) · 3 ✅ A page refresh preserves the selected artifact, but a new browser request selects its working page (6.7s) · 4 ✅ The other conversation keeps its original tab and page (4.3s)
verdict passed · 13 user observations (see ×9, notSee ×4) · 0 probes · steps 4/4


Test run created 2026-09-07T01:49:15.211Z · Source: evals/results/test-runs/2026-09-07T01-49-15-211Z-a-transcript-link-replaces-the-selected-artifact-with-its-own-live-sidebar-tab-n/test-run.json · Repro: pnpm --dir evals artifacts:publish -- --pr 4468 --test-run 2026-09-07T01-49-15-211Z-a-transcript-link-replaces-the-selected-artifact-with-its-own-live-sidebar-tab-n

Historical receipt on f57e309 (superseded)

Test evidence — a-transcript-link-replaces-the-selected-artifact-with-its-own-live-sidebar-tab-n — ✅ passed

SHA f57e309 · engine v1

✅ passed · 0 expectations passed · 0 failed · 0 pending

screenshots not attached (gh < 2.99; run brew upgrade gh)

[world] desktop(daytona) · workspace(/tmp/openwork-builtin-browser-1788744033550) · session("Browser project") · [seed:raw] evalIn() · desktop(daytona) · workspace(/tmp/openwork-builtin-browser-1788744164158) · session("New task") · [seed:raw] evalIn() · session("Unrelated browser research") · [seed:raw] evalIn() ×4 · desktop(daytona) · workspace(/tmp/openwork-builtin-browser-1788744297131) · session("New task") · [seed:raw] evalIn()
[user] see(label=/^Select tab: .*viewport-probe=other-conversation$/, timeoutMs=30000) · text=Linked research · click(text=Linked research) · see(text=http://127.0.0.1:42593/?viewport-probe=transcript-link&source=chat%20link#working-page, timeoutMs=30000) · label=/^browser-handoff.md\b/ · click(label=/^browser-handoff.md\b/) · see(label=Select tab: browser-handoff.md, timeoutMs=30000) · see(text=Keep these notes open while following the research link., timeoutMs=30000) · text=http://127.0.0.1:42593/?viewport-probe=transcript-link&source=chat%20link#working-page · click(text=http://127.0.0.1:42593/?viewport-probe=transcript-link&source=chat%20link#working-page) · see(label=Select tab: 127.0.0.1:42593/?viewport-probe=transcript-link&source=chat link#working-page) · notSee(text=Keep these notes open while following the research link.) · notSee(label=/^Select tab: .*viewport-probe=other-conversation$/) · label=Close side panel · click(label=Close side panel) · label=Open side panel · click(label=Open side panel) · label=Select tab: browser-handoff.md · click(label=Select tab: browser-handoff.md) · see(text=Keep these notes open while following the research link.) ×2 · see(label=/^Select tab: .*viewport-probe=requested-preview$/) · notSee(text=Keep these notes open while following the research link.) · text=Other conversation · click(text=Other conversation) · see(label=/^Select tab: .*viewport-probe=other-conversation$/) · notSee(text=http://127.0.0.1:42593/?viewport-probe=transcript-link&source=chat%20link#working-page)
steps 1 ✅ Clicking the real transcript link selects exactly one owned sidebar page with the complete URL (8.1s) · 2 ✅ Hiding and showing the sidebar keeps the same CDP page and its live input (12.5s) · 3 ✅ A page refresh preserves the selected artifact, but a new browser request selects its working page (8.1s) · 4 ✅ The other conversation keeps its original tab and page (4.9s)
verdict passed · 13 user observations (see ×9, notSee ×4) · 0 probes · steps 4/4


Test run created 2026-09-07T01:18:48.659Z · Source: evals/results/test-runs/2026-09-07T01-18-48-659Z-a-transcript-link-replaces-the-selected-artifact-with-its-own-live-sidebar-tab-n/test-run.json · Repro: pnpm --dir evals artifacts:publish -- --pr 4468 --test-run 2026-09-07T01-18-48-659Z-a-transcript-link-replaces-the-selected-artifact-with-its-own-live-sidebar-tab-n

diff-warden[bot]
diff-warden Bot previously approved these changes Sep 7, 2026

@diff-warden diff-warden Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warden security clearance: clear. No blocking findings (security or desktop↔den sync) in this diff (590b05592f5c1da3429eea9f90a956b2b7c77cb6). Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run

Comment thread apps/desktop/electron/browser-task.mjs
Comment thread apps/desktop/electron/browser-panel.mjs Outdated
Comment thread apps/desktop/electron/browser-panel.mjs Outdated
@reachjalil

Copy link
Copy Markdown
Member Author

Test evidence — a conversation signs in, uses site tools and page controls with consent, isolation and recovery — ✅ passed

SHA fba1b02 · engine v1

✅ passed · 7 expectations passed · 0 failed · 0 pending

screenshots not attached (gh < 2.99; run brew upgrade gh)

[world] desktop(daytona) · workspace(/tmp/openwork-browser-tools-1788912621563) · session("Browser project") · [seed:raw] evalIn() ×2
[probe] browserState · browserFixtureState(GET /state)
[agent] desktopApi(POST /workspace/ws_0b6a2c0c896e/opencode/session/ses_f7c7de906ffe4zWHUDz8ycfTso/prompt_async)
[user] see(label=Allow origin in this tab)
[probe] desktopApi(GET /workspace/ws_0b6a2c0c896e/opencode/session/ses_f7c7de906ffe4zWHUDz8ycfTso/message) · browserState · browserFixtureState(GET /state)
[user] notSee(label=Allow reading this origin) · label=Allow origin in this tab · click(label=Allow origin in this tab) · see(label=Allow reading this origin)
[probe] desktopApi(GET /workspace/ws_0b6a2c0c896e/opencode/session/ses_f7c7de906ffe4zWHUDz8ycfTso/message) · browserFixtureState(GET /state)
[user] label=Allow reading this origin · click(label=Allow reading this origin)
[probe] desktopApi(GET /workspace/ws_0b6a2c0c896e/opencode/session/ses_f7c7de906ffe4zWHUDz8ycfTso/message) · eventually(the discovery turn completes through the engine) · browserFixtureState(GET /state)
[agent] run(browser.open_url)
[probe] browserFixtureState(GET /state) · browserState · browserFixtureState(GET /state)
[user] see(label=Allow origin in this tab)
[probe] browserState · browserFixtureState(GET /state)
[user] label=Deny · click(label=Deny)
[agent] browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned)
[probe] browserState · eventually(the refused open releases its blank tab) · browserState · browserFixtureState(GET /state)
[user] see(label=Allow origin in this tab)
[probe] browserState · browserFixtureState(GET /state)
[user] hover(label=Select tab: New tab) · label=Close tab: New tab · click(label=Close tab: New tab)
[agent] browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned)
[probe] browserState · eventually(the refused open releases its blank tab) · browserState · browserFixtureState(GET /state) ×2
[user] see(label=Allow origin in this tab)
[probe] browserFixtureState(GET /state)
[user] label=Allow origin in this tab · click(label=Allow origin in this tab) · see(text=Allow this conversation to connect to http://localhost:45051 in this tab?) · see(label=Allow origin in this tab)
[probe] browserFixtureState(GET /state)
[user] label=Deny · click(label=Deny)
[agent] browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned)
[probe] browserFixtureState(GET /state)
[user] see(label=Allow origin in this tab)
[probe] browserFixtureState(GET /state)
[user] label=Allow origin in this tab · click(label=Allow origin in this tab)
[agent] browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned)
[probe] browserFixtureState(GET /state)
[user] label=Allow reading this origin · click(label=Allow reading this origin)
[agent] browserTask(site_tools, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcgujv_b)
[user] hover(label=Select tab: Project localhost-approved) · label=Close tab: Project localhost-approved · click(label=Close tab: Project localhost-approved)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcgujv_b) · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] label=Take over · click(label=Take over)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned)
[user] see(text=Signed out) · type(label=Fixture user, "fixture-user") · type(label=Fixture password, )
[probe] browserFixtureState(GET /state)
[user] label=Sign in to project · click(label=Sign in to project) · see(text=Session active)
[probe] browserFixtureState(GET /state) · browserState
[user] label=Resume browser · click(label=Resume browser)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] see(label=Deny)
[probe] browserFixtureState(GET /state)
[user] label=Deny · click(label=Deny)
[agent] browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[agent] desktopApi(POST /workspace/ws_0b6a2c0c896e/opencode/session/ses_f7c7de906ffe4zWHUDz8ycfTso/prompt_async)
[user] see(label=Allow once)
[probe] browserFixtureState(GET /state)
[user] label=Allow once · click(label=Allow once) · see(label=Share result)
[probe] browserFixtureState(GET /state) · desktopApi(GET /workspace/ws_0b6a2c0c896e/opencode/session/ses_f7c7de906ffe4zWHUDz8ycfTso/message)
[user] label=Share result · click(label=Share result)
[probe] desktopApi(GET /workspace/ws_0b6a2c0c896e/opencode/session/ses_f7c7de906ffe4zWHUDz8ycfTso/message) · eventually(the engine verifies the approved save in a new observation) · browserFixtureState(GET /state)
[user] see(label=Allow once)
[probe] browserFixtureState(GET /state)
[user] label=Allow once · click(label=Allow once) · see(label=Share result)
[probe] browserFixtureState(GET /state)
[user] label=Deny · click(label=Deny)
[agent] browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[user] label=Allow once · click(label=Allow once)
[probe] browserFixtureState(GET /state) · eventually(the callback started)
[user] label=Take over · click(label=Take over)
[agent] browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state) · eventually(the callback received cancellation)
[agent] browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned)
[user] label=Resume browser · click(label=Resume browser)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state) ×2
[user] see(label=Allow origin in this tab)
[probe] browserFixtureState(GET /state)
[user] label=Allow origin in this tab · click(label=Allow origin in this tab)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tools, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] see(label=Allow once)
[seed] browserFixtureDiscovery(hold)
[user] label=Allow once · click(label=Allow once)
[probe] browserFixtureState(GET /state) · eventually(registered execution waits in discovery before the callback) · browserState
[user] label=Take over · click(label=Take over)
[agent] browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] see(label=Resume browser)
[seed] browserFixtureDiscovery(release)
[probe] browserFixtureState(GET /state) · eventually(every pending discovery request was canceled or its released continuation finished) · browserState
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] label=Draft title · click(label=Draft title) · press(ArrowLeft) · navigate(/redirect)
[probe] browserFixtureState(GET /state)
[user] type(placeholder=Enter URL..., "http://127.0.0.1:45051/execution-delay", replace) · press(Enter) · see(text=Session active) · see(label=Resume browser)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] label=Resume browser · click(label=Resume browser)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[seed] browserFixtureDiscovery(release)
[user] label=Allow origin in this tab · click(label=Allow origin in this tab)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tools, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] see(label=Allow once)
[probe] browserFixtureState(GET /state)
[user] label=Allow once · click(label=Allow once)
[agent] browserTask(act, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) ×2 · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] eventually(the page visibly completes its DOM save) · browserFixtureState(GET /state) · eventually(the fixture records the DOM save)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] see(label=Allow once)
[probe] browserFixtureState(GET /state)
[user] label=Allow once · click(label=Allow once)
[agent] browserTask(act, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserState · eventually(the owned popup becomes active)
[user] see(label=Allow origin in this tab)
[probe] browserFixtureState(GET /state) ×2
[user] label=Allow origin in this tab · click(label=Allow origin in this tab)
[probe] browserFixtureState(GET /state) · eventually(the popup GET follows its own navigation consent)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttchr4f_p)
[probe] browserFixtureState(GET /state) · eventually(the popup reports its isolation)
[user] hover(label=Select tab: Project popup) · label=Close tab: Project popup · click(label=Close tab: Project popup)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttchr4f_p) · browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tools, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · createSession("Separate browser task") · browserTask(observe, session=ses_f7c7cd764ffeFVO9vtETJf1tEI, tab=tab_mttcggls_1) · browserTask(site_tools, session=ses_f7c7cd764ffeFVO9vtETJf1tEI, tab=tab_mttcggls_1) · browserTask(site_tool, session=ses_f7c7cd764ffeFVO9vtETJf1tEI, tab=tab_mttcggls_1)
[user] label=Allow origin in this tab · click(label=Allow origin in this tab)
[agent] browserTask(open, session=ses_f7c7cd764ffeFVO9vtETJf1tEI, tab=owned)
[user] label=Allow reading this origin · click(label=Allow reading this origin)
[agent] browserTask(site_tools, session=ses_f7c7cd764ffeFVO9vtETJf1tEI, tab=tab_mttchxke_r) · browserTask(site_tool, session=ses_f7c7cd764ffeFVO9vtETJf1tEI, tab=tab_mttchxke_r)
[probe] browserState · browserFixtureState(GET /state) · browserState ×2 · eventually(the background open allocates a review tab without switching conversations) · browserState · browserFixtureState(GET /state)
[user] notSee(label=Allow origin in this tab)
[probe] browserFixtureState(GET /state)
[user] text=Browser project · click(text=Browser project) · see(label=Allow origin in this tab)
[probe] browserFixtureState(GET /state)
[user] label=Allow origin in this tab · click(label=Allow origin in this tab)
[agent] browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned)
[probe] browserFixtureState(GET /state) · browserState ×2
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(act, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(act, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(act, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(act, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] notSee(label=Allow once)
[probe] browserState · browserFixtureState(GET /state)
[user] label=Select tab: Project home · click(label=Select tab: Project home)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tools, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] eventually(the delegated frame registers its tool) · browserFixtureState(GET /state) · eventually(both frames report their isolation)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserState
[user] see(label=Allow once)
[probe] browserFixtureState(GET /state) · browserState
[user] label=Allow once · click(label=Allow once)
[agent] browserTask(act, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state) · eventually(the iframe received one visual click) · browserState
[agent] browserTask(act, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state) · eventually(the hostile frame reports both its explicit opt-out and retained site-keyed document)
[agent] browserTask(site_tools, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] eventually(normal delegated tools remain available beside the hostile frame)
[user] notSee(label=Allow once)
[probe] browserFixtureState(GET /state)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[agent] browserTask(site_tools, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] notSee(label=Allow once)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] label=Take over · click(label=Take over) · see(label=Resume browser, timeoutMs=5000)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[user] label=Resume browser · click(label=Resume browser)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[user] label=Allow origin in this tab · click(label=Allow origin in this tab)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tools, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[seed] den(daytona) · signIn(admin)
[agent] run(route.session) · run(session.open)
[seed] [seed] api GET /v1/desktop-policies · [seed] api PATCH /v1/desktop-policies/dpo_01m21rbgp7ecmtar5bmkk17zg5
[seed:raw] [seed:raw] evalIn()
[probe] desktopApi(GET /managed-policy) · eventually(the desktop receives the real Den execution policy)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) ×3 · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[seed] [seed] api GET /v1/desktop-policies · [seed] api PATCH /v1/desktop-policies/dpo_01m21rbgp7ecmtar5bmkk17zg5
[seed:raw] [seed:raw] evalIn()
[probe] desktopApi(GET /managed-policy) · eventually(the desktop receives the real Den execution policy)
[agent] browserRequest(POST http://127.0.0.1:45051/upload)
[probe] browserFixtureState(GET /state)
[seed] [seed] api GET /v1/desktop-policies · [seed] api PATCH /v1/desktop-policies/dpo_01m21rbgp7ecmtar5bmkk17zg5
[seed:raw] [seed:raw] evalIn()
[probe] desktopApi(GET /managed-policy) · eventually(the desktop receives the real Den execution policy)
[agent] browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned) · browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(site_tool, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserFixtureState(GET /state)
[seed] [seed] api GET /v1/desktop-policies · [seed] api PATCH /v1/desktop-policies/dpo_01m21rbgp7ecmtar5bmkk17zg5
[seed:raw] [seed:raw] evalIn()
[probe] desktopApi(GET /managed-policy) · eventually(the desktop receives the real Den execution policy)
[agent] browserRequest(POST http://127.0.0.1:45051/upload)
[probe] browserFixtureState(GET /state)
[agent] browserTask(navigate, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1) · browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[probe] browserState
[user] hover(label=Select tab: Project fallback) · label=Close tab: Project fallback · click(label=Close tab: Project fallback)
[agent] browserTask(observe, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=tab_mttcggls_1)
[seed:raw] [seed:raw] evalIn()
[agent] browserTask(open, session=ses_f7c7de906ffe4zWHUDz8ycfTso, tab=owned)
[probe] browserState · browserFixtureState(GET /state)
[agent] desktopApi(POST /experimental/ui-control/request)
[probe] browserState · browserFixtureState(GET /state) ×2
steps 1 ✅ The first agent open mounts consent before any GET, then separately requests reading access (8.2s) · 2 ✅ Denied and closed pending opens contact no destination and release their blank tabs (5.4s) · 3 ✅ A redirect needs its destination origin approved, and localhost works after explicit approval (8.1s) · 4 ✅ The person signs in directly during takeover and resumes the very same tab (5.9s) · 5 ✅ Invalid input and denied action consent never invoke a website callback (2.5s) · 6 ✅ Execution approval and result sharing are separate, then the engine observes completion (3.2s) · 7 ✅ A session-reading callback cannot disclose its result when sharing is denied (3.1s) · 8 ✅ Takeover cancels a running callback and rejects new writes until human resume (4.6s) · 9 ✅ Takeover during execution-time discovery prevents a callback from starting after the delay is released (11.4s) · 10 ✅ Navigation invalidates site tools; DOM fallback uses a fresh observation in the signed-in tab (5.4s) · 11 ✅ Image-derived input opens an owned popup without losing sign-in or exposing Node (6.3s) · 12 ✅ Foreign conversations cannot inspect a tab or reuse its tool handles (22.2s) · 13 ✅ Frame delegation follows actual child frames, with image-only controls still usable (8.6s) · 14 ✅ Spoofed page globals and forged policy payloads cannot expose non-origin-keyed callbacks (8.1s) · 15 ✅ Hostile schemas are rejected promptly without blocking observations or Take over (7.6s) · 16 ✅ Real Den origin and upload policy blocks requests before fixture writes, and can be updated (146.0s)
verdict passed · 31 user observations (see ×26, notSee ×5) · 122 probes · steps 16/16

ℹ️ ASSERTION — 1. Explicit in-tab sign-in survives human takeover and resume

The initial GET and filled-but-unsubmitted form recorded zero sign-ins. Only trusted form submission established the fixture session. Paused operations were refused and the same owned tab resumed with Session active.

  • ✅ PASS Explicit in-tab sign-in survives human takeover and resume — The initial GET and filled-but-unsubmitted form recorded zero sign-ins. Only trusted form submission established the fixture session. Paused operations were refused and the same owned tab resumed with Session active.

ℹ️ ASSERTION — 2. No action before approval, no disclosure before sharing, no success before observation

The fixture saw no save at execution review. It saw exactly one authenticated save at result review, while neither the model nor the transcript had its result. Sharing released the receipt; browser_observe then verified Saved 1 before the final answer.

  • ✅ PASS No action before approval, no disclosure before sharing, no success before observation — The fixture saw no save at execution review. It saw exactly one authenticated save at result review, while neither the model nor the transcript had its result. Sharing released the receipt; browser_observe then verified Saved 1 before the final answer.

ℹ️ ASSERTION — 3. Injected webpage input cannot authorize navigation after takeover

CDP mouse and keyboard input followed by navigation to the redirect left the destination request count at zero. Explicit app address-bar navigation restored the signed-in page while agent operations remained paused until Resume browser.

  • ✅ PASS Injected webpage input cannot authorize navigation after takeover — CDP mouse and keyboard input followed by navigation to the redirect left the destination request count at zero. Explicit app address-bar navigation restored the signed-in page while agent operations remained paused until Resume browser.

ℹ️ ASSERTION — 4. Popup ownership, inherited sign-in, and isolation are independently witnessed

A PNG-derived coordinate opened the popup only after approval. Its request carried the existing session without another sign-in. Hostile popup features exposed no Node globals and could not read the controlled cross-origin response.

  • ✅ PASS Popup ownership, inherited sign-in, and isolation are independently witnessed — A PNG-derived coordinate opened the popup only after approval. Its request carried the existing session without another sign-in. Hostile popup features exposed no Node globals and could not read the controlled cross-origin response.

ℹ️ ASSERTION — 5. Frame permissions and visual fallback preserve isolation

Nested fallback iframe markup did not grant the undelegated sibling tools. Both cross-origin frames reported no Node globals. The approved PNG-derived click reached the child control once, changed a fresh image, and could not reuse the consumed observation.

  • ✅ PASS Frame permissions and visual fallback preserve isolation — Nested fallback iframe markup did not grant the undelegated sibling tools. Both cross-origin frames reported no Node globals. The approved PNG-derived click reached the child control once, changed a fresh image, and could not reuse the consumed observation.

ℹ️ ASSERTION — 6. Origin-keying decisions do not trust the website's JavaScript world

Both main-world getters returned eligible values and the page directly supplied forged policy booleans. Native opt-out and retained site-keying still refused registration and execution. A forged modelContext did not enter host discovery; the delegated sibling stayed available and no unsafe callback ran.

  • ✅ PASS Origin-keying decisions do not trust the website's JavaScript world — Both main-world getters returned eligible values and the page directly supplied forged policy booleans. Native opt-out and retained site-keying still refused registration and execution. A forged modelContext did not enter host discovery; the delegated sibling stayed available and no unsafe callback ran.

ℹ️ ASSERTION — 7. Current Den execution policy blocks origin, redirect, upload and deny-all attempts

The desktop reported exact browserOrigins and blockBrowserUploads from real administrator PATCHes. Blocked requests produced no fixture writes. The identical upload succeeded only after its restriction was removed. An empty origin list denied all; clearing it restored the same signed-in tab. Closed and disabled handles remained refused.

  • ✅ PASS Current Den execution policy blocks origin, redirect, upload and deny-all attempts — The desktop reported exact browserOrigins and blockBrowserUploads from real administrator PATCHes. Blocked requests produced no fixture writes. The identical upload succeeded only after its restriction was removed. An empty origin list denied all; clearing it restored the same signed-in tab. Closed and disabled handles remained refused.

Test run created 2026-09-09T00:10:21.540Z · Source: evals/results/test-runs/2026-09-09T00-10-21-539Z-a-conversation-signs-in-uses-site-tools-and-page-controls-with-consent-isolation/test-run.json · Repro: pnpm --dir evals artifacts:publish -- --pr 4468 --test-run 2026-09-09T00-10-21-539Z-a-conversation-signs-in-uses-site-tools-and-page-controls-with-consent-isolation

@diff-warden diff-warden Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warden security clearance: clear. No blocking findings (security or desktop↔den sync) in this diff (fba1b02aa74f159521f8fc31b72a694844a645e6). Automated clearance satisfies the required-review gate only — a human still reviews and merges. Analysis run

This branch was successfully deployed

5 active deployments
Preview – openwork-den — fba1b02a Deployed Sep 9, 2026 by vercel[bot]
Preview – openwork-landing — fba1b02a Deployed Sep 9, 2026 by vercel[bot]
Preview – openwork-app — fba1b02a Deployed Sep 9, 2026 by vercel[bot]
staging - packages/docs — fba1b02a Deployed Sep 9, 2026 by mintlify[bot]
Preview – openwork-diagnostics — fba1b02a Deployed Sep 9, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants