@gajae-code/ai 0.15.6 → 0.16.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/types/adapter-internals/aws-region.d.ts +7 -0
  3. package/dist/types/core.d.ts +1 -0
  4. package/dist/types/index.d.ts +1 -0
  5. package/dist/types/provider-models/openai-compat.d.ts +1 -0
  6. package/dist/types/providers/anthropic.d.ts +1 -1
  7. package/dist/types/providers/cursor.d.ts +27 -1
  8. package/dist/types/providers/google-gemini-headers.d.ts +1 -1
  9. package/dist/types/providers/openai-codex-responses.d.ts +6 -0
  10. package/dist/types/utils/discovery/openai-compatible.d.ts +21 -0
  11. package/dist/types/utils/h2-fetch.d.ts +7 -0
  12. package/dist/types/utils/schema/normalize.d.ts +0 -5
  13. package/dist/types/utils/sqlite-errors.d.ts +4 -0
  14. package/package.json +3 -3
  15. package/src/adapter-internals/aws-region.d.ts +7 -0
  16. package/src/adapter-internals/aws-region.ts +14 -0
  17. package/src/auth-broker/server.ts +10 -1
  18. package/src/auth-storage.ts +14 -14
  19. package/src/core.ts +1 -0
  20. package/src/index.ts +1 -0
  21. package/src/model-thinking.ts +8 -0
  22. package/src/models.json +201 -3
  23. package/src/provider-models/openai-compat.ts +93 -8
  24. package/src/providers/amazon-bedrock.ts +5 -1
  25. package/src/providers/anthropic.d.ts +1 -1
  26. package/src/providers/anthropic.ts +1 -1
  27. package/src/providers/aws-credentials.ts +6 -0
  28. package/src/providers/cursor.d.ts +27 -1
  29. package/src/providers/cursor.ts +234 -17
  30. package/src/providers/google-gemini-headers.d.ts +1 -1
  31. package/src/providers/google-gemini-headers.ts +1 -1
  32. package/src/providers/kiro-api-key.ts +33 -8
  33. package/src/providers/kiro-codewhisperer.ts +4 -1
  34. package/src/providers/openai-codex-responses.d.ts +6 -0
  35. package/src/providers/openai-codex-responses.ts +17 -2
  36. package/src/providers/pi-native-client.ts +24 -1
  37. package/src/utils/discovery/antigravity.ts +10 -1
  38. package/src/utils/discovery/openai-compatible.ts +38 -0
  39. package/src/utils/h2-fetch.ts +10 -0
  40. package/src/utils/oauth/callback-server.ts +8 -1
  41. package/src/utils/oauth/glm-zcode.ts +1 -1
  42. package/src/utils/oauth/kiro.ts +91 -22
  43. package/src/utils/schema/dereference.ts +169 -49
  44. package/src/utils/schema/draft.ts +46 -23
  45. package/src/utils/schema/normalize.d.ts +0 -5
  46. package/src/utils/schema/normalize.ts +396 -119
  47. package/src/utils/schema/types.ts +3 -1
  48. package/src/utils/schema/zod-decontaminate.ts +83 -29
  49. package/src/utils/sqlite-errors.d.ts +4 -0
  50. package/src/utils/sqlite-errors.ts +13 -0
  51. package/src/utils/tool-choice-capability.ts +2 -3
@@ -59,15 +59,39 @@ function toInputCapabilities(value: unknown): ("text" | "image")[] {
59
59
  return supportsImage ? ["text", "image"] : ["text"];
60
60
  }
61
61
 
