From 21ed60207d73ab2a048a2e17381b2d98e96514b2 Mon Sep 17 00:00:00 2001
From: Neil <4138956+nwparker@users.noreply.github.com>
Date: Sun, 20 Sep 2026 18:04:24 -0700
Subject: [PATCH] feat(antigravity): show detected native account (#21798)
---
.../native-account-service.test.ts | 11 ++++
.../antigravity/native-account-service.ts | 22 ++++++-
src/preload/api/agent-account-api.ts | 6 ++
.../AntigravityAccountsSection.test.tsx | 58 +++++++++++++++++++
.../settings/AntigravityAccountsSection.tsx | 26 ++++++++-
src/renderer/src/i18n/locales/en.json | 1 +
.../web/preload-api/web-agent-accounts-api.ts | 2 +-
7 files changed, 121 insertions(+), 5 deletions(-)
create mode 100644 src/renderer/src/components/settings/AntigravityAccountsSection.test.tsx
diff --git a/src/main/antigravity/native-account-service.test.ts b/src/main/antigravity/native-account-service.test.ts
index 6ea2ce9cc99..3bab722c125 100644
--- a/src/main/antigravity/native-account-service.test.ts
+++ b/src/main/antigravity/native-account-service.test.ts
@@ -35,6 +35,17 @@ describe('AntigravityAccountService', () => {
expect(state.activeAccountId).toBe(secondAccount.id.split('-').slice(0, 2).join('-'))
})
+ it('reports a signed-in account before it is explicitly saved to the vault', async () => {
+ const active = backend(first)
+ const service = new AntigravityAccountService(createMemoryAntigravityAccountStore(), active)
+
+ const state = await service.listAccounts()
+
+ expect(state.accounts).toEqual([])
+ expect(state.activeAccountId).toBeNull()
+ expect(state.detectedAccount).toMatchObject({ authMethod: 'consumer' })
+ })
+
it('switches the live native credential and verifies readback', async () => {
const active = backend(first)
const account = createSyntheticAntigravityAccount(second)
diff --git a/src/main/antigravity/native-account-service.ts b/src/main/antigravity/native-account-service.ts
index 2f1e8133db4..a365188907e 100644
--- a/src/main/antigravity/native-account-service.ts
+++ b/src/main/antigravity/native-account-service.ts
@@ -31,6 +31,7 @@ export type AntigravityCredentialBackend = {
export type AntigravityAccountState = {
accounts: AntigravityAccountSummary[]
activeAccountId: string | null
+ detectedAccount: Pick | null
}
function accountId(contents: string): string {
@@ -58,9 +59,24 @@ export class AntigravityAccountService {
async listAccounts(): Promise {
const accounts = this.store.read()
- const active = await this.knownActiveAccount(accounts)
+ const current = await this.credentialBackend.read()
+ const active = current
+ ? accounts.find((account) => account.id === accountId(current.contents))
+ : undefined
this.activeAccountId = active?.id ?? null
- return { accounts: accounts.map(summary), activeAccountId: this.activeAccountId }
+ return {
+ accounts: accounts.map(summary),
+ activeAccountId: this.activeAccountId,
+ detectedAccount:
+ current && !active
+ ? {
+ id: accountId(current.contents),
+ email: current.identity?.email ?? null,
+ subject: current.identity?.subject ?? null,
+ authMethod: current.authMethod
+ }
+ : null
+ }
}
async addCurrentAccount(): Promise {
@@ -99,7 +115,7 @@ export class AntigravityAccountService {
throw new Error('Antigravity account switching could not be verified.')
}
this.activeAccountId = id
- return { accounts: accounts.map(summary), activeAccountId: id }
+ return { accounts: accounts.map(summary), activeAccountId: id, detectedAccount: null }
}
async removeAccount(id: string): Promise {
diff --git a/src/preload/api/agent-account-api.ts b/src/preload/api/agent-account-api.ts
index b8c5a9e1421..37895f1b27b 100644
--- a/src/preload/api/agent-account-api.ts
+++ b/src/preload/api/agent-account-api.ts
@@ -68,6 +68,12 @@ export type AntigravityAccountState = {
updatedAt: number
}[]
activeAccountId: string | null
+ detectedAccount: {
+ id: string
+ email: string | null
+ subject: string | null
+ authMethod: string
+ } | null
}
export type AntigravityAccountsApi = {
diff --git a/src/renderer/src/components/settings/AntigravityAccountsSection.test.tsx b/src/renderer/src/components/settings/AntigravityAccountsSection.test.tsx
new file mode 100644
index 00000000000..0435196e9f4
--- /dev/null
+++ b/src/renderer/src/components/settings/AntigravityAccountsSection.test.tsx
@@ -0,0 +1,58 @@
+// @vitest-environment happy-dom
+
+import '@testing-library/jest-dom/vitest'
+
+import React from 'react'
+import { cleanup, render, screen } from '@testing-library/react'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+
+const list = vi.hoisted(() => vi.fn())
+
+vi.mock('@/i18n/i18n', () => ({
+ translate: (_key: string, fallback: string) => fallback
+}))
+
+vi.mock('../ui/button', () => ({
+ Button: ({ children, ...props }: React.ComponentProps<'button'>) =>
+ React.createElement('button', props, children)
+}))
+
+vi.mock('../status-bar/icons', () => ({
+ GeminiIcon: () => React.createElement('span', { 'data-testid': 'gemini-icon' })
+}))
+
+import { AntigravityAccountsSection } from './AntigravityAccountsSection'
+
+describe('AntigravityAccountsSection', () => {
+ beforeEach(() => {
+ list.mockResolvedValue({
+ accounts: [],
+ activeAccountId: null,
+ detectedAccount: {
+ id: 'antigravity-detected',
+ email: 'detected@example.com',
+ subject: 'subject',
+ authMethod: 'consumer'
+ }
+ })
+ Object.defineProperty(window, 'api', {
+ configurable: true,
+ value: { antigravityAccounts: { list } }
+ })
+ })
+
+ afterEach(() => {
+ cleanup()
+ vi.clearAllMocks()
+ })
+
+ it('shows a detected account before explicit vault save', async () => {
+ render()
+
+ expect(await screen.findByText('detected@example.com')).toBeInTheDocument()
+ expect(
+ screen.getByText('Detected on this computer; add it to enable switching')
+ ).toBeInTheDocument()
+ expect(screen.getByText('Add current agy account')).toBeInTheDocument()
+ })
+})
diff --git a/src/renderer/src/components/settings/AntigravityAccountsSection.tsx b/src/renderer/src/components/settings/AntigravityAccountsSection.tsx
index f71c83af347..149bdb5e905 100644
--- a/src/renderer/src/components/settings/AntigravityAccountsSection.tsx
+++ b/src/renderer/src/components/settings/AntigravityAccountsSection.tsx
@@ -13,7 +13,8 @@ export function AntigravityAccountsSection({
}): React.JSX.Element {
const [state, setState] = useState({
accounts: [],
- activeAccountId: null
+ activeAccountId: null,
+ detectedAccount: null
})
const [busy, setBusy] = useState(false)
@@ -74,6 +75,29 @@ export function AntigravityAccountsSection({
))}
)}
+ {state.detectedAccount && (
+
+
+
+ {state.detectedAccount.email ??
+ state.detectedAccount.subject ??
+ translate(
+ 'auto.components.settings.AccountsPane.antigravitySignedIn',
+ 'Signed-in Antigravity account'
+ )}
+
+
+ {translate(
+ 'auto.components.settings.AccountsPane.antigravityDetected',
+ 'Detected on this computer; add it to enable switching'
+ )}
+
+
+
+ )}
{state.accounts.map((account) => (
Promise.reject(new Error('Antigravity account storage is only available in the desktop app.'))
return {