Skip to content

Commit 9f00ced

Browse files
StiensWoutclaudeYash-Singh1
authored
fix(antigravity): explain missing SOCKS dependency during refresh (#19660)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Yash Singh <saiansh2525@gmail.com>
1 parent f016a16 commit 9f00ced

3 files changed

Lines changed: 100 additions & 6 deletions

File tree

‎apps/server/scripts/acp-mock-agent.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ const failLoadSessionAfterConfigReplay =
6464
process.env.T3_ACP_FAIL_LOAD_SESSION_AFTER_CONFIG_REPLAY === "1";
6565
const emitLoadReplay = process.env.T3_ACP_EMIT_LOAD_REPLAY === "1";
6666
const failPrompt = process.env.T3_ACP_FAIL_PROMPT === "1";
67+
// JSON for the `data` of an "Internal error" response to `session/new`, as
68+
// Antigravity sends when its runtime cannot reach its local harness.
69+
const newSessionErrorData =
70+
process.env.T3_ACP_NEW_SESSION_ERROR_DATA === undefined
71+
? undefined
72+
: Schema.decodeSync(Schema.fromJsonString(Schema.Unknown))(
73+
process.env.T3_ACP_NEW_SESSION_ERROR_DATA,
74+
);
6775
const failPromptOrdinal = Number(process.env.T3_ACP_FAIL_PROMPT_ORDINAL);
6876
const compactionPromptOrdinals = new Set(
6977
(process.env.T3_ACP_COMPACTION_PROMPT_ORDINALS ?? "").split(",").map(Number),
@@ -594,6 +602,9 @@ const program = Effect.gen(function* () {
594602
yield* agent.handleCreateSession(() =>
595603
Effect.gen(function* () {
596604
yield* requireAuthentication();
605+
if (newSessionErrorData !== undefined) {
606+
return yield* AcpError.AcpRequestError.internalError("Internal error", newSessionErrorData);
607+
}
597608
if (antigravityProfile) {
598609
yield* publishAntigravityCommands(sessionId);
599610
}

‎apps/server/src/provider/Drivers/AntigravityDriver.test.ts‎

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* (
7777

7878
const makeExecutable = Effect.fn("AntigravityDriverTest.makeExecutable")(function* (
7979
name: string,
80-
loginRequired = false,
80+
options: {
81+
readonly loginRequired?: boolean;
82+
/** `data` of the agent's "Internal error" response to `session/new`. */
83+
readonly newSessionErrorData?: unknown;
84+
} = {},
8185
) {
8286
const directory = path.join(root, name);
8387
const executablePath = path.join(directory, "agy_acp_server.par");
@@ -89,9 +93,14 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* (
8993
executablePath,
9094
[
9195
"#!/bin/sh",
92-
...(loginRequired
96+
...(options.loginRequired
9397
? [`printf '%s\\n' ${shellQuote(ANTIGRAVITY_AUTH_STDOUT_PREFIX + authorizationUrl)}`]
9498
: []),
99+
...(options.newSessionErrorData === undefined
100+
? []
101+
: [
102+
`export T3_ACP_NEW_SESSION_ERROR_DATA=${shellQuote(JSON.stringify(options.newSessionErrorData))}`,
103+
]),
95104
`exec ${shellQuote(nodePath)} ${shellQuote(mockAgentPath)} "$@"`,
96105
"",
97106
].join("\n"),
@@ -110,7 +119,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* (
110119

111120
const first = yield* makeExecutable("runtime 'one");
112121
const second = yield* makeExecutable("runtime two");
113-
const signedOut = yield* makeExecutable("runtime signed-out", true);
122+
const signedOut = yield* makeExecutable("runtime signed-out", { loginRequired: true });
114123
const controls = { selected: first, failResolution: false, beforeAcquire: Effect.void };
115124
const acquisitions: Array<{ binaryPath: string | undefined; path: string | undefined }> = [];
116125
const releases: Array<string | null> = [];
@@ -229,6 +238,7 @@ const makeHarness = Effect.fn("makeAntigravityDriverHarness")(function* (
229238
return {
230239
instance,
231240
refresh,
241+
makeExecutable,
232242
fs,
233243
path,
234244
profileDirectory,
@@ -423,6 +433,64 @@ it.layer(layerTest)("AntigravityDriver", (it) => {
423433
}).pipe(Effect.scoped),
424434
);
425435

436+
it.effect.skipIf(windowsHost)(
437+
"reports the missing SOCKS dependency when session creation fails and keeps the catalog",
438+
() =>
439+
Effect.gen(function* () {
440+
const h = yield* makeHarness();
441+
yield* h.refresh();
442+
const before = yield* h.instance.snapshot.getSnapshot;
443+
h.controls.selected = yield* h.makeExecutable("runtime socks", {
444+
newSessionErrorData: { details: "python-socks is required to use a SOCKS proxy" },
445+
});
446+
const error = yield* h.refresh().pipe(Effect.flip);
447+
expect(error.detail).toBe(
448+
"Could not refresh Antigravity models. The previous model list is unchanged. Antigravity reported: python-socks is required to use a SOCKS proxy",
449+
);
450+
const after = yield* h.instance.snapshot.getSnapshot;
451+
expect(after.models).toEqual(before.models);
452+
expect(after.auth).toEqual(before.auth);
453+
yield* h.assertClosed;
454+
}).pipe(Effect.scoped),
455+
);
456+
457+
it.effect.skipIf(windowsHost)(
458+
"never copies unrecognized agent error details into the refresh error",
459+
() =>
460+
Effect.gen(function* () {
461+
const h = yield* makeHarness();
462+
yield* h.refresh();
463+
const before = yield* h.instance.snapshot.getSnapshot;
464+
const generic =
465+
"Could not refresh Antigravity models. The previous model list is unchanged.";
466+
const secret = "https://accounts.example.test/o/oauth2?code=sk-secret-123";
467+
h.controls.selected = yield* h.makeExecutable("runtime socks with extra", {
468+
newSessionErrorData: {
469+
details: `Traceback (most recent call last):\n proxy ${secret}\npython-socks is required to use a SOCKS proxy`,
470+
},
471+
});
472+
const known = yield* h.refresh().pipe(Effect.flip);
473+
expect(known.detail).toBe(
474+
`${generic} Antigravity reported: python-socks is required to use a SOCKS proxy`,
475+
);
476+
477+
for (const [name, data] of [
478+
["runtime unknown details", { details: `Token refresh failed for ${secret}` }],
479+
["runtime object details", { details: { stack: "secret frame", env: { HOME: "/" } } }],
480+
["runtime no details", ["python-socks is required to use a SOCKS proxy"]],
481+
] as const) {
482+
h.controls.selected = yield* h.makeExecutable(name, { newSessionErrorData: data });
483+
const error = yield* h.refresh().pipe(Effect.flip);
484+
expect(error.detail).toBe(generic);
485+
}
486+
const after = yield* h.instance.snapshot.getSnapshot;
487+
expect(after.models).toEqual(before.models);
488+
expect(after.auth).toEqual(before.auth);
489+
expect(JSON.stringify(after)).not.toContain("sk-secret-123");
490+
yield* h.assertClosed;
491+
}).pipe(Effect.scoped),
492+
);
493+
426494
it.effect.skipIf(windowsHost)(
427495
"removes runtime temp directories left by a previous server on create",
428496
() =>

‎apps/server/src/provider/Drivers/AntigravityDriver.ts‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import * as Scope from "effect/Scope";
2323
import * as Stream from "effect/Stream";
2424
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
2525
import { HttpClient } from "effect/http";
26-
import type { AcpError } from "effect-acp/errors";
26+
import * as AcpErrors from "effect-acp/errors";
2727

2828
import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
2929
import {
@@ -76,6 +76,21 @@ import { discoverAntigravitySkills, resolveAntigravityUserHome } from "./Antigra
7676
const DRIVER = ProviderDriverKind.make("antigravity");
7777
const decodeSettings = Schema.decodeSync(AntigravitySettings);
7878
const isNodeRuntimeUnavailableError = Schema.is(NodeRuntimeUnavailableError);
79+
const isAcpRequestError = Schema.is(AcpErrors.AcpRequestError);
80+
const isAcpErrorDetails = Schema.is(Schema.Struct({ details: Schema.String }));
81+
const REFRESH_FAILED =
82+
"Could not refresh Antigravity models. The previous model list is unchanged.";
83+
const PYTHON_SOCKS_REQUIRED = "python-socks is required to use a SOCKS proxy";
84+
85+
// Keep upstream diagnostics in cause; only recognized, fixed text reaches clients.
86+
function refreshFailureDetail(cause: unknown) {
87+
return isAcpRequestError(cause) &&
88+
cause.operation === "receive-response" &&
89+
isAcpErrorDetails(cause.data) &&
90+
cause.data.details.includes(PYTHON_SOCKS_REQUIRED)
91+
? `${REFRESH_FAILED} Antigravity reported: ${PYTHON_SOCKS_REQUIRED}`
92+
: REFRESH_FAILED;
93+
}
7994

8095
export type AntigravityDriverEnv =
8196
| AntigravityInstallation.AntigravityInstallation
@@ -185,7 +200,7 @@ export const AntigravityDriver: ProviderDriver<
185200
input: Omit<AntigravityAcpRuntimeInput, "spawn" | "childProcessSpawner">,
186201
): Effect.fn.Return<
187202
AcpSessionRuntime.AcpSessionRuntime["Service"],
188-
AcpError | ProviderSetupError,
203+
AcpErrors.AcpError | ProviderSetupError,
189204
Scope.Scope
190205
> {
191206
if (authConfigIssue !== null) {
@@ -513,7 +528,7 @@ export const AntigravityDriver: ProviderDriver<
513528
: cause._tag === "ProviderSetupError" &&
514529
(cause.operation === "configure" || cause.operation === "start")
515530
? cause.detail
516-
: "Could not refresh Antigravity models. The previous model list is unchanged.",
531+
: refreshFailureDetail(cause),
517532
cause,
518533
}),
519534
),

0 commit comments

Comments
 (0)