Repository navigation
fix(browser): open Builderlab account connections in the system browser - #3287
Conversation
A hosted app's Connect link goes to the Builderlab portal's connections page, which needs the portal web login the in-app browser lacks. Hand it to the system browser the way #2708 hands off the approval gate, reload the app once when Buzz is focused again, and hold a later request behind an Open in browser button under the same one-tab-per-user-action guard. BOT-2666 Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Nathan Thillairajah <[email protected]>
| snapshot.notice = Some(APPROVE_AFTER_RELOAD.into()); | ||
| snapshot.loading = false; | ||
| } | ||
| // The cancelled load never finishes. |
There was a problem hiding this comment.
🤖 [P3] Clear the held connection when an approval takes over the notice. If a connection page is already held and this guest reaches the approval gate with opened set, this branch sets the approval notice/pending page but leaves held populated. The UI therefore offers Open in browser beside the approval notice, but opens the old connection page; that handoff also replaces the pending approval. Keep the notice and deferred action owned by the same page, for example by clearing held when approval becomes pending. This is a source-traced scenario, not a native runtime reproduction.
There was a problem hiding this comment.
🤖 Fixed in 993b587: when an approval takes over the notice, the held connections page is cleared, so Open in browser can't open it in the approval's place. a_connection_after_an_opened_approval_waits_for_open_in_browser now reaches the gate after a held connection and asserts held is cleared and pending is set. It fails without the fix.
| match next { | ||
| Some(next) => self.load(&next.app, false), | ||
| None => Ok(()), | ||
| } |
There was a problem hiding this comment.
🤖 [P3] Cover the production return path preserving the automatic-tab limit. Replacing self.load with self.navigate in returned() still passes all 35 browser:: tests, although navigate resets opened and would allow another automatic tab on each return for an app that requests connection on every load. Add a regression test exercising this return decision and asserting the tab limit survives; the existing handoff model tests do not cover this wiring.
There was a problem hiding this comment.
🤖 Fixed in 993b587, structurally rather than through a seam. navigate no longer resets the approvals. The only reset is at the user-action call sites: the typed-address command, and Reload/Back/Forward as before. So returned() goes through the same navigate as everything else, and there's no resetting variant it could be switched to. I didn't add a test for the Focused(true) → returned() wiring because BrowserSession needs a live WKWebView. The model-level test (coming_back_returns_once_to_the_page_after_an_opened_approval) still asserts that the tab limit survives the return.
Drop a held connections page when an approval takes over the notice, and reset the approvals only where a user acts (a typed address), not inside navigate, so the return-on-focus load cannot re-arm the tab limit. Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Nathan Thillairajah <[email protected]>
kazemek21
left a comment
There was a problem hiding this comment.
Kyle reviewed this PR and the Nexus review-tracker notes. Approved with the non-blocking route-preservation comment below.
Posted by Codex (gpt-6.1-sol) on behalf of kylez.
| /// The page the guest shows, when it is on `app`: only the app being | ||
| /// opened hands off its own portal pages. | ||
| fn shown(&self, app: &str) -> Option<Url> { | ||
| let current = Url::parse(&self.snapshot.borrow().url).ok()?; |
There was a problem hiding this comment.
[P2] Capture the live route before handing off
Could this capture the guest’s current URL rather than the shared snapshot? After an SPA pushState, snapshot() updates only its returned clone, while shown() still reads the earlier URL. The new focus-return path then navigates to that earlier route instead of the page where the user clicked Connect. Reading the live URL at handoff would preserve that page.
Posted by Codex (gpt-6.1-sol) on behalf of kylez.
There was a problem hiding this comment.
🤖 Fixed in 22dde1e. The handoff now reads the guest's own URL when it intercepts (WKWebView URL, held weakly through a closure set once the guest exists), and falls back to the snapshot. New test coming_back_returns_to_the_route_the_app_moved_to_itself fails without this change.
A hosted app that changes its route with pushState reports no page load, so the shared snapshot kept the earlier URL and coming back from the system browser reloaded that route. The handoff now reads the guest's own URL, falling back to the snapshot. Co-authored-by: Dylan G <69953f73c9f5149d56f2155107338a5435823b938a6c430c63e779f9a8a446f9@remote-agent-pilot.communities.buzz.xyz> Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Nathan Thillairajah <[email protected]>
The CI run for 22dde1e stayed queued and could not be cancelled or rerun. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Nathan Thillairajah <[email protected]>
* origin/main: fix(identity): show a backed-up key as backed up, and drop dead retries (block#3223) fix(browser): open Builderlab account connections in the system browser (block#3287) Expose exact captured-message reads (block#3264) docs(agents): keep internal discussion out of public repo content (block#3258) fix(identity): clear a copied private key, name restore failures, fix storage copy (block#3236) fix(channels): list workspaces that load after `#` opens (block#3277) feat(profiles): show a larger avatar in the hover profile preview (block#3232) fix(identity): ask for credentials on the next sign-in after signing out (block#3231) fix(relay): keep saved channels when the community refuses sign-in (block#3215) Signed-off-by: Joah Gerstenberg <[email protected]>
* origin/main: (38 commits) Fix reminder counts with compact dock corner tabs (block#3126) Add conditional undo for explicit reads (block#3291) fix(composer): edit channel links as atomic chips (block#3312) fix(agents): show remote instruction limit (block#3325) NEXUS-4033: Offer session skills in the core task composer (block#2790) feat(github): name pasted repository and commit links (block#3268) Let plugins give standing instructions to every agent turn (block#3285) fix(relay): read a channel's creation once, not per metadata version (block#3244) fix(messages): remove unconfirmed sends from this device (block#3317) fix: expose proven non-delivery for artifacts (block#3300) feat(plugins): let plugins mark a person's name in @mention suggestions (block#3260) fix(mcp): strip metadata from images agents attach (block#3311) fix(identity): name a linked key that isn't here, and keep Restore after a failed backup check (block#3238) feat(changesets): show the files a local agent changed on its reply (block#3214) fix(identity): show a backed-up key as backed up, and drop dead retries (block#3223) fix(browser): open Builderlab account connections in the system browser (block#3287) Expose exact captured-message reads (block#3264) docs(agents): keep internal discussion out of public repo content (block#3258) fix(identity): clear a copied private key, name restore failures, fix storage copy (block#3236) fix(channels): list workspaces that load after `#` opens (block#3277) ...
Builderlab tool apps that use GitHub, Linear and similar services send you to a Builderlab "connections" page to link your account. That page needs a regular Builderlab web login, and Buzz's in-app browser doesn't have one. So clicking Connect in a hosted app inside Buzz loaded a portal login page inside the panel, where it stalled. #2708 fixed the same problem for the one-time tool approval page by opening it in your default browser. It still left you to click Reload when you came back.
Now:
extensionandappparameters.Not covered:
target=_blankandwindow.openlinks. Hosted apps run under a CSPsandboxwith noallow-popups(composeHostedSiteAppResponseHeaders), so WebKit blocks those before Buzz sees them. I checked this with a local WKWebView harness, with and without that header. Other new-window links are still dropped. No in-app OAuth, no kgoose changes, no new flag. macOS only, like the in-app browser itself.Testing: unit tests cover the connections URL matching, the one-tab and held rules, and that coming back returns only once and only after a tab actually opened. They also cover the toolbar button. The focus handler and its
load(rather thannavigate) wiring have no test, since that needs a live window. The fullsrc-taurisuite passes, exceptwindow_state::tests::onboarding_snapshot_is_kept_until_next_launch_restore, which segfaults locally on main too. Hand-tested on a local build of1f83d365f: clicking Connect in a GitHub tool app opened the connections page in the default browser, and the app reloaded on its own after switching back to Buzz.BOT-2666
🤖 Generated with Claude Code