62
- async function fetchModelsDevPayload(fetchImpl: typeof fetch = fetch): Promise<unknown> {
63
- const response = await fetchImpl(MODELS_DEV_URL, {
64
- method: "GET",
65
- headers: { Accept: "application/json" },
66
- });
67
- if (!response.ok) {
68
- throw new Error(`models.dev fetch failed: ${response.status}`);
62
+ /**
63
+ * models.dev serves one catalog document describing every provider, and a
64
+ * discovery pass resolves several providers from it. Downloads are coalesced
65
+ * per fetch implementation so one pass transfers the catalog once instead of
66
+ * once per provider; the window is short enough that a later refresh still
67
+ * observes catalog updates.
68
+ */
69
+ const MODELS_DEV_PAYLOAD_TTL_MS = 60_000;
70
+ const modelsDevPayloadCache = new WeakMap<typeof fetch, { at: number; payload: Promise<unknown> }>();
71
+
72
+ export async function fetchModelsDevPayload(fetchImpl: typeof fetch = fetch): Promise<unknown> {
73
+ const now = Date.now();
74
+ const cached = modelsDevPayloadCache.get(fetchImpl);
75
+ if (cached && now - cached.at < MODELS_DEV_PAYLOAD_TTL_MS) return cached.payload;
76
+ const payload = (async () => {
77
+ const response = await fetchImpl(MODELS_DEV_URL, {
78
+ method: "GET",
79
+ headers: { Accept: "application/json" },
80
+ signal: AbortSignal.timeout(5_000),
81
+ });
82
+ if (!response.ok) {
83
+ throw new Error(`models.dev fetch failed: ${response.status}`);
84
+ }
85
+ return (await response.json()) as unknown;
86
+ })();
87
+ const entry = { at: now, payload };
88
+ modelsDevPayloadCache.set(fetchImpl, entry);
89
+ try {
90
+ return await payload;
91
+ } catch (error) {
92
+ if (modelsDevPayloadCache.get(fetchImpl) === entry) modelsDevPayloadCache.delete(fetchImpl);
93
+ throw error;
69
94
  }
70
- return response.json();
71
95
  }
72
96
 
73
97
  function anthropicToolChoiceCompat(modelId: string): Pick<Model<"anthropic-messages">, "compat"> {
@@ -2357,11 +2381,15 @@ const OPENCODE_GO_BASE_PATH = "https://opencode.ai/zen/go";
2357
2381
  const OPENCODE_ZEN_API_RESOLUTION = createOpenCodeApiResolution("https://opencode.ai/zen");
2358
2382
  const OPENCODE_GO_CHAT_COMPLETIONS_MODEL_IDS = [
2359
2383
  "deepseek-v4-flash",
2384
+ "deepseek-v4-flash-vision-exp",
2360
2385
  "deepseek-v4-pro",
2361
2386
  "glm-5.1",
2362
2387
  "glm-5.2",
2388
+ "glm-5.3-flash",
2363
2389
  "kimi-k2.6",
2364
2390
  "kimi-k2.7-code",
2391
+ "hy4-preview",
2392
+ "longcat-2.0",
2365
2393
  "mimo-v2.5",
2366
2394
  "mimo-v2.5-pro",
2367
2395
  ] as const;
@@ -2372,6 +2400,7 @@ const OPENCODE_GO_MESSAGES_MODEL_IDS = [
2372
2400
  "qwen3.6-plus",
2373
2401
  "qwen3.7-max",
2374
2402
  "qwen3.7-plus",
2403
+ "qwen3.8-flash",
2375
2404
  ] as const;
2376
2405
  const OPENCODE_GO_API_OVERRIDES: Readonly<Record<string, Api>> = {
2377
2406
  ...Object.fromEntries(OPENCODE_GO_CHAT_COMPLETIONS_MODEL_IDS.map(id => [id, "openai-completions"])),
@@ -2796,6 +2825,62 @@ const OPENCODE_GO_OFFICIAL_MODELS: Readonly<Record<string, OpenCodeGoOfficialMod
2796
2825
  reasoning: true,
2797
2826
  cost: { input: 0.066, output: 0.26, cacheRead: 0.029, cacheWrite: 0 },
2798
2827
  },
2828
+ "deepseek-v4-flash-vision-exp": {
2829
+ name: "DeepSeek V4 Flash Vision Exp",
2830
+ contextWindow: 1_000_000,
2831
+ maxTokens: 384_000,
2832
+ input: ["text", "image"],
2833
+ reasoning: true,
2834
+ cost: { input: 0.22, output: 0.66, cacheRead: 0.007, cacheWrite: 0 },
2835
+ },
2836
+ "glm-5.3-flash": {
2837
+ name: "GLM-5.3-Flash (2x usage)",
2838
+ contextWindow: 1_000_000,
2839
+ maxTokens: 131_072,
2840
+ input: ["text", "image"],
2841
+ reasoning: true,
2842
+ cost: { input: 0.075, output: 0.25, cacheRead: 0.015, cacheWrite: 0 },
2843
+ },
2844
+ "grok-4.6": {
2845
+ name: "Grok 4.6",
2846
+ contextWindow: 500_000,
2847
+ maxTokens: 500_000,
2848
+ input: ["text", "image"],
2849
+ reasoning: true,
2850
+ cost: { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 },
2851
+ },
2852
+ "hy4-preview": {
2853
+ name: "Hy4 preview",
2854
+ contextWindow: 1_024_000,
2855
+ maxTokens: 64_000,
2856
+ input: ["text"],
2857
+ reasoning: true,
2858
+ cost: { input: 0.834, output: 2.501, cacheRead: 0.042, cacheWrite: 0 },
2859
+ },
2860
+ "longcat-2.0": {
2861
+ name: "LongCat-2.0",
2862
+ contextWindow: 1_000_000,
2863
+ maxTokens: 131_072,
2864
+ input: ["text"],
2865
+ reasoning: true,
2866
+ cost: { input: 0.3, output: 1.2, cacheRead: 0.006, cacheWrite: 0 },
2867
+ },
2868
+ "muse-spark-1.2-contributor": {
2869
+ name: "Muse Spark 1.2 Contributor",
2870
+ contextWindow: 1_048_576,
2871
+ maxTokens: 131_072,
2872
+ input: ["text", "image"],
2873
+ reasoning: true,
2874
+ cost: { input: 0.1, output: 0.2, cacheRead: 0.002, cacheWrite: 0 },
2875
+ },
2876
+ "qwen3.8-flash": {
2877
+ name: "Qwen3.8 Flash",
2878
+ contextWindow: 1_000_000,
2879
+ maxTokens: 131_072,
2880
+ input: ["text", "image"],
2881
+ reasoning: true,
2882
+ cost: { input: 0.15, output: 0.47, cacheRead: 0.016, cacheWrite: 0.2 },
2883
+ },
2799
2884
  };
2800
2885
 
2801
2886
  function applyOpenCodeGoOfficialMetadata<TApi extends Api>(model: Model<TApi>): Model<TApi> {
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { $credentialEnv, $env, $flag, extractHttpStatusFromError, fetchWithRetry } from "@gajae-code/utils";
11
+ import { assertAwsRegionLabel } from "../adapter-internals/aws-region";
11
12
  import type { Effort } from "../model-thinking";
12
13
  import {
13
14
  mapEffortToAnthropicAdaptiveEffort,
@@ -202,9 +203,10 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
202
203
 
203
204
  const blocks = output.content as Block[];
204
205
  let rawRequestDump: RawHttpRequestDump | undefined;
205
- const region = options.region || $env.AWS_REGION || $env.AWS_DEFAULT_REGION || "us-east-1";
206
+ const region = options.region ?? $env.AWS_REGION ?? $env.AWS_DEFAULT_REGION ?? "us-east-1";
206
207
 
207
208
  try {
209
+ assertAwsRegionLabel(region);
208
210
  const cacheRetention = resolveCacheRetention(options.cacheRetention);
209
211
  const resolvedToolChoice = resolveToolChoice(model, options.toolChoice);
210
212
  const toolConfig = convertToolConfig(context.tools, resolvedToolChoice.resolvedChoice);
@@ -303,6 +305,7 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
303
305
  method: "POST",
304
306
  headers: await buildRequestHeaders(retryBody),
305
307
  body: retryBody,
308
+ redirect: "error",
306
309
  signal: options.signal,
307
310
  maxAttempts: 1,
308
311
  });
@@ -312,6 +315,7 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
312
315
  method: "POST",
313
316
  headers: requestHeaders,
314
317
  body,
318
+ redirect: "error",
315
319
  signal: options.signal,
316
320
  maxAttempts: resolveRetryBudget(options.requestMaxRetries, 4) + 1,
317
321
  });
@@ -107,7 +107,7 @@ export interface CpaToolAliasRestoreFailure {
107
107
  */
108
108
  export declare function parseCpaToolAliasRestoreFailure(error: unknown): CpaToolAliasRestoreFailure | undefined;
109
109
  export declare function isCpaToolAliasRestoreFailure(error: unknown): boolean;
110
- export declare const claudeCodeVersion = "2.1.219";
110
+ export declare const claudeCodeVersion = "2.1.257";
111
111
  export declare const claudeCodeEntrypoint = "sdk-cli";
112
112
  export declare const claudeToolPrefix: string;
113
113
  export declare const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
@@ -828,7 +828,7 @@ function getCacheControl(
828
828
  }
829
829
 
830
830
  // Stealth mode: Mimic Anthropic Code headers and tool prefixing.
831
- export const claudeCodeVersion = "2.1.219";
831
+ export const claudeCodeVersion = "2.1.257";
832
832
  export const claudeCodeEntrypoint = "sdk-cli";
833
833
  export const claudeToolPrefix: string = "proxy_";
834
834
  export const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
@@ -22,6 +22,7 @@
22
22
  import * as fs from "node:fs";
23
23
  import * as path from "node:path";
24
24
  import { $env, getTrustedHomeDir, isEnoent, logger } from "@gajae-code/utils";
25
+ import { assertAwsRegionLabel } from "../adapter-internals/aws-region";
25
26
  import {
26
27
  type AwsIniFile,
27
28
  classifyAwsProfileCapability,
@@ -155,6 +156,7 @@ async function readSsoCredentials(
155
156
  }
156
157
  }
157
158
  if (!startUrl || !ssoRegion) return undefined;
159
+ assertAwsRegionLabel(ssoRegion);
158
160
 
159
161
  const token = await loadSsoCachedToken(startUrl, sessionName);
160
162
  if (!token?.accessToken) {
@@ -172,6 +174,7 @@ async function readSsoCredentials(
172
174
  const response = await fetch(url, {
173
175
  method: "GET",
174
176
  headers: { "x-amz-sso_bearer_token": token.accessToken },
177
+ redirect: "error",
175
178
  signal,
176
179
  });
177
180
  if (!response.ok) {
@@ -411,6 +414,7 @@ async function readImdsCredentials(parentSignal: AbortSignal | undefined): Promi
411
414
  const tokenRes = await fetch(`http://${IMDS_HOST}/latest/api/token`, {
412
415
  method: "PUT",
413
416
  headers: { "x-aws-ec2-metadata-token-ttl-seconds": "21600" },
417
+ redirect: "error",
414
418
  signal,
415
419
  });
416
420
  if (!tokenRes.ok) return undefined;
@@ -418,6 +422,7 @@ async function readImdsCredentials(parentSignal: AbortSignal | undefined): Promi
418
422
 
419
423
  const roleRes = await fetch(`http://${IMDS_HOST}/latest/meta-data/iam/security-credentials/`, {
420
424
  headers: { "x-aws-ec2-metadata-token": token },
425
+ redirect: "error",
421
426
  signal,
422
427
  });
423
428
  if (!roleRes.ok) return undefined;
@@ -428,6 +433,7 @@ async function readImdsCredentials(parentSignal: AbortSignal | undefined): Promi
428
433
  `http://${IMDS_HOST}/latest/meta-data/iam/security-credentials/${encodeURIComponent(role)}`,
429
434
  {
430
435
  headers: { "x-aws-ec2-metadata-token": token },
436
+ redirect: "error",
431
437
  signal,
432
438
  },
433
439
  );
@@ -1,8 +1,9 @@
1
1
  import { type JsonValue } from "@bufbuild/protobuf";
2
- import type { CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, Model, StreamFunction, StreamOptions, ToolCall, ToolResultMessage } from "../types";
2
+ import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, Model, StreamFunction, StreamOptions, ToolCall, ToolResultMessage, Usage } from "../types";
3
3
  import { kCursorExecResolved } from "../utils/block-symbols";
4
4
  import { CURSOR_CLIENT_VERSION } from "./cursor/client-version";
5
5
  import type { CursorRule, RequestedModel_ModelParameterbytes } from "./cursor/gen/agent_pb";
6
+ import { type ConversationStateStructure } from "./cursor/gen/agent_pb";
6
7
  export declare const CURSOR_API_URL = "https://api2.cursor.sh";
7
8
  export { CURSOR_CLIENT_VERSION };
8
9
  /** Drop all cached state + blob bytes for a conversation (F15 bound + session-teardown hook). */
@@ -31,6 +32,19 @@ type ToolCallState = ToolCall & {
31
32
  kind: "mcp" | "todo_write" | "native" | "cursor-exec";
32
33
  [kCursorExecResolved]?: true;
33
34
  };
35
+ interface UsageState {
36
+ sawTokenDelta: boolean;
37
+ /**
38
+ * Latest `ConversationTokenDetails.used_tokens`: the whole conversation's
39
+ * token consumption as counted by Cursor, not this turn's output.
40
+ */
41
+ conversationUsedTokens: number;
42
+ /** Output tokens already included in the latest checkpoint snapshot. */
43
+ checkpointOutputTokens: number;
44
+ /** Whether the current stream received a checkpoint, including an explicit zero. */
45
+ hasConversationCheckpoint: boolean;
46
+ pendingCheckpoint?: ConversationStateStructure;
47
+ }
34
48
  /** Exported for tests: verifies handler is invoked with correct `this` when passed as bound. */
35
49
  export declare function resolveExecHandler<TArgs, TResult>(args: TArgs, handler: ((args: TArgs) => Promise<CursorExecHandlerResult<TResult>>) | undefined, onToolResult: CursorToolResultHandler | undefined, buildFromToolResult: (toolResult: ToolResultMessage) => TResult, buildRejected: (reason: string) => TResult, buildError: (error: string) => TResult): Promise<{
36
50
  execResult: TResult;
@@ -44,6 +58,18 @@ export declare function createCursorMessageQueueForTest(onError?: (error: unknow
44
58
  /** Exported for direct regression coverage of the JSON-safety boundary. */
45
59
  export declare function cursorJsonSafeValueForTest(value: unknown): unknown;
46
60
  export declare function buildNativeToolCallBlock(toolCall: Record<string, unknown>, callId: string, index: number): ToolCallState | null;
61
+ /**
62
+ * Cursor streams output tokens as deltas and reports whole-conversation
63
+ * consumption separately as `ConversationTokenDetails.used_tokens`. Derive
64
+ * prompt tokens from the difference so context accounting and compaction see a
65
+ * real prompt size instead of zero.
66
+ */
67
+ export declare function finalizeCursorUsage(output: AssistantMessage, usageState: UsageState): void;
68
+ /** Exposes {@link finalizeCursorUsage} for tests without a live HTTP/2 stream. */
69
+ export declare function finalizeCursorUsageForTest(usedTokens: number, outputTokens: number, options?: {
70
+ checkpointOutputTokens?: number;
71
+ hasConversationCheckpoint?: boolean;
72
+ }): Usage;
47
73
  /**
48
74
  * Build `ConversationStateStructure.rootPromptMessagesJson` blob IDs for the
49
75
  * system prompt plus prior conversation history, as JSON blobs matching