fix(owned-browser): tag eval-with-url navigations with their owner#3934
Merged
Conversation
The owned browser is one webview shared by every chat and every background pipe. Navigations are owner-tagged so the sidebar ignores ones owned by a chat other than the one on screen — but only the /navigate endpoint read the x-screenpipe-session owner header. The navigate-and-scrape /eval path (a pipe opening a page and reading it in one call) dropped the owner, so its owned-browser:navigate event was emitted with owner=None, which the frontend treats as "the sidebar's own action" and always reveals. Result: a background pipe popped its page into whatever chat you were looking at. Thread the owner through the eval path, mirroring navigate_with_owner: - browser_run_eval reads x-screenpipe-session and calls eval_with_owner - new Browser::eval_with_owner / OwnedWebviewHandle::eval_with_owner (both default to the existing eval), overridden by OwnedBrowser + TauriOwnedHandle - TauriOwnedHandle::eval_inner threads owner into prepare_navigation Also tighten the frontend gate: a tagged navigation with no bound chat (conversationId null, e.g. a fresh unsaved chat) is now treated as foreign, so a background pipe can't pop into an empty chat either. Tests: - screenpipe-connect: eval_with_owner forwards owner to the handle; plain eval stays un-owned - screenpipe-engine: POST /eval with x-screenpipe-session reaches the browser with the owner (real axum router; would fail pre-fix, which routed through eval) - frontend vitest: isForeignNavigation gates a tagged nav when no chat is bound - e2e: zz-owned-browser-background-nav drives the real /eval-with-url path with a foreign owner header and asserts the browser stays hidden Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Problem
When you're in a chat, the browser sidebar sometimes pops open because a background pipe is driving the owned browser. It should only show for the chat that actually drove it.
Root cause
The owned browser is a single webview shared by every chat and every background pipe. Each navigation is tagged with an
owner, and the sidebar ignores navigations owned by a chat other than the one on screen. That gate already worked forPOST /navigate— it reads thex-screenpipe-sessionheader the agent's curl shim injects (pipe:<name>for a pipe).The gap:
POST /evalwith aurl(navigate-and-scrape in one call — a common pipe path) never read that header.TauriOwnedHandle::evalhardcodedprepare_navigation(.., None), so theowned-browser:navigateevent was emitted withowner = None, which the frontend treats as "the sidebar's own restore/reload" and always reveals.Fix
Thread the owner through the eval path, mirroring the existing
navigate_with_ownerchain:browser_run_evalreadsx-screenpipe-sessionand callseval_with_ownerBrowser::eval_with_owner/OwnedWebviewHandle::eval_with_owner(both default to plaineval), overridden byOwnedBrowserandTauriOwnedHandleTauriOwnedHandle::eval_innerthreadsownerintoprepare_navigationDefensive frontend tightening in
isForeignNavigation: a tagged navigation with no bound chat (conversationIdnull — e.g. a brand-new unsaved chat) is now treated as foreign too, so a pipe can't pop into an empty chat.Tests
eval_with_ownerforwards the owner to the handle; plainevalstays un-ownedPOST /evalwithx-screenpipe-sessionreaches the browser with the owner — this is the repro discriminator (pre-fix it routed throughevaland the owner wasNone)isForeignNavigationgates a tagged nav when no chat is boundzz-owned-browser-background-nav): drives the real/eval-with-url path with a foreign owner header and asserts the browser stays hiddenLocal run: connect + engine cargo tests, frontend vitest, and
bindings:checkall green. The wdio GUI e2e runs in CI (not launched locally — there is a live screenpipe on:3030it would conflict with).