Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}`,
Expand Down Expand Up @@ -328,8 +333,8 @@ function ProviderAccount({
) : canSignIn && !signedIn ? (
<ControlPill
variant="primary"
label="Sign in"
accessibilityLabel={`Sign in to ${name}`}
label={usesCredentials ? "Connect" : "Sign in"}
accessibilityLabel={usesCredentials ? `Connect to ${name}` : `Sign in to ${name}`}
disabled={signInDisabled}
onPress={() => startSignIn()}
/>
Expand Down
63 changes: 43 additions & 20 deletions apps/server/src/provider/AntigravityProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ServerProviderUsageLimits>;
Expand All @@ -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)),
Expand Down Expand Up @@ -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);
}
}),
),
);
Expand Down Expand Up @@ -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");
Expand All @@ -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: [],
Expand All @@ -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);
}),
),
Expand Down
19 changes: 15 additions & 4 deletions apps/server/src/provider/AntigravityProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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: [],
Expand Down
21 changes: 16 additions & 5 deletions apps/web/src/components/settings/ProviderAuthenticationSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 ?? ""}`;
Expand Down Expand Up @@ -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"}
</Button>
) : null}
{!active && signedIn && (provider.auth.canLogout ?? provider.setup?.canAuthenticate) ? (
Expand Down
Loading