@gajae-code/ai 0.18.11 → 0.19.0

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.
Files changed (90) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1 -1
  3. package/dist/types/auth-broker/wire-schemas.d.ts +30 -0
  4. package/dist/types/auth-storage.d.ts +98 -13
  5. package/dist/types/bedrock-claude-cache-policy.d.ts +1 -1
  6. package/dist/types/model-cache.d.ts +6 -4
  7. package/dist/types/model-manager.d.ts +2 -0
  8. package/dist/types/model-pricing.d.ts +8 -0
  9. package/dist/types/model-thinking.d.ts +11 -0
  10. package/dist/types/openai-completions-compat.d.ts +2 -1
  11. package/dist/types/provider-models/google.d.ts +2 -0
  12. package/dist/types/providers/amazon-bedrock.d.ts +17 -2
  13. package/dist/types/providers/anthropic.d.ts +25 -0
  14. package/dist/types/providers/aws-credential-config.d.ts +5 -0
  15. package/dist/types/providers/google-auth.d.ts +7 -0
  16. package/dist/types/providers/google-gemini-headers.d.ts +11 -6
  17. package/dist/types/providers/kiro-api-key.d.ts +5 -0
  18. package/dist/types/providers/openai-chat-server-schema.d.ts +2 -1
  19. package/dist/types/providers/openai-completions.d.ts +1 -1
  20. package/dist/types/providers/openai-responses-shared.d.ts +2 -1
  21. package/dist/types/stream.d.ts +33 -1
  22. package/dist/types/types.d.ts +26 -3
  23. package/dist/types/usage.d.ts +3 -0
  24. package/dist/types/utils/discovery/antigravity.d.ts +16 -1
  25. package/dist/types/utils/idle-iterator.d.ts +3 -2
  26. package/dist/types/utils/oauth/glm-zcode.d.ts +0 -30
  27. package/dist/types/utils/oauth/types.d.ts +9 -0
  28. package/package.json +4 -4
  29. package/src/auth-broker/remote-store.ts +62 -11
  30. package/src/auth-broker/wire-schemas.ts +2 -0
  31. package/src/auth-gateway/server.ts +3 -2
  32. package/src/auth-storage.ts +908 -246
  33. package/src/bedrock-claude-cache-policy.d.ts +1 -1
  34. package/src/bedrock-claude-cache-policy.ts +2 -2
  35. package/src/model-cache.ts +45 -10
  36. package/src/model-manager.ts +18 -6
  37. package/src/model-pricing.d.ts +8 -0
  38. package/src/model-pricing.ts +87 -0
  39. package/src/model-thinking.d.ts +11 -0
  40. package/src/model-thinking.ts +97 -12
  41. package/src/models.json +3176 -1670
  42. package/src/models.ts +10 -3
  43. package/src/openai-completions-compat.d.ts +2 -1
  44. package/src/openai-completions-compat.ts +5 -0
  45. package/src/provider-models/google.ts +11 -1
  46. package/src/provider-models/openai-compat.ts +26 -0
  47. package/src/providers/amazon-bedrock.d.ts +17 -2
  48. package/src/providers/amazon-bedrock.ts +76 -16
  49. package/src/providers/anthropic.d.ts +25 -0
  50. package/src/providers/anthropic.ts +230 -18
  51. package/src/providers/aws-credential-config.d.ts +5 -0
  52. package/src/providers/aws-credential-config.ts +29 -1
  53. package/src/providers/google-auth.d.ts +7 -0
  54. package/src/providers/google-auth.ts +165 -4
  55. package/src/providers/google-gemini-headers.d.ts +11 -6
  56. package/src/providers/google-gemini-headers.ts +16 -11
  57. package/src/providers/google-vertex.ts +2 -4
  58. package/src/providers/kiro-api-key.d.ts +5 -0
  59. package/src/providers/kiro-api-key.ts +379 -68
  60. package/src/providers/ollama.ts +19 -9
  61. package/src/providers/openai-anthropic-shim.ts +1 -0
  62. package/src/providers/openai-chat-server-schema.ts +1 -1
  63. package/src/providers/openai-chat-server.ts +14 -2
  64. package/src/providers/openai-codex-responses.ts +21 -14
  65. package/src/providers/openai-completions.d.ts +1 -1
  66. package/src/providers/openai-completions.ts +65 -4
  67. package/src/providers/openai-responses-server.ts +10 -1
  68. package/src/providers/openai-responses-shared.d.ts +2 -1
  69. package/src/providers/openai-responses-shared.ts +42 -1
  70. package/src/providers/openai-responses.ts +51 -10
  71. package/src/providers/pi-native-server.ts +1 -1
  72. package/src/providers/transform-messages.ts +32 -1
  73. package/src/stream.d.ts +33 -1
  74. package/src/stream.ts +111 -17
  75. package/src/types.d.ts +26 -3
  76. package/src/types.ts +55 -2
  77. package/src/usage/claude.ts +4 -2
  78. package/src/usage.ts +3 -0
  79. package/src/utils/discovery/antigravity.ts +192 -7
  80. package/src/utils/idle-iterator.d.ts +3 -2
  81. package/src/utils/idle-iterator.ts +30 -10
  82. package/src/utils/oauth/anthropic.ts +23 -22
  83. package/src/utils/oauth/api-key-validation.ts +34 -11
  84. package/src/utils/oauth/glm-zcode.d.ts +0 -30
  85. package/src/utils/oauth/glm-zcode.ts +15 -4
  86. package/src/utils/oauth/openai-codex.ts +41 -6
  87. package/src/utils/oauth/types.d.ts +9 -0
  88. package/src/utils/oauth/types.ts +9 -0
  89. package/src/utils/parse-bind.d.ts +35 -0
  90. package/src/utils/proxy.ts +35 -6
