Repository navigation
fix(antigravity): reliable Google sign-in on Windows; app-owned sign-in page opening - #595
Merged
Merged
Conversation
The managed ACP runtime's profile linked config/skills and antigravity-cli/skills to ~/.gemini even when those directories did not exist. On session/new the agent creates them, hits the dangling link or junction, and rejects the session with JSON-RPC -32603, so sign-in verification and every chat session failed for users without ~/.gemini. Link only a target directory that exists, and remove a dangling link at the link path so the agent can create its own directory. Real directories and links that still resolve are never touched. The check runs on every launch, so skills installed later are linked then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every sign-in ran the browser-suppression preflight again. On Windows the packaged helper is Scient.exe, and creating that process is synchronous in Node: under load it blocked the event loop for seconds, outside any timer. When the 5-second timeout did fire, it then waited for the helper's teardown, which runs taskkill and waits for the exit without a bound. Keep a successful verification per helper executable for the server process; failures are checked again on the next sign-in. A timeout now returns at once and lets the teardown finish in the background. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a failed session/new the private profile kept <session>.db-wal and <session>.db-shm: the database and metadata were gone and the session id cleanup needs was never reported. Every verification cleanup now removes SQLite sidecars whose database no longer exists; a live session always has its database, so its files are kept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r cause When Google accepted an Antigravity sign-in but the agent could not start a session, the UI still read "Google sign-in didn't finish". The server message was right; nothing told the client that the sign-in itself had finished. A failed connection operation now carries failedAfterSignIn, set for Antigravity's session-setup failure and for an account Scient could not verify after the provider finished. Providers can also name a starting message; Antigravity says its start can take up to a minute, since the agent unpacks itself first. A failed sign-in now records, in the local server trace only, the failed ACP request's method, JSON-RPC code, message and data.details, bounded to 500 characters, without URL queries and with the user's home shown as ~. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without a ~/.gemini skill directory the agent creates its own, empty one in the private profile on session/new. That real directory then kept the link from ever being made when the user installed skills later. Remove it when it is empty and the target exists; rmdir refuses a directory with content, so nothing the user or agent put there can be lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Antigravity opened Google's page only if its setup card was still mounted when the start command returned. Starting the agent takes up to a minute on Windows; if the card closed or remounted meanwhile, no browser opened and the card later claimed one had. The lifecycle controller now runs every start through one app-level module that opens the page from the start command's response, once per environment, provider instance and operation, whatever is mounted. Only the starting client receives that response, so other windows never open it. A visible setup card shows an opening failure; otherwise a toast offers to open the page. Cards say a page is open only when this client opened it, and say that Antigravity is starting while it does. Codex, Cursor and Scient Agent account sign-ins use the same module instead of opening pages themselves. Antigravity's card also tells a session failure after Google accepted the sign-in from a sign-in that did not finish. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The row's Sign in opened the setup card for every provider but Codex, so users clicked Sign in twice. It now starts sign-in directly when the provider has exactly one method that only needs a page Scient opens (Codex browser, Cursor, Antigravity Google), and opens the card for a choice of methods, a device code, a pairing, configured credentials, or a started flow that turns out to need something else. The row says Starting while the provider starts, with the provider's message on hover, and Setup failed when the sign-in finished but setup did not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… rmdir through a link The once-per-process browser-helper check now keys on the executable and on NODE_OPTIONS and ELECTRON_RUN_AS_NODE from the helper's launch environment, held only as a digest. The empty-directory replacement checks with lstat that the link path is a real directory, so it never acts through a link or junction. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A start request may name an opaque client-generated sign-in request id (8 to 64 letters, digits or dashes). The manager stamps it on every state of the operation it creates, so the starting client can recognize its own sign-in in streamed snapshots even if the start reply never arrives. Older clients and servers ignore the field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ownership of a sign-in page depended on receiving the start command's reply, so a brief disconnect during a slow start lost the opening. The client now records a random sign-in request id before sending the start and sends it along; the server stamps it on the operation. The page opens once when the reply or a streamed provider snapshot shows an operation carrying an id this client holds, waiting on a primary sign-in page. Other clients see the id but do not hold it, so they never open the page. A coordinator mounted once in the root route watches the snapshots of environments where this client holds an id. Held ids live in session storage, so a renderer reload during the start still opens the page, and are released when the page opens, the operation ends, the server rejects the start, or after 15 minutes. A start joined to one already in flight shares its operation and releases its own id. Against a server that does not stamp ids, the reply alone identifies a new operation, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… device Google returns a browser sign-in to 127.0.0.1 on the browser's device. When the environment's server is not on this device, a one-click row sign-in now starts and also opens the setup card, so the field for pasting the return URL is visible. Local environments stay one click. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e diagnostics Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… away Startup ran inside the start request, so a reload or brief disconnect of the asking client interrupted it and published the operation cancelled; a held sign-in request id could not recover that. Once the manager has published the starting operation, the rest of startup now runs in a manager-owned fiber. The request still waits for it and answers as before, but its interruption only detaches the caller. Cancel interrupts the startup, and its failure, the provider's own deadline and manager teardown still end it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trace diagnostic kept bearer tokens, refresh_token values, URL passwords and paths outside the home folder, and those attributes reach any configured trace exporter. The method and JSON-RPC code stay as structured fields; the message and data.details are now reduced: URLs to scheme and host, absolute paths (Windows, UNC, POSIX, file://) to their last segment, and bearer and basic values, JWTs, credential-keyed values and long opaque runs are replaced. The incident's text still reads as the agent's sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…und its teardown The cache key took the first case-insensitive spelling of NODE_OPTIONS and ELECTRON_RUN_AS_NODE, so with node_options before NODE_OPTIONS a change to the spelling the child actually read reused a verification. The digest now covers every spelling and value, so any change checks the helper again. A timed-out preflight's background teardown is now bounded: if interrupting it stalls, one forced kill of the helper (recorded by a marked line as it starts) is tried, then the teardown is left with a debug log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…windows In a browser tab the page opens long after the user's click, Chromium blocks it as a popup, and the opener still recorded it as opened. It now opens without noopener so it can see the block, cuts the opener link afterwards, and records a blocked page as failed. The card and the notification say the browser blocked it and offer to open it from the user's own click. The desktop app's system-browser opening is unchanged. A window opened from another starts with a copy of its session storage, so both held the same request ids and both opened the page. Before an automatic opening, a window now claims the operation in local storage, under a Web Lock where available; only the claiming window opens it. An unknown start failure (a lost reply) also keeps its id for the stream. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d start reply Scient Agent's account sign-in turned a failed start command into a missing operation, which the opener read as a reply from a server that does not stamp request ids and so released the id. The failure is now thrown into the opener unwrapped: a server rejection releases the id, an unknown outcome such as a dropped connection keeps it for the stream. The lifecycle controller used by Codex, Cursor and Antigravity already throws failures this way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…device A browser tab on localhost was treated as running beside its server, but it can be reaching a remote one through an SSH tunnel, where Google's 127.0.0.1 return never arrives. Only the desktop app's own primary and local backends now count; anything else starts sign-in and opens the card with the return-URL field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing and locality Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… only The redaction blacklist still let credentials through: a second cookie, escaped JSON passwords, nested credential objects. No text of the agent's own now reaches the trace. A failed request records its method, JSON-RPC code, the message only when it is a standard JSON-RPC message, and a kind classified from data.details: the workspace-folder incident becomes workspace_folder_create_failed with the folder's last segment when it is a profile folder and the OS error as an allowlisted code; anything else is unrecognized, with its length. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… referrer Without Web Locks two windows could both read an absent opening record and both open the page, and a storage failure granted permission. An automatic opening now requires the record's check and write to run inside an exclusive Web Lock; without Web Locks or storage, or when the lock is held, the window does not open the page, and the card or a notification offers to open it. Detecting a blocked popup had dropped noreferrer. The page now opens as about:blank, is checked and cut from its opener, and then follows a noreferrer link inside the new window, so the sign-in page receives no referrer. A browser-lane test proves it against a control navigation that does carry one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A window that found another window's claim lock held released its request id and stood down; if the holder closed or gave up before opening, nothing opened the page and nothing offered it. Such a window now keeps its id and waits for the lock, until the id expires. Web Locks end with their holder, including when its page closes; the waiting window then reads the opening record and either stays quiet or claims and opens the page itself. If the wait runs out, the card or a notification offers to open it. When navigating the new blank window fails, it is now closed and the opening fails with its open action instead of leaving the window behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A claim waiting for another window's lock outlived its operation: a terminal snapshot released the request id but left the lock request queued, and once granted the claim opened the URL it had captured. Each claim now has its own AbortController, combined with the expiry timeout, and is withdrawn as soon as its operation is cancelled, fails, connects, is replaced by another operation, leaves the snapshot, or the page is hidden. A withdrawn claim opens and offers nothing. Once a claim holds the lock it reads the latest operation again and opens only if the same operation still waits on the same page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…earer card and notification copy Right after the row's click the button was a faded, disabled "Starting" with no tooltip while the status still read "Sign-in required"; only the server's starting operation switched it to the full Starting state. The row and status line now show that state from the click for every one-click provider, from this client's start in flight. The provider snapshot carries its starting message (Antigravity's "Starting Antigravity. This can take up to a minute."), so the tooltip matches the server's operation from the start; the shared default lives in contracts. A failed sign-in or setup row now offers Retry with the reason on hover and takes the error status dot, like other failed rows. - The card's slow-start note uses the provider's own starting message. - When the user must open the sign-in page (not opened here, or opening failed), the open action takes the primary emphasis and the icon stops spinning. A blocked popup reads "Sign-in page blocked" with "Open Google sign-in to continue." - A failure after Google accepted the sign-in names the next step, "Your Google account is connected. Try again, or check Settings > Diagnostics if it keeps failing.", and its retry is a refresh, not an external link. - Notifications are short titles with an "Open" action and no repeated description. - Setup card body text wraps with text-pretty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client-side cross-window claim (Web Locks, local-storage records, waiters and their withdrawal) kept finding new holes. The server owns the operation, so the claim belongs there: a new claimProviderSignInPage command grants exactly once per operation, under the operation's transition lock, and only to a holder of the request id stamped on the current operation while it waits on a primary sign-in page. Every other claim is denied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the client-side cross-window claim (Web Locks, local-storage opening records, lock waiters, abort controllers and pagehide handling) and its tests. When the start reply or a snapshot shows an operation with a held request id waiting on its primary page, the client asks the server's claimProviderSignInPage once per operation per window, and opens the page only if it is granted. A grant or a denial releases the id; an unknown outcome keeps it for the next snapshot; terminal states, server rejections and expiry release it as before. The app-level coordinator lends the opener the command. A server without request ids keeps the reply-only fallback. Both round-5 findings disappear by construction: nothing aborts a claim on pagehide, and an id held in session storage across a reload simply asks the server again, which grants once; and no client-side waiter exists to outlive a terminal reply, while the server denies any claim for an operation that is no longer waiting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A failed-after-sign-in row's Retry tooltip says what happened ("Signed
in, but Antigravity couldn't start. Try again.") instead of the server's
diagnostic sentence.
- The card's "Settings > Diagnostics" links to that page where the app's
router is present, and keeps its no-break spaces so no line starts
with ">".
- Notification titles name the provider: "Antigravity sign-in page
blocked" and "Antigravity sign-in page didn't open". A layout test
keeps both on one line at the default toast width; the longer "Couldn't
open the Antigravity sign-in page" left "page" alone on a second line.
- The generic starting message is "Starting sign-in.", and Antigravity's
is "Antigravity can take up to a minute to start.", so the starting
card no longer says "Starting" twice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…web code The claim command no longer touches client-runtime: the web defines it with createEnvironmentRpcCommand in a Scient module, as the Scient skill commands do. Its group entry, scope and instrumentation aggregate sit with the existing provider-connection entries instead of opening new marked regions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A grant that arrived after its operation was cancelled, failed, connected or replaced still opened the captured URL. After the claim is answered, the page now opens only if this window still holds the id and the latest known operation is the same one, still waiting on the same page. When the coordinator remounted with a held id and cached waiting providers, its observer ran before the claim command was registered and nothing asked again. The store now records the operations it sees even without the command, and registering the command replays them, so mount order does not matter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rrent A start reply delayed past a snapshot that replaced its operation overwrote that snapshot as the latest operation, so a grant for the replaced operation still opened its page. Once a snapshot of an instance has arrived, snapshots are the only source of its current operations; a reply only seeds an instance no snapshot has shown and never replaces a different operation. Held ids remember the operation the server stamped them on and are released when a snapshot no longer shows it or the id shows up on another operation. The client claims only while the operation is current, so a reply the stream overtook waits for the snapshot that shows it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gence The neighbouring provider-connection entries in rpc.ts, RpcAuthorization.ts and RpcInstrumentation.ts are earlier unmarked Scient additions, not upstream lines, so the new claim entries get their own paired SCIENT-FORK markers, as does ProviderInstanceCard's changed presentation hook import. UPSTREAM.md notes the existing unmarked entries for a follow-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lagging snapshots could release an id the start reply had just shown waiting, or discard a pending grant, so a one-click sign-in stayed "Signing in" with no page. The client stops judging which operation is current: a reply or snapshot showing an operation stamped with a held id, waiting on a primary page, asks for one claim; the server grants only the current operation, once, and the grant carries the page URL the client opens. Ids are released only on the claim's answer, an operation shown ended, a rejected start, or expiry. The latest-operations store, snapshot precedence, the currency checks and the replay on claimer registration are gone; the coordinator mounts its observers once the claim command is registered instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…its grant While a claim awaited its answer, the card's manual open recorded an opening but left the request id held, so the grant opened the page a second time. Opening the page by hand now spends this window's id for the operation, and an automatic opening skips a page this window already opened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
providerSignInBrowser.ts held request ids, the in-flight start state, the page opener, the opening records and toasts, one-click eligibility and the claim flow in one 600-line module. They now live in small modules: providerSignInRequests (held ids, session storage, expiry, change signal), providerSignInStarting (start in flight), providerSignInPage (desktop shell or a referrer-free popup), providerSignInOpenings (opening state and the failure toast), providerSignInFlow (start, claim, open, release), useProviderSignInBrowser (the setup-view hook) and oneClickSignIn. The claim no longer checks for a page already opened here before asking: only a manual open records one while an id is held, and that open releases the id. The check after the grant stays. Tests move with their module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…up card Codex and Cursor computed the same failure, blocked, user-must-open and waiting copy from the opening; Antigravity the same failure, blocked and not-opened-here facts. providerSignInPageState and browserSignInWaitingView compute them next to the hook, and the cards keep only their own copy and masking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The browser-helper preflight and the connection manager described the checks they replaced; their comments now say only what runs. The skill link preparation reads a link only when it needs to know. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The section grew a sentence per review round. It now names the modules, states the request-id, claim, grant and release rules in order, and keeps the startup, popup and one-click notes, without the mechanisms they replaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hout a claim The split dropped the check before claiming as unreachable, but a request id restored after a reload can meet an opening recorded without it: a manual open of an operation whose card had no id, or an unstamped fallback reply. The claim then ran, and kept retrying after a transport failure. The check is back, with tests for both orderings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yaacovcorcos
enabled auto-merge
October 19, 2026 07:45
… modules The Antigravity ACP qualification loads antigravityAuthSupport.ts, which now imports the Scient skill-link and browser-helper preflight modules. Add both to the publication guard so a change to either voids an in-flight run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # apps/server/src/scient/providerLifecycle/ProviderConnectionManager.ts # apps/web/src/scient/providerConnection/ScientAgentAccounts.tsx # packages/contracts/src/providerLifecycle.ts
"<Provider> sign-in page didn't open" wrapped onto two lines with the Linux CI fonts at the default notification width. "<Provider> sign-in didn't open" is shorter than the blocked title, which already fits, and the notification's Open action still names the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
yaacovcorcos
pushed a commit
that referenced
this pull request
Oct 19, 2026
Catches up owned main with Scient PRs #582 (CI exact-commit reuse), #595 (Antigravity and one-click provider sign-in), #568, #569, #570 (Oh My Pi resume, status, multi-question sign-in), #583 and #594. - #582's reuse gate and its test no longer require the Test Web layout job, which this alignment retired with upstream's move to browser tests. - #595's sign-in flow is carried into the React Compiler conversion of the provider-connection files: plain values and functions instead of useMemo/useCallback, conditionals outside try blocks, and the controller's authorization page delegated to #595's shared opener. - The CI guide names the browser coverage that replaced the layout job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 of 4 tasks
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.
What Changed
Fixes Antigravity Google sign-in failing on Windows (and for anyone without Google's own tools installed), and makes provider sign-in pages open reliably.
config/skillsandantigravity-cli/skillsto~/.gemini/...even when those folders didn't exist. Google's agent then failedsession/newwith "Internal error … failed to create workspace folder …/config/skills", so a successful Google sign-in was reported as "did not finish" and every chat would fail. Links are now created only for existing targets; dangling links (and an empty directory the agent created earlier,lstat-checked) are removed. Upstream bug (T3 #10257), recorded inUPSTREAM.mdas worth upstreaming.server.claimProviderSignInPage), returning the URL. Opening survives card close, remounts, reconnects and reloads; other windows and devices never open it. Browser clients detect popup blocking and open without a referrer.Why
Reproduced on the owner's Windows machine with Google's official
agy_acp_server.exe: standalone, the agent's sign-in completes; inside Scient it failed because (1) the dangling links made the verification session fail, (2) the page was only opened while the card stayed mounted during a 30–60 s start, (3) a per-sign-in helper pre-check could stall for 11–46 s under load. Each fix has a regression test that fails without it.Scient code lives in Scient-owned modules; upstream-owned files carry only marked seams (10 files, ~95 changed lines), each recorded in
UPSTREAM.md.Reviewed by Codex over ten rounds (final round: GO, no findings) plus a targeted simplification review, whose one behaviour finding is restored in
719d337243.Documentation Impact
Documentation impact: Updated — docs/user/providers-antigravity.md, docs/user/providers.md, docs/internals/provider-lifecycle.md, UPSTREAM.md
UI Changes
Sign-in states (row, Settings dialog, model-picker card, toasts) in light theme; dark theme was captured too. Before: the row only opened the card; the card claimed "Complete the sign-in in your browser" even when no browser had opened; a failure after a successful Google sign-in read "Google sign-in didn't finish".
Screenshots were reviewed locally (
antigravity-signin-screens-v2); not attached here.Checklist
Not verified before merge: a live Google sign-in on a packaged build (to be tested in the next beta), macOS/Linux execution (covered by CI), real WebSocket drop and renderer reload in the desktop app (covered by tests at the RPC and store level).
🤖 Generated with Claude Code