@claudexor/harness-agy 3.6.0 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,384 @@
1
+ import { spawn } from "node:child_process";
2
+ import { extname, isAbsolute } from "node:path";
3
+ import { composeBaseEnv, defaultProcessGroupService, labelStreams, reapProcessTree, registerChildProcess, resolveHarnessBinary, unregisterChildProcess, } from "@claudexor/core";
4
+ import { redactSecrets } from "@claudexor/util";
5
+ export const AGY_PRINT_TIMEOUT_MS = 30_000;
6
+ export const AGY_PRINT_STREAM_LIMIT_BYTES = 256 * 1024;
7
+ const AGY_PRINT_DRAIN_MS = 200;
8
+ const AGY_PRINT_CANCEL_DEADLINE_MS = 5_000;
9
+ /** Spawn policy is exported so Windows fixtures can assert no inherited console/stdio. */
10
+ export function agyPrintSpawnOptions(platform, env) {
11
+ return {
12
+ env,
13
+ shell: false,
14
+ detached: true,
15
+ windowsHide: platform === "win32",
16
+ // On Windows CREATE_NO_WINDOW still exposes a windowless CONIN$. A real
17
+ // DETACHED_PROCESS prevents console inheritance; ignored stdin gives print
18
+ // mode immediate EOF without exposing an input descriptor.
19
+ };
20
+ }
21
+ function composedEnv(patch) {
22
+ const env = composeBaseEnv("mirror_native");
23
+ for (const [key, value] of Object.entries(patch)) {
24
+ if (value === null || value === undefined)
25
+ delete env[key];
26
+ else
27
+ env[key] = value;
28
+ }
29
+ return env;
30
+ }
31
+ function exactBinary(bin, env, platform, resolver) {
32
+ const resolved = resolver
33
+ ? resolver(bin, env, platform)
34
+ : resolveHarnessBinary(bin, env, process.execPath, platform);
35
+ if (!resolved || !isAbsolute(resolved))
36
+ return null;
37
+ if (platform === "win32" && ![".exe", ".com"].includes(extname(resolved).toLowerCase())) {
38
+ return null;
39
+ }
40
+ return resolved;
41
+ }
42
+ function boundedBuffer(maxBytes, overflow) {
43
+ const chunks = [];
44
+ let bytes = 0;
45
+ let overflowed = false;
46
+ const markOverflow = () => {
47
+ if (overflowed)
48
+ return;
49
+ overflowed = true;
50
+ overflow();
51
+ };
52
+ return {
53
+ push(chunk) {
54
+ if (chunk.length === 0)
55
+ return;
56
+ // A prior chunk may have filled the cap exactly. The NEXT byte is still
57
+ // overflow and must cancel; silently returning here used to let an
58
+ // unbounded producer run forever. This also makes a zero-byte cap real.
59
+ if (bytes >= maxBytes) {
60
+ markOverflow();
61
+ return;
62
+ }
63
+ const remaining = maxBytes - bytes;
64
+ if (chunk.length > remaining) {
65
+ if (remaining > 0)
66
+ chunks.push(chunk.subarray(0, remaining));
67
+ bytes = maxBytes;
68
+ markOverflow();
69
+ return;
70
+ }
71
+ chunks.push(chunk);
72
+ bytes += chunk.length;
73
+ },
74
+ text() {
75
+ return Buffer.concat(chunks, bytes).toString("utf8");
76
+ },
77
+ };
78
+ }
79
+ function delay(ms) {
80
+ return new Promise((resolve) => {
81
+ const timer = setTimeout(resolve, ms);
82
+ timer.unref();
83
+ });
84
+ }
85
+ /**
86
+ * Bounded print-mode owner for `/model` and `/quota`. The child has no stdin
87
+ * and no controlling terminal: POSIX starts a new session; Windows uses
88
+ * DETACHED_PROCESS because CREATE_NO_WINDOW still exposes a windowless
89
+ * CONIN$. Child exit is completion authority; descendant-held pipes receive
90
+ * only a bounded drain.
91
+ */
92
+ export async function runAgyPrintCommand(bin, command, envPatch, options = {}) {
93
+ const platform = options.platform ?? process.platform;
94
+ const env = composedEnv(envPatch);
95
+ const resolved = exactBinary(bin, env, platform, options.resolveBinary);
96
+ if (!resolved) {
97
+ return {
98
+ kind: "failed",
99
+ reason: "spawn_failed",
100
+ detail: `agy executable could not be resolved to an exact ${platform === "win32" ? "Windows image" : "regular executable"}`,
101
+ stdout: "",
102
+ stderr: "",
103
+ };
104
+ }
105
+ if (options.abortSignal?.aborted) {
106
+ return {
107
+ kind: "failed",
108
+ reason: "aborted",
109
+ detail: "agy print probe was aborted",
110
+ stdout: "",
111
+ stderr: "",
112
+ };
113
+ }
114
+ let child;
115
+ try {
116
+ child = (options.spawnProcess ?? spawn)(resolved, ["-p", command, "--output-format", "json"], {
117
+ ...agyPrintSpawnOptions(platform, env),
118
+ stdio: ["ignore", "pipe", "pipe"],
119
+ });
120
+ }
121
+ catch (error) {
122
+ return {
123
+ kind: "failed",
124
+ reason: "spawn_failed",
125
+ detail: redactSecrets(error instanceof Error ? error.message : String(error)).slice(0, 300),
126
+ stdout: "",
127
+ stderr: "",
128
+ };
129
+ }
130
+ const pid = child.pid;
131
+ const processRegistry = options.processRegistry ?? {
132
+ register: registerChildProcess,
133
+ unregister: unregisterChildProcess,
134
+ };
135
+ let registered = false;
136
+ if (typeof pid === "number") {
137
+ processRegistry.register(pid, resolved);
138
+ registered = true;
139
+ }
140
+ const unregisterExitedChild = () => {
141
+ if (!registered || typeof pid !== "number")
142
+ return;
143
+ registered = false;
144
+ processRegistry.unregister(pid);
145
+ };
146
+ let group;
147
+ if (platform !== "win32" && typeof pid === "number") {
148
+ const captured = defaultProcessGroupService.captureLeader(pid);
149
+ if (captured.status === "known")
150
+ group = captured.handle;
151
+ }
152
+ let requested = null;
153
+ let reap = null;
154
+ let cancellationDeadlineTimer;
155
+ let resolveCancellationDeadline;
156
+ const cancellationDeadline = new Promise((resolve) => {
157
+ resolveCancellationDeadline = resolve;
158
+ });
159
+ const requestCancel = (why) => {
160
+ requested ??= why;
161
+ if (!cancellationDeadlineTimer) {
162
+ // Relative to the FIRST cancellation trigger, not the original process
163
+ // timeout. Abort/overflow at t=0 must settle after one cancel deadline,
164
+ // never after timeout+deadline.
165
+ cancellationDeadlineTimer = setTimeout(() => resolveCancellationDeadline({ kind: "unconfirmed_wait" }), options.cancelDeadlineMs ?? AGY_PRINT_CANCEL_DEADLINE_MS);
166
+ }
167
+ if (reap || typeof pid !== "number") {
168
+ try {
169
+ child.kill("SIGKILL");
170
+ }
171
+ catch {
172
+ /* already gone */
173
+ }
174
+ return;
175
+ }
176
+ reap = (options.reap ?? reapProcessTree)({
177
+ rootPid: pid,
178
+ cooperativeSignal: "SIGKILL",
179
+ graceMs: 0,
180
+ deadlineMs: options.cancelDeadlineMs ?? AGY_PRINT_CANCEL_DEADLINE_MS,
181
+ ...(group ? { seedHandles: [group], rootIdentity: group.leader } : {}),
182
+ platform,
183
+ });
184
+ try {
185
+ child.kill("SIGKILL");
186
+ }
187
+ catch {
188
+ /* the tree reaper owns the bounded proof */
189
+ }
190
+ };
191
+ const stdout = boundedBuffer(options.maxStdoutBytes ?? AGY_PRINT_STREAM_LIMIT_BYTES, () => requestCancel("overflow"));
192
+ const stderr = boundedBuffer(options.maxStderrBytes ?? AGY_PRINT_STREAM_LIMIT_BYTES, () => requestCancel("overflow"));
193
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
194
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
195
+ let stdoutEnded = false;
196
+ let stderrEnded = false;
197
+ const stdoutEnd = new Promise((resolve) => child.stdout.once("end", () => {
198
+ stdoutEnded = true;
199
+ resolve();
200
+ }));
201
+ const stderrEnd = new Promise((resolve) => child.stderr.once("end", () => {
202
+ stderrEnded = true;
203
+ resolve();
204
+ }));
205
+ const terminal = new Promise((resolve) => {
206
+ let settled = false;
207
+ const settle = (value) => {
208
+ if (settled)
209
+ return;
210
+ settled = true;
211
+ resolve(value);
212
+ };
213
+ child.once("error", (error) => {
214
+ unregisterExitedChild();
215
+ settle({ kind: "error", error });
216
+ });
217
+ child.once("exit", (code, signal) => {
218
+ unregisterExitedChild();
219
+ settle({ kind: "exit", code, signal });
220
+ });
221
+ });
222
+ const timeout = setTimeout(() => requestCancel("timeout"), options.timeoutMs ?? AGY_PRINT_TIMEOUT_MS);
223
+ timeout.unref();
224
+ const onAbort = () => requestCancel("abort");
225
+ options.abortSignal?.addEventListener("abort", onAbort, { once: true });
226
+ // Abort can race the synchronous spawn/setup window after the pre-spawn
227
+ // check but before listener registration. AbortSignal does not replay that
228
+ // event, so sample it again after the listener is armed.
229
+ if (options.abortSignal?.aborted)
230
+ requestCancel("abort");
231
+ const outcome = await Promise.race([terminal, cancellationDeadline]);
232
+ clearTimeout(timeout);
233
+ if (cancellationDeadlineTimer)
234
+ clearTimeout(cancellationDeadlineTimer);
235
+ options.abortSignal?.removeEventListener("abort", onAbort);
236
+ // Do not await descendant-owned EOF indefinitely. Give bytes already in the
237
+ // pipes a small drain window, then detach the readers.
238
+ await Promise.race([
239
+ Promise.all([stdoutEnd, stderrEnd]).then(() => undefined),
240
+ delay(options.drainMs ?? AGY_PRINT_DRAIN_MS),
241
+ ]);
242
+ if (!stdoutEnded)
243
+ child.stdout.destroy();
244
+ if (!stderrEnded)
245
+ child.stderr.destroy();
246
+ let termination = null;
247
+ if (reap) {
248
+ try {
249
+ termination = await Promise.race([
250
+ reap,
251
+ delay(options.cancelDeadlineMs ?? AGY_PRINT_CANCEL_DEADLINE_MS).then(() => ({
252
+ state: "unconfirmed",
253
+ survivors: typeof pid === "number" ? [pid] : [],
254
+ unresolved: [],
255
+ })),
256
+ ]);
257
+ }
258
+ catch {
259
+ termination = {
260
+ state: "unconfirmed",
261
+ survivors: typeof pid === "number" ? [pid] : [],
262
+ unresolved: [],
263
+ };
264
+ }
265
+ }
266
+ const capturedOut = stdout.text();
267
+ const capturedErr = stderr.text();
268
+ const streamDetail = labelStreams(capturedErr, capturedOut, {
269
+ maxLen: 300,
270
+ transform: redactSecrets,
271
+ });
272
+ if (outcome.kind === "error") {
273
+ return {
274
+ kind: "failed",
275
+ reason: "spawn_failed",
276
+ detail: redactSecrets(outcome.error.message).slice(0, 300),
277
+ stdout: capturedOut,
278
+ stderr: capturedErr,
279
+ };
280
+ }
281
+ if (outcome.kind === "unconfirmed_wait" || termination?.state === "unconfirmed") {
282
+ return {
283
+ kind: "failed",
284
+ reason: "termination_unconfirmed",
285
+ detail: streamDetail ?? "agy print probe termination could not be confirmed",
286
+ stdout: capturedOut,
287
+ stderr: capturedErr,
288
+ };
289
+ }
290
+ if (requested) {
291
+ const reason = requested === "timeout" ? "timed_out" : requested === "abort" ? "aborted" : "output_overflow";
292
+ return {
293
+ kind: "failed",
294
+ reason,
295
+ detail: streamDetail ??
296
+ (reason === "timed_out"
297
+ ? `agy did not answer ${command} within ${(options.timeoutMs ?? AGY_PRINT_TIMEOUT_MS) / 1000}s`
298
+ : reason === "aborted"
299
+ ? "agy print probe was aborted"
300
+ : "agy print output exceeded its bounded stream limit"),
301
+ stdout: capturedOut,
302
+ stderr: capturedErr,
303
+ };
304
+ }
305
+ return {
306
+ kind: "completed",
307
+ code: outcome.code,
308
+ signal: outcome.signal,
309
+ stdout: capturedOut,
310
+ stderr: capturedErr,
311
+ };
312
+ }
313
+ const RECOGNIZED_AUTH_REJECTIONS = [
314
+ "authentication required",
315
+ "authentication failed or timed out",
316
+ "not authenticated",
317
+ "login required",
318
+ "credential revoked",
319
+ "credentials revoked",
320
+ "token revoked",
321
+ "token expired",
322
+ ];
323
+ function vendorError(envelope) {
324
+ const error = envelope["error"];
325
+ if (typeof error === "string" && error.trim())
326
+ return error.trim();
327
+ if (error && typeof error === "object" && !Array.isArray(error)) {
328
+ const message = error["message"];
329
+ if (typeof message === "string" && message.trim())
330
+ return message.trim();
331
+ }
332
+ return null;
333
+ }
334
+ function recognizedAuthRejection(detail) {
335
+ const normalized = detail.trim().toLowerCase().replaceAll(/\s+/g, " ");
336
+ return RECOGNIZED_AUTH_REJECTIONS.some((prefix) => normalized === prefix ||
337
+ normalized.startsWith(`${prefix}.`) ||
338
+ normalized.startsWith(`${prefix}:`) ||
339
+ normalized.startsWith(`${prefix} `));
340
+ }
341
+ /** Shared exit/envelope/auth classifier for doctor and quota. */
342
+ export function classifyAgyPrintResult(result) {
343
+ if (result.kind === "failed") {
344
+ return { kind: "probe_failed", detail: redactSecrets(result.detail).slice(0, 300) };
345
+ }
346
+ const streams = labelStreams(result.stderr, result.stdout, {
347
+ maxLen: 300,
348
+ transform: redactSecrets,
349
+ });
350
+ if (result.signal !== null) {
351
+ return {
352
+ kind: "probe_failed",
353
+ detail: `agy exited by signal ${result.signal}${streams ? ` (${streams})` : ""}`,
354
+ };
355
+ }
356
+ if (!result.stdout.trim()) {
357
+ return { kind: "probe_failed", detail: streams ?? "agy returned empty stdout" };
358
+ }
359
+ let envelope;
360
+ try {
361
+ envelope = JSON.parse(result.stdout);
362
+ }
363
+ catch {
364
+ return { kind: "probe_failed", detail: streams ?? "agy output was not valid JSON" };
365
+ }
366
+ if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
367
+ return { kind: "probe_failed", detail: "agy output was not a JSON object" };
368
+ }
369
+ const record = envelope;
370
+ const error = vendorError(record);
371
+ if (record["status"] === "ERROR" && error && recognizedAuthRejection(error)) {
372
+ return { kind: "unauthenticated", detail: redactSecrets(error).slice(0, 300) };
373
+ }
374
+ if (result.code !== 0 || record["status"] !== "SUCCESS") {
375
+ return {
376
+ kind: "probe_failed",
377
+ detail: (error ? redactSecrets(error).slice(0, 300) : null) ??
378
+ streams ??
379
+ `agy returned unsupported status/exit combination (status ${String(record["status"])}; exit ${String(result.code)})`,
380
+ };
381
+ }
382
+ return { kind: "success", envelope: record };
383
+ }
384
+ //# sourceMappingURL=print-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"print-command.js","sourceRoot":"","sources":["../src/print-command.ts"],"names":[],"mappings":"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"}
package/dist/profile.d.ts CHANGED
@@ -1,21 +1,12 @@
1
1
  import type { CredentialProfile, CredentialProfileStatus } from "@claudexor/schema";
