fix(cliproxy): propagate agy entitlement failure metadata

This commit is contained in:
Tam Nhu Tran
2026-04-10 14:19:02 -04:00
parent 7c4545eb1b
commit 506b61eca6
2 changed files with 63 additions and 0 deletions
+9
View File
@@ -698,6 +698,14 @@ async function fetchAvailableModels(
error: 'Invalid quota response from provider',
errorCode: 'provider_unavailable',
retryable: true,
entitlement: buildProviderEntitlementEvidence({
normalizedTier: 'unknown',
source: 'runtime_inference',
confidence: 'low',
accessState: 'unknown',
capacityState: 'temporarily_unavailable',
notes: 'Provider returned a 2xx response with an empty or invalid quota payload.',
}),
};
}
@@ -814,6 +822,7 @@ export async function fetchAccountQuota(
httpStatus: lastProjectResult.httpStatus,
needsReauth: lastProjectResult.needsReauth,
isUnprovisioned: lastProjectResult.isUnprovisioned,
entitlement: lastProjectResult.entitlement,
isExpired: authData.isExpired,
expiresAt: authData.expiresAt || undefined,
};
@@ -28,4 +28,58 @@ describe('Antigravity quota failure metadata', () => {
capacityState: 'rate_limited',
});
});
it('preserves entitlement evidence when project lookup fails before quota fetch', async () => {
const moduleId = Date.now() + Math.random();
const { fetchAccountQuota } = await import(`../../../src/cliproxy/quota-fetcher?agy-early=${moduleId}`);
const { getProviderAuthDir } = await import(
`../../../src/cliproxy/config-generator?agy-config=${moduleId}`
);
const fs = await import('node:fs');
const os = await import('node:os');
const path = await import('node:path');
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-agy-failure-'));
const originalCcsHome = process.env.CCS_HOME;
process.env.CCS_HOME = tempHome;
try {
const authDir = getProviderAuthDir('agy');
fs.mkdirSync(authDir, { recursive: true });
fs.writeFileSync(
path.join(authDir, 'antigravity-user@example.com.json'),
JSON.stringify({
type: 'antigravity',
email: 'user@example.com',
project_id: 'project-x',
access_token: 'token',
})
);
const originalFetch = globalThis.fetch;
globalThis.fetch = (async () =>
new Response(JSON.stringify({ error: { message: 'forbidden' } }), {
status: 403,
headers: { 'Content-Type': 'application/json' },
})) as typeof fetch;
try {
const result = await fetchAccountQuota('agy', 'user@example.com');
expect(result.success).toBe(false);
expect(result.entitlement).toMatchObject({
accessState: 'not_entitled',
capacityState: 'unknown',
});
} finally {
globalThis.fetch = originalFetch;
}
} finally {
if (originalCcsHome === undefined) {
delete process.env.CCS_HOME;
} else {
process.env.CCS_HOME = originalCcsHome;
}
fs.rmSync(tempHome, { recursive: true, force: true });
}
});
});