diff --git a/apps/mobile/src/features/settings/SettingsProviderAccountsRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsProviderAccountsRouteScreen.tsx index 6dfb8745ed967..171a6382d619e 100644 --- a/apps/mobile/src/features/settings/SettingsProviderAccountsRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsProviderAccountsRouteScreen.tsx @@ -204,6 +204,9 @@ function ProviderAccount({ !active && signedIn && (provider.auth.canLogout ?? provider.setup?.canAuthenticate); const showCallback = Boolean(url) && (interaction?.type === "browser" ? interaction.acceptsCallback : !interaction); + const usesCredentials = + provider.driver === "antigravity" && + (provider.auth.type === "gemini-api-key" || provider.auth.type === "agent-platform"); const status = active || state?.phase === "failed" || state?.phase === "cancelled" ? state.message @@ -217,7 +220,9 @@ function ProviderAccount({ ? "No in-app sign-in advertised. Follow the provider's docs to finish setup." : !provider.installed ? "Not installed" - : "Not signed in"; + : usesCredentials + ? "Connect to load models" + : "Not signed in"; // A provider with several sign-in methods picks one from a menu on the same control. const methodActions: MenuAction[] = methods.map((method) => ({ id: `method:${method.id}`, @@ -328,8 +333,8 @@ function ProviderAccount({ ) : canSignIn && !signedIn ? ( startSignIn()} /> diff --git a/apps/server/src/provider/AntigravityProvider.test.ts b/apps/server/src/provider/AntigravityProvider.test.ts index 1f852ad8bcc87..349a44a7005e1 100644 --- a/apps/server/src/provider/AntigravityProvider.test.ts +++ b/apps/server/src/provider/AntigravityProvider.test.ts @@ -129,6 +129,7 @@ type ProbeError = EffectAcpErrors.AcpError | ProviderSetupError; const makeHarness = Effect.fn("makeAntigravityProviderHarness")(function* ( options: { + readonly auth?: { readonly type: string; readonly label: string }; readonly enabled?: boolean; readonly safe?: boolean; readonly probeUsage?: Effect.Effect; @@ -145,6 +146,7 @@ const makeHarness = Effect.fn("makeAntigravityProviderHarness")(function* ( decodeSettings({ enabled: options.enabled ?? true, customModels: ["do-not-seed-me"] }), { stampIdentity: (snapshot) => Effect.succeed({ ...snapshot, instanceId, driver }), + ...(options.auth ? { auth: options.auth } : {}), ...(options.probeUsage ? { probeUsage: options.probeUsage } : {}), probe: Ref.update(probeCalls, (count) => count + 1).pipe( Effect.andThen(Ref.get(probe)), @@ -305,23 +307,35 @@ it.layer(layerTest)("Antigravity provider snapshots", (it) => { it.effect("treats initialize as installation proof, not account or model discovery", () => Effect.scoped( Effect.gen(function* () { - const harness = yield* makeHarness(); - expect(yield* harness.provider.snapshot.getSnapshot).toMatchObject({ - installed: false, - status: "warning", - auth: { status: "unknown" }, - models: [], - }); - yield* harness.initialize; - const snapshot = yield* harness.provider.snapshot.getSnapshot; - expect(snapshot).toMatchObject({ - installed: true, - status: "warning", - version: "agy_acp_server_20260818_01_RC01", - auth: { status: "unknown" }, - models: [], - }); - expect(yield* Ref.get(harness.probeCalls)).toBe(1); + for (const auth of [ + undefined, + { type: "oauth-personal", label: "Google account" }, + { type: "oauth-business", label: "Gemini Enterprise" }, + { type: "gemini-api-key", label: "Gemini API key" }, + { type: "agent-platform", label: "Agent Platform" }, + ]) { + const harness = yield* makeHarness(auth ? { auth } : {}); + expect(yield* harness.provider.snapshot.getSnapshot).toMatchObject({ + installed: false, + status: "warning", + auth: { status: "unknown" }, + models: [], + }); + yield* harness.initialize; + const snapshot = yield* harness.provider.snapshot.getSnapshot; + expect(snapshot).toMatchObject({ + installed: true, + status: "warning", + version: "agy_acp_server_20260818_01_RC01", + auth: { status: "unknown", ...(auth ? { type: auth.type } : {}) }, + message: + auth?.type === "gemini-api-key" || auth?.type === "agent-platform" + ? `Antigravity is installed. Connect with ${auth.label} to load models.` + : "Antigravity is installed. Google account access is not checked yet.", + models: [], + }); + expect(yield* Ref.get(harness.probeCalls)).toBe(1); + } }), ), ); @@ -360,7 +374,8 @@ it.layer(layerTest)("Antigravity provider snapshots", (it) => { it.effect("clears all account metadata on sign-out and authentication failure", () => Effect.scoped( Effect.gen(function* () { - const harness = yield* makeHarness(); + const auth = { type: "gemini-api-key", label: "Gemini API key" }; + const harness = yield* makeHarness({ auth }); yield* harness.initialize; for (const clear of [harness.provider.onSignedOut, harness.provider.onAuthRequired]) { yield* harness.provider.onSessionStarted(started, "/workspace"); @@ -369,7 +384,8 @@ it.layer(layerTest)("Antigravity provider snapshots", (it) => { expect(yield* harness.provider.snapshot.getSnapshot).toMatchObject({ installed: true, status: "warning", - auth: { status: "unauthenticated" }, + auth: { status: "unauthenticated", type: auth.type, label: auth.label }, + message: "Connect with Gemini API key to use Antigravity and load models.", models: [], slashCommands: [], skills: [], @@ -382,7 +398,14 @@ it.layer(layerTest)("Antigravity provider snapshots", (it) => { expect((yield* harness.provider.snapshot.getSnapshot).models).toEqual([]); } const refreshed = yield* harness.provider.snapshot.refresh; - expect(refreshed.auth.status).toBe("unauthenticated"); + expect(refreshed.auth).toEqual({ + status: "unauthenticated", + type: auth.type, + label: auth.label, + }); + expect(refreshed.message).toBe( + "Connect with Gemini API key to use Antigravity and load models.", + ); expect(refreshed.supportsTextGeneration).toBe(false); }), ), diff --git a/apps/server/src/provider/AntigravityProvider.ts b/apps/server/src/provider/AntigravityProvider.ts index b5e9f257d17b3..6dcde50c5329c 100644 --- a/apps/server/src/provider/AntigravityProvider.ts +++ b/apps/server/src/provider/AntigravityProvider.ts @@ -164,6 +164,14 @@ export const makeAntigravityProvider = Effect.fn("makeAntigravityProvider")(func settings: AntigravitySettings, options: AntigravityProviderOptions, ) { + const usesCredentials = + options.auth?.type === "gemini-api-key" || options.auth?.type === "agent-platform"; + const signInMessage = usesCredentials + ? `Connect with ${options.auth.label} to use Antigravity and load models.` + : SIGN_IN_MESSAGE; + const authUncheckedMessage = usesCredentials + ? `Antigravity is installed. Connect with ${options.auth.label} to load models.` + : AUTH_UNCHECKED_MESSAGE; const checkedAt = DateTime.formatIso(yield* DateTime.now); const initialDraft = { ...buildServerProvider({ @@ -243,8 +251,8 @@ export const makeAntigravityProvider = Effect.fn("makeAntigravityProvider")(func (authenticated ? undefined : draft.auth.status === "unauthenticated" - ? SIGN_IN_MESSAGE - : AUTH_UNCHECKED_MESSAGE); + ? signInMessage + : authUncheckedMessage); return { ...state, draft: { @@ -420,9 +428,12 @@ export const makeAntigravityProvider = Effect.fn("makeAntigravityProvider")(func authRevision: state.authRevision + 1, draft: { ...state.draft, - auth: { status: "unauthenticated" }, + auth: { + status: "unauthenticated", + ...(usesCredentials ? options.auth : {}), + }, status: "warning", - message: SIGN_IN_MESSAGE, + message: signInMessage, checkedAt: updatedAt, models: [], slashCommands: [], diff --git a/apps/web/src/components/settings/ProviderAuthenticationSection.tsx b/apps/web/src/components/settings/ProviderAuthenticationSection.tsx index b09fd5002c194..f5c4ee146db2a 100644 --- a/apps/web/src/components/settings/ProviderAuthenticationSection.tsx +++ b/apps/web/src/components/settings/ProviderAuthenticationSection.tsx @@ -73,11 +73,16 @@ export function ProviderAuthenticationSection({ !signedIn && (provider.setup?.canAuthenticate === false || (provider.driver === "acpRegistry" && auth?.methods?.length === 0)); + const usesCredentials = + provider.driver === "antigravity" && + (provider.auth.type === "gemini-api-key" || provider.auth.type === "agent-platform"); const accountDescription = active ? auth?.phase === "starting" - ? (auth.message ?? "Starting sign-in…") + ? (auth.message ?? (usesCredentials ? "Connecting…" : "Starting sign-in…")) : auth?.phase === "verifying" - ? "Checking your account…" + ? usesCredentials + ? "Checking credentials and loading models…" + : "Checking your account…" : interaction?.type === "terminal" ? "Complete sign-in in the terminal below." : interaction?.type === "credentials" @@ -89,7 +94,9 @@ export function ProviderAuthenticationSection({ ? "Discovering sign-in methods…" : needsExternalSetup ? "No in-app sign-in advertised. Follow the provider's docs to finish setup." - : `Sign in on ${environmentLabel}.`; + : usesCredentials + ? `Connect on ${environmentLabel} to load models.` + : `Sign in on ${environmentLabel}.`; const statusMessage = auth?.phase === "failed" ? auth.message : null; const disabled = readOnly || pending || query.error !== null || isDiscovering; const draftId = `${auth?.flowId ?? ""}:${interaction?.id ?? ""}`; @@ -291,8 +298,12 @@ export function ProviderAuthenticationSection({ {signedIn ? "Change account" : auth?.phase === "failed" || auth?.phase === "cancelled" - ? "Retry sign-in" - : "Sign in"} + ? usesCredentials + ? "Retry connection" + : "Retry sign-in" + : usesCredentials + ? "Connect" + : "Sign in"} ) : null} {!active && signedIn && (provider.auth.canLogout ?? provider.setup?.canAuthenticate) ? (