diff --git a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts index 63286a4bcea79..ebcde1741e14b 100644 --- a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts @@ -35,6 +35,7 @@ import { normalizeAntigravitySessionUpdate } from "../../provider/acp/Antigravit import { ANTIGRAVITY_SIGN_IN_REQUIRED_MESSAGE } from "../../provider/antigravityAuthSupport.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; +import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; import { makeAntigravityAcpAdapterFlavor, makeAntigravityAdapterV2, @@ -142,6 +143,58 @@ const layerSession = Layer.mergeAll( TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), ); +describe("AntigravityAdapterV2 model availability", () => { + it.live("explains ACP access restrictions when the session does not offer the saved model", () => + Effect.gen(function* () { + const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; + const path = yield* Path.Path; + const crypto = yield* Crypto.Crypto; + const mockAgentPath = yield* path.fromFileUrl( + new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), + ); + const instanceId = ProviderInstanceId.make("antigravity-model-access-test"); + const adapter = yield* makeAntigravityAdapterV2({ + instanceId, + selfInvocation: yield* resolveSelfInvocation(), + makeRuntime: (input) => + makeAntigravityAcpRuntime({ + ...input, + childProcessSpawner, + spawn: { + command: process.execPath, + args: [mockAgentPath], + cwd: input.cwd, + env: { T3_ACP_ANTIGRAVITY: "1" }, + }, + }).pipe(Effect.provideService(Crypto.Crypto, crypto)), + withProcess: (_stop, task) => task, + // @effect-diagnostics-next-line effectSucceedWithVoid:off - This optional result requires undefined, not the wider void type. + defaultModel: Effect.succeed(undefined), + }); + const threadId = ThreadId.make("thread-antigravity-model-access"); + const runtimePolicy = ProviderAdapter.ProviderAdapterV2RuntimePolicy.make({ + runtimeMode: "full-access", + interactionMode: "default", + cwd: process.cwd(), + }); + const open = (model: string) => + adapter.openSession({ + threadId, + providerSessionId: ProviderSessionId.make(`provider-session-${model}`), + modelSelection: { instanceId, model }, + runtimePolicy, + }); + const error = yield* open("claude-test").pipe(Effect.flip); + const failure = makeProviderFailure({ cause: error }); + assert.include(failure.message, '"claude-test"'); + assert.include(failure.message, "sign-in method and ACP client"); + assert.include(failure.message, "Antigravity app"); + assert.include(failure.message, "refresh Antigravity models"); + yield* open("gemini-test-low"); + }).pipe(Effect.provide(layerSession), Effect.scoped), + ); +}); + describe("AntigravityAdapterV2 compaction", () => { it.effect("does not expose unsupported manual compaction on the provider session", () => Effect.gen(function* () { diff --git a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts index 8c5ccb48b149d..91695195fed20 100644 --- a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.ts @@ -234,6 +234,8 @@ export function makeAntigravityAcpAdapterFlavor( // Loading history replays tool calls; resume avoids replay. subagentsIdleOnTurnCompletion: true, ...(options.onSessionEvent === undefined ? {} : { onSessionEvent: options.onSessionEvent }), + modelSelectionGuidance: + "Antigravity model access can depend on the sign-in method and ACP client, and can differ from the Antigravity app. Update T3 Code and refresh Antigravity models, then choose an available model and retry.", applyModelSelection: Effect.fn("AntigravityAdapterV2.applyModelSelection")(function* ({ runtime, modelSelection, diff --git a/apps/server/src/provider/acp/AntigravityAcpSupport.ts b/apps/server/src/provider/acp/AntigravityAcpSupport.ts index a2be5ccaa3f6a..f162d34f68f1b 100644 --- a/apps/server/src/provider/acp/AntigravityAcpSupport.ts +++ b/apps/server/src/provider/acp/AntigravityAcpSupport.ts @@ -256,7 +256,7 @@ export const applyAntigravityAcpModelSelection = Effect.fn("applyAntigravityAcpM return yield* Effect.fail( input.mapError( EffectAcpErrors.AcpRequestError.invalidParams( - `Antigravity model '${resolved}' is unavailable for this Google account. Select an available model.`, + `Antigravity model '${resolved}' is unavailable in this ACP session. Select an available model.`, ), ), ); diff --git a/packages/provider-acp/src/server/adapter.ts b/packages/provider-acp/src/server/adapter.ts index 2110f9616de54..fcb7ca836ed27 100644 --- a/packages/provider-acp/src/server/adapter.ts +++ b/packages/provider-acp/src/server/adapter.ts @@ -219,11 +219,12 @@ class AcpModelSelectionError extends Schema.TaggedError( "AcpModelSelectionError", { model: Schema.String, + guidance: Schema.optional(Schema.String), cause: Schema.Defect(), }, ) { override get message(): string { - return `The agent did not accept the model "${this.model}". Choose another model for this provider, then retry.`; + return `The agent did not accept the model "${this.model}". ${this.guidance ?? "Choose another model for this provider, then retry."}`; } } @@ -364,6 +365,7 @@ export interface AcpAdapterV2Flavor { readonly startResult: AcpSessionRuntime.AcpSessionRuntimeStartResult; readonly modelSelection: ModelSelection; }) => Effect.Effect; + readonly modelSelectionGuidance?: string; /** * Model-selection option ids `applyModelSelection` sets itself under a * native id, which the generic option loop then skips (Kiro's @@ -7102,7 +7104,11 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( // own error. const rejectedModel = (error: EffectAcpErrors.AcpError) => error._tag === "AcpRequestError" && error.code === -32602 - ? new AcpModelSelectionError({ model: modelSelection.model, cause: error }) + ? new AcpModelSelectionError({ + model: modelSelection.model, + guidance: flavor.modelSelectionGuidance, + cause: error, + }) : error; let appliedModel: string | undefined; if (flavor.applyModelSelection !== undefined) {