2
2
  type EnvMap = Record<string, string | null | undefined>;
3
3
  export declare const AGY_BIN: () => string;
4
- /** Canonical, Claudexor-owned HOME for one named Antigravity identity. */
4
+ /** Canonical, Claudexor-owned HOME for one named Antigravity binding. */
5
5
  export declare function canonicalAgyProfileHome(locator: string): string;
6
- /**
7
- * The vendor's file-based OAuth token inside a profile HOME. agy lands on
8
- * file storage because its keyring lookup FAILS in a scoped HOME (macOS: no
9
- * login keychain in `$HOME/Library/Keychains` → `falling back to file`); the
10
- * profile mechanism therefore REQUIRES that no keychain ever be created
11
- * inside the profile HOME. Live-proven on three separate Google accounts
12
- * (PLAN §1.2a-1.2c); re-proven per AGY_VENDOR_CLI_VERSION bump because the
13
- * fallback is a vendor error path, not a documented mode (R-2').
14
- */
6
+ /** Diagnostic-only path for stale-file precedence fixtures. Credential
7
+ * readiness and routing never infer auth from this file: the effective
8
+ * transport is platform/version dependent and only the vendor probe proves it. */
15
9
  export declare function agyTokenPath(profileHome: string): string;
16
- /** The token must be a regular FILE: a directory at that path is a malformed
17
- * profile, not a login, and must refuse rather than route (Ф0 review #11). */
18
- export declare function agyTokenFilePresent(profileHome: string): boolean;
19
10
  /**
20
11
  * Exact strict-profile env: the profile HOME selects the vendor's whole
21
12
  * config root (`$HOME/.gemini/...`) — agy has no config-dir env var, HOME is
@@ -46,15 +37,17 @@ export type AgyResolvedProfileRoute = {
46
37
  refusal: string;
47
38
  };
48
39
  /**
49
- * INV-135 strict profile routing: an agy profile is exactly its
50
- * `config_dir_login` HOME with a present vendor token file, or a typed
51
- * refusal. There is NO engine-default agy credential (owner decision Л-4),
52
- * so no fallback ladder exists to fall into.
40
+ * INV-135 route construction only: validate the named binding and build the
41
+ * exact scoped environment. Credential evidence belongs exclusively to the
42
+ * vendor probe; this resolver never treats a fallback token file (or its
43
+ * absence) as an auth oracle.
53
44
  */
