Skip to content
Open
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 @@ -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,
Expand Down Expand Up @@ -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* () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/provider/acp/AntigravityAcpSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.`,
),
),
);
Expand Down
10 changes: 8 additions & 2 deletions packages/provider-acp/src/server/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,12 @@ class AcpModelSelectionError extends Schema.TaggedError<AcpModelSelectionError>(
"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."}`;
}
}

Expand Down Expand Up @@ -364,6 +365,7 @@ export interface AcpAdapterV2Flavor {
readonly startResult: AcpSessionRuntime.AcpSessionRuntimeStartResult;
readonly modelSelection: ModelSelection;
}) => Effect.Effect<string | undefined, EffectAcpErrors.AcpError>;
readonly modelSelectionGuidance?: string;
/**
* Model-selection option ids `applyModelSelection` sets itself under a
* native id, which the generic option loop then skips (Kiro's
Expand Down Expand Up @@ -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) {
Expand Down
Loading