@@ -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 () =>
0 commit comments