@@ -15,6 +15,7 @@
15
15
  */
16
16
 
17
17
  import { Buffer } from "node:buffer";
18
+ import * as fs from "node:fs/promises";
18
19
  import * as path from "node:path";
19
20
  import { $credentialEnv, $envpos, getTrustedHomeDir, isEnoent, logger } from "@gajae-code/utils";
20
21
  import type { FetchImpl } from "../types";
@@ -22,6 +23,10 @@ import type { FetchImpl } from "../types";
22
23
  const OAUTH_TOKEN_URL = "https://oauth2.googleapis.com/token";
23
24
  const STS_TOKEN_URL = "https://sts.googleapis.com/v1/token";
24
25
  const METADATA_TOKEN_URL = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
26
+ const METADATA_PROJECT_ID_URL = "http://metadata.google.internal/computeMetadata/v1/project/project-id";
27
+ const METADATA_SERVICE_ACCOUNT_EMAIL_URL =
28
+ "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email";
29
+ const VERTEX_LOCATION_RE = /^[a-z0-9-]+$/;
25
30
  const CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
26
31
  const JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
27
32
  const TOKEN_EXCHANGE_GRANT = "urn:ietf:params:oauth:grant-type:token-exchange";
@@ -37,6 +42,8 @@ interface CachedToken {
37
42
  expiresAtMs: number;
38
43
  }
39
44
 