54
45
  export declare function resolveAgyProfileRoute(profile: Pick<CredentialProfile, "profile_id" | "credential_kind" | "isolation_locator">, specEnv?: EnvMap): AgyResolvedProfileRoute;
55
46
  export interface AgyProfileProbeDeps {
56
47
  /** Injectable live probe (tests): print-mode `/model` under the profile env. */
57
48
  runModelProbe: (env: EnvMap, abortSignal?: AbortSignal) => Promise<AgyModelProbe>;
49
+ /** Production adapter seam: prepare the profile before the vendor probe. */
50
+ prepareProfileKeychain?: (home: string) => void;
58
51
  }
59
52
  export type AgyModelProbe = {
60
53
  kind: "authenticated";
@@ -69,7 +62,7 @@ export type AgyModelProbe = {
69
62
  /**
70
63
  * Quota-free liveness probe: `agy -p "/model" --output-format json` answers
71
64
  * from the CLI without spending a turn (vendor 1.1.11+ print-mode
72
- * slash-commands). SUCCESS proves the profile's token authenticates in the
65
+ * slash-commands). SUCCESS proves the named binding authenticates in the
73
66
  * exact env its runs will spawn with (INV-067); an auth error is an honest
74
67
  * logged-out/revoked verdict, and a spawn/parse failure stays `unknown`.
75
68
  */
@@ -1 +1 @@
1
- {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAIpF,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAExD,eAAO,MAAM,OAAO,cAA+C,CAAC;AAEpE,0EAA0E;AAC1E,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;8EAC8E;AAC9E,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAMhE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW,GAAG,MAAM,CASlF;AAED,MAAM,MAAM,uBAAuB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,YAAY,GAAG,iBAAiB,GAAG,mBAAmB,CAAC,EACxF,OAAO,GAAE,MAAW,GACnB,uBAAuB,CAezB;AAED,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CACnF;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,aAAa,CAAC,CAwBxB;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,iBAAiB,EAC1B,IAAI,GAAE,mBAA6D,EACnE,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,uBAAuB,CAAC,CAgClC"}
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAMpF,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAExD,eAAO,MAAM,OAAO,cAA+C,CAAC;AAEpE,yEAAyE;AACzE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;kFAEkF;AAClF,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW,GAAG,MAAM,CASlF;AAED,MAAM,MAAM,uBAAuB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1F;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE,YAAY,GAAG,iBAAiB,GAAG,mBAAmB,CAAC,EACxF,OAAO,GAAE,MAAW,GACnB,uBAAuB,CAWzB;AAED,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAClF,4EAA4E;IAC5E,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,aAAa,CAAC,CA0BxB;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,iBAAiB,EAC1B,IAAI,GAAE,mBAA6D,EACnE,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,uBAAuB,CAAC,CAyDlC"}
package/dist/profile.js CHANGED
@@ -1,35 +1,20 @@
1
- import { statSync } from "node:fs";
2
1
  import { join } from "node:path";
3
- import { canonicalIsolationLocator, providerScrubEnv, runCapture } from "@claudexor/core";
2
+ import { canonicalIsolationLocator, providerScrubEnv } from "@claudexor/core";
4
3
  import { CredentialProfileStatus as CredentialProfileStatusSchema } from "@claudexor/schema";
5
4
  import { nowIso, redactSecrets } from "@claudexor/util";
5
+ import { isAgyProfileKeychainUnsafe } from "./keychain.js";
6
+ import { classifyAgyPrintResult, runAgyPrintCommand } from "./print-command.js";
6
7
  export const AGY_BIN = () => process.env.CLAUDEXOR_AGY_BIN || "agy";
7
- /** Canonical, Claudexor-owned HOME for one named Antigravity identity. */
8
+ /** Canonical, Claudexor-owned HOME for one named Antigravity binding. */
8
9
  export function canonicalAgyProfileHome(locator) {
9
10
  return canonicalIsolationLocator(locator, "credential profile Antigravity HOME");
10
11
  }
11
- /**
12
- * The vendor's file-based OAuth token inside a profile HOME. agy lands on
13
- * file storage because its keyring lookup FAILS in a scoped HOME (macOS: no
14
- * login keychain in `$HOME/Library/Keychains` → `falling back to file`); the
15
- * profile mechanism therefore REQUIRES that no keychain ever be created
16
- * inside the profile HOME. Live-proven on three separate Google accounts
17
- * (PLAN §1.2a-1.2c); re-proven per AGY_VENDOR_CLI_VERSION bump because the
18
- * fallback is a vendor error path, not a documented mode (R-2').
19
- */
12
+ /** Diagnostic-only path for stale-file precedence fixtures. Credential
13
+ * readiness and routing never infer auth from this file: the effective
14
+ * transport is platform/version dependent and only the vendor probe proves it. */
20
15
  export function agyTokenPath(profileHome) {
21
16
  return join(profileHome, ".gemini", "antigravity-cli", "antigravity-oauth-token");
22
17
  }
23
- /** The token must be a regular FILE: a directory at that path is a malformed
24
- * profile, not a login, and must refuse rather than route (Ф0 review #11). */
25
- export function agyTokenFilePresent(profileHome) {
26
- try {
27
- return statSync(agyTokenPath(profileHome)).isFile();
28
- }
29
- catch {
30
- return false;
31
- }
32
- }
33
18
  /**
34
19
  * Exact strict-profile env: the profile HOME selects the vendor's whole
35
20
  * config root (`$HOME/.gemini/...`) — agy has no config-dir env var, HOME is
@@ -63,10 +48,10 @@ export function agyProfileRunEnv(profileHome, specEnv = {}) {
63
48
  };
64
49
  }
65
50
  /**
66
- * INV-135 strict profile routing: an agy profile is exactly its
67
- * `config_dir_login` HOME with a present vendor token file, or a typed
68
- * refusal. There is NO engine-default agy credential (owner decision Л-4),
69
- * so no fallback ladder exists to fall into.
51
+ * INV-135 route construction only: validate the named binding and build the
52
+ * exact scoped environment. Credential evidence belongs exclusively to the
53
+ * vendor probe; this resolver never treats a fallback token file (or its
54
+ * absence) as an auth oracle.
70
55
  */
71
56
  export function resolveAgyProfileRoute(profile, specEnv = {}) {
72
57
  if (profile.credential_kind !== "config_dir_login")
@@ -75,10 +60,6 @@ export function resolveAgyProfileRoute(profile, specEnv = {}) {
75
60
  };
76
61
  try {
77
62
  const home = canonicalAgyProfileHome(profile.isolation_locator ?? "");
78
- if (!agyTokenFilePresent(home))
79
- return {
80
- refusal: `credential profile "${profile.profile_id}" has no Antigravity login in its profile HOME (run \`claudexor profiles login agy ${profile.profile_id}\` first)`,
81
- };
82
63
  return { home, env: agyProfileRunEnv(home, specEnv) };
83
64
  }
84
65
  catch (err) {
@@ -88,28 +69,28 @@ export function resolveAgyProfileRoute(profile, specEnv = {}) {
88
69
  /**
89
70
  * Quota-free liveness probe: `agy -p "/model" --output-format json` answers
90
71
  * from the CLI without spending a turn (vendor 1.1.11+ print-mode
91
- * slash-commands). SUCCESS proves the profile's token authenticates in the
72
+ * slash-commands). SUCCESS proves the named binding authenticates in the
92
73
  * exact env its runs will spawn with (INV-067); an auth error is an honest
93
74
  * logged-out/revoked verdict, and a spawn/parse failure stays `unknown`.
94
75
  */
95
76
  export async function defaultAgyModelProbe(env, abortSignal) {
96
77
  try {
97
- const r = await runCapture(AGY_BIN(), ["-p", "/model", "--output-format", "json"], {
98
- timeoutMs: 30_000,
99
- env,
100
- abortSignal,
101
- cancelSignal: "SIGTERM",
102
- });
103
- const parsed = JSON.parse(r.stdout.trim() || "{}");
104
- if (parsed?.status === "SUCCESS")
105
- return {
106
- kind: "authenticated",
107
- modelId: typeof parsed?.command?.data?.id === "string" ? parsed.command.data.id : null,
108
- };
109
- return {
110
- kind: "unauthenticated",
111
- detail: redactSecrets(String(parsed?.error ?? "authentication required")).slice(0, 300),
112
- };
78
+ const classified = classifyAgyPrintResult(await runAgyPrintCommand(AGY_BIN(), "/model", env, { abortSignal }));
79
+ if (classified.kind === "unauthenticated")
80
+ return classified;
81
+ if (classified.kind === "probe_failed")
82
+ return classified;
83
+ const command = classified.envelope["command"];
84
+ const data = command && typeof command === "object" && !Array.isArray(command)
85
+ ? command["data"]
86
+ : null;
87
+ const id = data && typeof data === "object" && !Array.isArray(data)
88
+ ? data["id"]
89
+ : null;
90
+ if (typeof id !== "string" || !id.trim()) {
91
+ return { kind: "probe_failed", detail: "agy model envelope carried no model id" };
92
+ }
93
+ return { kind: "authenticated", modelId: id };
113
94
  }
114
95
  catch (err) {
115
96
  return {
@@ -134,13 +115,35 @@ export async function probeAgyCredentialProfile(profile, deps = { runModelProbe:
134
115
  verification: "not_run",
135
116
  detail: route.refusal,
136
117
  });
118
+ let keychainSetupWarning = null;
119
+ try {
120
+ deps.prepareProfileKeychain?.(route.home);
121
+ }
122
+ catch (error) {
123
+ if (isAgyProfileKeychainUnsafe(error)) {
124
+ return CredentialProfileStatusSchema.parse({
125
+ ...base,
126
+ availability: "unavailable",
127
+ verification: "not_run",
128
+ detail: redactSecrets(error instanceof Error ? error.message : String(error)).slice(0, 300),
129
+ });
130
+ }
131
+ // A custom operational seam may model a recoverable security-tool miss.
132
+ // Keep the vendor file fallback and disclose the degraded container on
133
+ // the profile status rather than presenting a completely clean proof.
134
+ keychainSetupWarning = redactSecrets(error instanceof Error ? error.message : String(error)).slice(0, 300);
135
+ }
137
136
  const probe = await deps.runModelProbe(route.env, abortSignal);
138
137
  if (probe.kind === "authenticated")
139
138
  return CredentialProfileStatusSchema.parse({
140
139
  ...base,
141
140
  availability: "available",
142
141
  verification: "passed",
143
- detail: `Antigravity login verified in the profile HOME${probe.modelId ? ` (model ${probe.modelId})` : ""}`,
142
+ verification_source: "vendor",
143
+ detail: `Antigravity accepted the named binding${probe.modelId ? ` (model ${probe.modelId})` : ""}; whether the credential is backed by a keyring or file is not inferred` +
144
+ (keychainSetupWarning
145
+ ? `; private profile keychain setup degraded: ${keychainSetupWarning}`
146
+ : ""),
144
147
  last_verified_at: nowIso(),
145
148
  });
146
149
  if (probe.kind === "unauthenticated")
@@ -148,7 +151,8 @@ export async function probeAgyCredentialProfile(profile, deps = { runModelProbe:
148
151
  ...base,
149
152
  availability: "unavailable",
150
153
  verification: "failed",
151
- detail: `Antigravity token present but not accepted: ${probe.detail}`,
154
+ verification_source: "vendor",
155
+ detail: `Antigravity rejected the named binding credential: ${probe.detail}`,
152
156
  });
153
157
  return CredentialProfileStatusSchema.parse({
154
158
  ...base,