45
+ export type VertexCredentialSource = "explicit-file" | "default-file" | "metadata-server";
46
+
40
47
  interface ServiceAccountCredentials {
41
48
  type: "service_account";
42
49
  client_email: string;
@@ -115,6 +122,11 @@ interface TokenResponse {
115
122
  const tokenCache = new Map<string, CachedToken>();
116
123
  const inflight = new Map<string, Promise<string>>();
117
124
 
125
+ /** Match the region-label constraint used before Vertex interpolates a location into the request host. */
126
+ export function isValidVertexLocation(location: string): boolean {
127
+ return VERTEX_LOCATION_RE.test(location);
128
+ }
129
+
118
130
  function getRefreshSkewMs(): number {
119
131
  return $envpos("GOOGLE_VERTEX_REFRESH_SKEW_MS", 60_000);
120
132
  }
@@ -132,6 +144,82 @@ async function readJsonFile<T>(filePath: string): Promise<T | undefined> {
132
144
  }
133
145
  }
134
146
 
147
+ function isRecord(value: unknown): value is Record<string, unknown> {
148
+ return typeof value === "object" && value !== null && !Array.isArray(value);
149
+ }
150
+
151
+ function isNonEmptyString(value: unknown): value is string {
152
+ return typeof value === "string" && value.trim().length > 0;
153
+ }
154
+
155
+ function isSubjectTokenFormat(value: unknown): value is SubjectTokenFormat {
156
+ if (!isRecord(value)) return false;
157
+ if (value.type !== undefined && value.type !== "text" && value.type !== "json") return false;
158
+ if (value.subject_token_field_name !== undefined && !isNonEmptyString(value.subject_token_field_name)) return false;
159
+ return value.type !== "json" || isNonEmptyString(value.subject_token_field_name);
160
+ }
161
+
162
+ function isCredentialSource(value: unknown): value is CredentialSource {
163
+ if (!isRecord(value)) return false;
164
+ if ("file" in value) {
165
+ return isNonEmptyString(value.file) && (value.format === undefined || isSubjectTokenFormat(value.format));
166
+ }
167
+ if ("url" in value) {
168
+ if (!isNonEmptyString(value.url) || (value.format !== undefined && !isSubjectTokenFormat(value.format)))
169
+ return false;
170
+ if (value.headers === undefined) return true;
171
+ return isRecord(value.headers) && Object.values(value.headers).every(header => typeof header === "string");
172
+ }
173
+ if ("executable" in value) {
174
+ const executable = value.executable;
175
+ if (!isRecord(executable) || !isNonEmptyString(executable.command)) return false;
176
+ if (executable.timeout_millis !== undefined && typeof executable.timeout_millis !== "number") {
177
+ return false;
178
+ }
179
+ return executable.output_file === undefined || typeof executable.output_file === "string";
180
+ }
181
+ return false;
182
+ }
183
+
184
+ /** Validate the credential fields required by the ADC resolvers supported here. */
185
+ function isSupportedAdcFileCredentials(value: unknown): value is AdcFileCredentials {
186
+ if (!isRecord(value)) return false;
187
+ switch (value.type) {
188
+ case "service_account":
189
+ return isNonEmptyString(value.client_email) && isNonEmptyString(value.private_key);
190
+ case "authorized_user":
191
+ return (
192
+ isNonEmptyString(value.client_id) &&
193
+ isNonEmptyString(value.client_secret) &&
194
+ isNonEmptyString(value.refresh_token)
195
+ );
196
+ case "external_account":
197
+ return (
198
+ isNonEmptyString(value.audience) &&
199
+ isNonEmptyString(value.subject_token_type) &&
200
+ isCredentialSource(value.credential_source)
201
+ );
202
+ default:
203
+ return false;
204
+ }
205
+ }
206
+
207
+ async function isAvailableAdcFileCredentialSource(credentials: AdcFileCredentials): Promise<boolean> {
208
+ if (credentials.type !== "external_account") return true;
209
+ const source = credentials.credential_source;
210
+ if ("executable" in source) return $credentialEnv("GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES") === "1";
211
+ if ("file" in source) {
212
+ try {
213
+ if (!(await fs.stat(source.file)).isFile()) return false;
214
+ extractSubjectToken(await fs.readFile(source.file, "utf8"), source.format, `file ${source.file}`);
215
+ return true;
216
+ } catch {
217
+ return false;
218
+ }
219
+ }
220
+ return true;
221
+ }
222
+
135
223
  /** Test seam: the ADC credentials file path as resolved from trusted env. */
136
224
  export function resolveAdcCredentialsPathForTest(): string | undefined {
137
225
  return $credentialEnv("GOOGLE_APPLICATION_CREDENTIALS");
@@ -146,15 +234,25 @@ async function loadAdcCredentials(): Promise<{ source: string; creds: AdcFileCre
146
234
  // `stream.ts` already resolves the same variable through `$credentialEnv`.
147
235
  const gacPath = $credentialEnv("GOOGLE_APPLICATION_CREDENTIALS");
148
236
  if (gacPath) {
149
- const creds = await readJsonFile<AdcFileCredentials>(gacPath);
237
+ const value = await readJsonFile<unknown>(gacPath);
238
+ const creds = isSupportedAdcFileCredentials(value) ? value : undefined;
150
239
  if (!creds) {
151
- throw new Error(`GOOGLE_APPLICATION_CREDENTIALS points to a missing file: ${gacPath}`);
240
+ if (
241
+ isRecord(value) &&
242
+ typeof value.type === "string" &&
243
+ value.type !== "service_account" &&
244
+ value.type !== "authorized_user" &&
245
+ value.type !== "external_account"
246
+ ) {
247
+ throw new Error(`Unsupported Google credential type "${value.type}"`);
248
+ }
249
+ throw new Error(`GOOGLE_APPLICATION_CREDENTIALS does not contain supported credentials: ${gacPath}`);
152
250
  }
153
251
  return { source: `gac:${gacPath}`, creds };
154
252
  }
155
253
  const userPath = userAdcPath();
156
- const creds = await readJsonFile<AdcFileCredentials>(userPath);
157
- if (creds) return { source: `user:${userPath}`, creds };
254
+ const value = await readJsonFile<unknown>(userPath);
255
+ if (isSupportedAdcFileCredentials(value)) return { source: `user:${userPath}`, creds: value };
158
256
  return undefined;
159
257
  }
160
258
 
@@ -244,6 +342,69 @@ async function fetchMetadataToken(
244
342
  }
245
343
  }
246
344
 
345
+ async function hasMetadataServer(fetchImpl: FetchImpl): Promise<boolean> {
346
+ const timeout = AbortSignal.timeout(1000);
347
+ try {
348
+ const projectResponse = await fetchImpl(METADATA_PROJECT_ID_URL, {
349
+ method: "GET",
350
+ headers: { "Metadata-Flavor": "Google" },
351
+ redirect: "error",
352
+ signal: timeout,
353
+ });
354
+ if (
355
+ !projectResponse.ok ||
356
+ projectResponse.headers.get("Metadata-Flavor")?.toLowerCase() !== "google" ||
357
+ !(await projectResponse.text()).trim()
358
+ ) {
359
+ return false;
360
+ }
361
+ const identityResponse = await fetchImpl(METADATA_SERVICE_ACCOUNT_EMAIL_URL, {
362
+ method: "GET",
363
+ headers: { "Metadata-Flavor": "Google" },
364
+ redirect: "error",
365
+ signal: timeout,
366
+ });
367
+ return (
368
+ identityResponse.ok &&
369
+ identityResponse.headers.get("Metadata-Flavor")?.toLowerCase() === "google" &&
370
+ Boolean((await identityResponse.text()).trim())
371
+ );
372
+ } catch {
373
+ return false;
374
+ }
375
+ }
376
+
377
+ function classifyCredentialSource(source: string): VertexCredentialSource | undefined {
378
+ if (source === "metadata") return "metadata-server";
379
+ if (source.startsWith("gac:")) return "explicit-file";
380
+ if (source.startsWith("user:")) return "default-file";
381
+ return undefined;
382
+ }
383
+
384
+ /** Resolve the source of a currently usable ADC credential without acquiring an access token. */
385
+ export async function getVertexCredentialSource(options?: {
386
+ fetch?: FetchImpl;
387
+ }): Promise<VertexCredentialSource | undefined> {
388
+ const now = Date.now();
389
+ const skew = getRefreshSkewMs();
390
+ for (const [source, cached] of tokenCache) {
391
+ if (cached.expiresAtMs - skew > now) return classifyCredentialSource(source);
392
+ }
393
+
394
+ let adc: { source: string; creds: AdcFileCredentials } | undefined;
395
+ try {
396
+ adc = await loadAdcCredentials();
397
+ } catch {
398
+ return undefined;
399
+ }
400
+ if (adc) {
401
+ return (await isAvailableAdcFileCredentialSource(adc.creds)) ? classifyCredentialSource(adc.source) : undefined;
402
+ }
403
+
404
+ const fetchImpl = options?.fetch ?? globalThis.fetch.bind(globalThis);
405
+ return (await hasMetadataServer(fetchImpl)) ? "metadata-server" : undefined;
406
+ }
407
+
247
408
  async function postForToken(
248
409
  url: string,
249
410
  body: URLSearchParams,
@@ -29,15 +29,20 @@ export declare const ANTIGRAVITY_SYSTEM_INSTRUCTION = "You are Antigravity, a po
29
29
  /**
30
30
  * Antigravity / Cloud Code Assist user agent.
31
31
  *
32
- * Disassembly-confirmed: getUserAgentName() @ 0x5ecb1dd loads "antigravity-ide"
33
- * via LEA RDX, [RIP-0x284fc90] → 0x367b554 = "antigravity-ide"
32
+ * Field-evidence-based default: A/B/A test on Windows+Ultra (subscriber) confirmed that
33
+ * 'antigravity/cli/1.3.4 (os_type=darwin; arch=arm64; aidev_client; auth_method=oauth)'
34
+ * unlocks Gemini 4 Argon (low/medium/high) and Gemini 3.8 Flash (low/medium/high) models,
35
+ * plus Opus 5.5. Reverting to 'antigravity-ide' hides Argon models again. Pro accounts
36
+ * receive 'gemini-4-argon-disabled' with disabled:true, confirming the UA gates model
37
+ * visibility by account tier. The darwin/arm64 values were tested on Windows and worked,
38
+ * suggesting this is not platform-specific but rather a client identity string.
34
39
  *
35
- * The LS sets HTTP headers via: fmt.Sprintf("User-Agent: %s", getUserAgentName())
36
- * So the final header is: User-Agent: antigravity-ide
40
+ * Historical note: Disassembly (0x5ecb1dd) showed the IDE binary loads 'antigravity-ide';
41
+ * this appears to be outdated or a fallback for older clients.
37
42
  *
38
- * -override_user_agent flag can override this (confirmed at 0x5ecbc37).
43
+ * Overridable via PI_AI_ANTIGRAVITY_USER_AGENT environment variable.
39
44
  */
40
- export declare let getAntigravityUserAgent: () => string;
45
+ export declare function getAntigravityUserAgent(): string;
41
46
  export declare const getAntigravityRequestHeaders: () => {
42
47
  "User-Agent": string;
43
48
  };
@@ -80,20 +80,25 @@ This means you do **NOT** need to poll in a loop while waiting for messages or u
80
80
  /**
81
81
  * Antigravity / Cloud Code Assist user agent.
82
82
  *
83
- * Disassembly-confirmed: getUserAgentName() @ 0x5ecb1dd loads "antigravity-ide"
84
- * via LEA RDX, [RIP-0x284fc90] → 0x367b554 = "antigravity-ide"
83
+ * Field-evidence-based default: A/B/A test on Windows+Ultra (subscriber) confirmed that
84
+ * 'antigravity/cli/1.3.4 (os_type=darwin; arch=arm64; aidev_client; auth_method=oauth)'
85
+ * unlocks Gemini 4 Argon (low/medium/high) and Gemini 3.8 Flash (low/medium/high) models,
86
+ * plus Opus 5.5. Reverting to 'antigravity-ide' hides Argon models again. Pro accounts
87
+ * receive 'gemini-4-argon-disabled' with disabled:true, confirming the UA gates model
88
+ * visibility by account tier. The darwin/arm64 values were tested on Windows and worked,
89
+ * suggesting this is not platform-specific but rather a client identity string.
85
90
  *
86
- * The LS sets HTTP headers via: fmt.Sprintf("User-Agent: %s", getUserAgentName())
87
- * So the final header is: User-Agent: antigravity-ide
91
+ * Historical note: Disassembly (0x5ecb1dd) showed the IDE binary loads 'antigravity-ide';
92
+ * this appears to be outdated or a fallback for older clients.
88
93
  *
89
- * -override_user_agent flag can override this (confirmed at 0x5ecbc37).
94
+ * Overridable via PI_AI_ANTIGRAVITY_USER_AGENT environment variable.
90
95
  */
91
- export let getAntigravityUserAgent = () => {
92
- const override = process.env.PI_AI_ANTIGRAVITY_USER_AGENT;
93
- const userAgent = override || "antigravity-ide";
94
- getAntigravityUserAgent = () => userAgent;
95
- return userAgent;
96
- };
96
+ export function getAntigravityUserAgent(): string {
97
+ return (
98
+ process.env.PI_AI_ANTIGRAVITY_USER_AGENT ||
99
+ "antigravity/cli/1.3.4 (os_type=darwin; arch=arm64; aidev_client; auth_method=oauth)"
100
+ );
101
+ }
97
102
 
98
103
  export const getAntigravityRequestHeaders = () => ({
99
104
  "User-Agent": getAntigravityUserAgent(),
@@ -1,7 +1,7 @@
1
1
  import { $credentialEnv, $pickCredentialEnv } from "@gajae-code/utils";
2
2
  import type { Context, Model, StreamFunction } from "../types";
3
3
  import type { AssistantMessageEventStream } from "../utils/event-stream";
4
- import { getVertexAccessToken } from "./google-auth";
4
+ import { getVertexAccessToken, isValidVertexLocation } from "./google-auth";
5
5
  import {
6
6
  buildGoogleGenerateContentParams,
7
7
  type GoogleGenAIRequestPlan,
@@ -99,10 +99,8 @@ function resolveEndpointHost(location: string): string {
99
99
  * Both halves are needed: trusted resolution keeps a repository from setting it,
100
100
  * and the shape check keeps any source from turning a region into an authority.
101
101
  */
102
- const VERTEX_LOCATION_RE = /^[a-z0-9-]+$/;
103
-
104
102
  function assertVertexLocation(location: string): string {
105
- if (!VERTEX_LOCATION_RE.test(location)) {
103
+ if (!isValidVertexLocation(location)) {
106
104
  throw new Error(
107
105
  `Invalid Vertex AI location ${JSON.stringify(location)}. Expected a region label such as "us-central1" or "global".`,
108
106
  );
@@ -38,6 +38,11 @@ type KiroStreamEvent = {
38
38
  inputTokens?: number;
39
39
  outputTokens?: number;
40
40
  };
41
+ } | {
42
+ type: "completion";
43
+ data: {
44
+ stopReason: "COMPLETED" | "TOOL_USE";
45
+ };
41
46
  } | {
42
47
  type: "refusal";
43
48
  data: {