@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
package/src/models.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { registerProviderSafetyStopModel } from "./adapter-internals/provider-safety-stop";
3
- import { getOpenAIModelCost } from "./model-pricing";
3
+ import { getAnthropicCacheWriteOneHourRate, getAnthropicModelCost, getOpenAIModelCost } from "./model-pricing";
4
4
  import { isRetiredModelKey } from "./model-retirements";
5
5
  import { applyGeneratedModelPolicies, enrichModelThinking } from "./model-thinking";
6
6
  // `with { type: "file" }` is embedded by `bun build --compile` and resolves to
@@ -105,11 +105,18 @@ export function getBundledModels(provider: GeneratedProvider): Model<Api>[] {
105
105
 
106
106
  export function calculateCost<TApi extends Api>(model: Model<TApi>, usage: Usage): Usage["cost"] {
107
107
  const inputTokens = usage.input + usage.cacheRead + usage.cacheWrite;
108
- const pricing = getOpenAIModelCost(model, inputTokens) ?? model.cost;
108
+ const pricing = getAnthropicModelCost(model, inputTokens) ?? getOpenAIModelCost(model, inputTokens) ?? model.cost;
109
109
  usage.cost.input = (pricing.input / 1000000) * usage.input;
110
110
  usage.cost.output = (pricing.output / 1000000) * usage.output;
111
111
  usage.cost.cacheRead = (pricing.cacheRead / 1000000) * usage.cacheRead;
112
- usage.cost.cacheWrite = (pricing.cacheWrite / 1000000) * usage.cacheWrite;
112
+ const oneHourRate = getAnthropicCacheWriteOneHourRate(model, inputTokens);
113
+ // Bill the response's 1-hour cache-write tokens at the 1-hour rate; everything else
114
+ // (5-minute writes and any unattributed remainder) uses the model's cacheWrite rate.
115
+ const oneHourTokens =
116
+ oneHourRate === undefined ? 0 : Math.min(Math.max(usage.cttl?.ephemeral1h ?? 0, 0), usage.cacheWrite);
117
+ usage.cost.cacheWrite =
118
+ (pricing.cacheWrite / 1000000) * (usage.cacheWrite - oneHourTokens) +
119
+ ((oneHourRate ?? 0) / 1000000) * oneHourTokens;
113
120
  usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
114
121
  return usage.cost;
115
122
  }
@@ -27,13 +27,14 @@ export declare function isDirectXaiReasoningEffortModel(model: {
27
27
  api?: string;
28
28
  baseUrl?: string;
29
29
  }, resolvedBaseUrl?: string): boolean;
30
- export type ResolvedOpenAICompat = Required<Omit<OpenAICompat, "openRouterRouting" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolChoiceSupport" | "supportsResponsesSessionAffinity" | "supportsServiceTier" | "reservedToolNames">> & {
30
+ export type ResolvedOpenAICompat = Required<Omit<OpenAICompat, "openRouterRouting" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolChoiceSupport" | "supportsResponsesSessionAffinity" | "supportsServiceTier" | "supportsUltrafastTier" | "reservedToolNames">> & {
31
31
  openRouterRouting?: OpenAICompat["openRouterRouting"];
32
32
  vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
33
33
  extraBody?: OpenAICompat["extraBody"];
34
34
  toolStrictMode: ResolvedToolStrictMode;
35
35
  supportsResponsesSessionAffinity?: OpenAICompat["supportsResponsesSessionAffinity"];
36
36
  supportsServiceTier?: OpenAICompat["supportsServiceTier"];
37
+ supportsUltrafastTier?: OpenAICompat["supportsUltrafastTier"];
37
38
  /** Optional explicit capability override; resolved via deriveToolChoiceSupport. */
38
39
  toolChoiceSupport?: OpenAICompat["toolChoiceSupport"];
39
40
  };
@@ -146,6 +146,7 @@ export type ResolvedOpenAICompat = Required<
146
146
  | "toolChoiceSupport"
147
147
  | "supportsResponsesSessionAffinity"
148
148
  | "supportsServiceTier"
149
+ | "supportsUltrafastTier"
149
150
  | "reservedToolNames"
150
151
  >
151
152
  > & {
@@ -155,6 +156,7 @@ export type ResolvedOpenAICompat = Required<
155
156
  toolStrictMode: ResolvedToolStrictMode;
156
157
  supportsResponsesSessionAffinity?: OpenAICompat["supportsResponsesSessionAffinity"];
157
158
  supportsServiceTier?: OpenAICompat["supportsServiceTier"];
159
+ supportsUltrafastTier?: OpenAICompat["supportsUltrafastTier"];
158
160
  /** Optional explicit capability override; resolved via deriveToolChoiceSupport. */
159
161
  toolChoiceSupport?: OpenAICompat["toolChoiceSupport"];
160
162
  };
@@ -349,6 +351,8 @@ export function detectOpenAICompat(model: Model<"openai-completions">, resolvedB
349
351
  supportsDeveloperRole: !isNonStandard,
350
352
  sendSessionHeaders: false,
351
353
  supportsResponsesSessionAffinity: false,
354
+ supportsServiceTier: false,
355
+ supportsUltrafastTier: false,
352
356
  supportsMultipleSystemMessages: supportsMultipleSystemMessagesDefault,
353
357
  supportsReasoningEffort:
354
358
  hasAuditedReasoningEffortTransport &&
@@ -422,6 +426,7 @@ export function resolveOpenAICompat(
422
426
  ? model.compat.supportsResponsesSessionAffinity
423
427
  : undefined) ?? detected.supportsResponsesSessionAffinity,
424
428
  supportsServiceTier: model.compat.supportsServiceTier ?? detected.supportsServiceTier,
429
+ supportsUltrafastTier: model.compat.supportsUltrafastTier ?? detected.supportsUltrafastTier,
425
430
  supportsMultipleSystemMessages:
426
431
  model.compat.supportsMultipleSystemMessages ?? detected.supportsMultipleSystemMessages,
427
432
  supportsReasoningEffort: model.compat.supportsReasoningEffort ?? detected.supportsReasoningEffort,
@@ -1,5 +1,5 @@
1
1
  import type { ModelManagerOptions } from "../model-manager";
2
- import { fetchAntigravityDiscoveryModels } from "../utils/discovery/antigravity";
2
+ import { fetchAntigravityDiscoveryModels, getDefaultAntigravityUserAgent } from "../utils/discovery/antigravity";
3
3
  import { fetchGeminiModels } from "../utils/discovery/gemini";
4
4
 
5
5
  export interface GoogleModelManagerConfig {
@@ -14,11 +14,13 @@ export interface GoogleVertexModelManagerConfig {
14
14
  export interface GoogleAntigravityModelManagerConfig {
15
15
  oauthToken?: string;
16
16
  endpoint?: string;
17
+ project?: string;
17
18
  }
18
19
 
19
20
  export interface GoogleGeminiCliModelManagerConfig {
20
21
  oauthToken?: string;
21
22
  endpoint?: string;
23
+ project?: string;
22
24
  }
23
25
 
24
26
  const CLOUD_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
@@ -48,14 +50,18 @@ export function googleAntigravityModelManagerOptions(
48
50
  config?: GoogleAntigravityModelManagerConfig,
49
51
  ): ModelManagerOptions<"google-gemini-cli"> {
50
52
  const token = config?.oauthToken;
53
+ const ua = getDefaultAntigravityUserAgent();
51
54
  return {
52
55
  providerId: "google-antigravity",
56
+ discoveryUserAgent: ua,
53
57
  ...(token
54
58
  ? {
55
59
  fetchDynamicModels: () =>
56
60
  fetchAntigravityDiscoveryModels({
57
61
  token,
58
62
  endpoint: config?.endpoint,
63
+ project: config?.project,
64
+ userAgent: ua,
59
65
  }),
60
66
  }
61
67
  : undefined),
@@ -67,15 +73,19 @@ export function googleGeminiCliModelManagerOptions(
67
73
  ): ModelManagerOptions<"google-gemini-cli"> {
68
74
  const token = config?.oauthToken;
69
75
  const endpoint = config?.endpoint ?? CLOUD_CODE_ASSIST_ENDPOINT;
76
+ const ua = getDefaultAntigravityUserAgent();
70
77
  return {
71
78
  providerId: "google-gemini-cli",
79
+ discoveryUserAgent: ua,
72
80
  ...(token
73
81
  ? {
74
82
  fetchDynamicModels: async () => {
75
83
  const models = await fetchAntigravityDiscoveryModels({
76
84
  token,
77
85
  endpoint,
86
+ project: config?.project,
78
87
  targetProvider: "google-gemini-cli",
88
+ userAgent: ua,
79
89
  });
80
90
  if (models === null) {
81
91
  return null;
@@ -1316,6 +1316,31 @@ export function kiloModelManagerOptions(config?: KiloModelManagerConfig): ModelM
1316
1316
  provider: "kilo",
1317
1317
  baseUrl,
1318
1318
  apiKey,
1319
+ // Kilo serves the OpenRouter catalog shape: nested limits/modalities and
1320
+ // per-token USD string prices (GJC costs are per million tokens).
1321
+ mapModel: (entry, defaults) => {
1322
+ const topProvider = isRecord(entry.top_provider) ? entry.top_provider : undefined;
1323
+ const architecture = isRecord(entry.architecture) ? entry.architecture : undefined;
1324
+ const pricing = isRecord(entry.pricing) ? entry.pricing : undefined;
1325
+ const params = Array.isArray(entry.supported_parameters) ? entry.supported_parameters : [];
1326
+ const perMillion = (value: unknown): number => {
1327
+ const perToken = toNumber(value);
1328
+ return perToken !== undefined && perToken >= 0 ? perToken * 1_000_000 : 0;
1329
+ };
1330
+ return {
1331
+ ...defaults,
1332
+ reasoning: params.includes("reasoning"),
1333
+ input: toInputCapabilities(architecture?.input_modalities),
1334
+ cost: {
1335
+ input: perMillion(pricing?.prompt),
1336
+ output: perMillion(pricing?.completion),
1337
+ cacheRead: perMillion(pricing?.input_cache_read),
1338
+ cacheWrite: perMillion(pricing?.input_cache_write),
1339
+ },
1340
+ contextWindow: toPositiveNumber(entry.context_length, defaults.contextWindow),
1341
+ maxTokens: toPositiveNumber(topProvider?.max_completion_tokens, defaults.maxTokens),
1342
+ };
1343
+ },
1319
1344
  }),
1320
1345
  };
1321
1346
  }
@@ -2125,6 +2150,7 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
2125
2150
  ...(api === "openai-completions"
2126
2151
  ? {
2127
2152
  compat: {
2153
+ ...(reference.compat ?? {}),
2128
2154
  supportsStore: false,
2129
2155
  supportsDeveloperRole: false,
2130
2156
  supportsReasoningEffort: false,
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { parseBedrockClaudeGeneration } from "../bedrock-claude-cache-policy";
10
10
  import type { Effort } from "../model-thinking";
11
- import type { Model, StreamFunction, StreamOptions, ThinkingBudgets, Tool, ToolChoice } from "../types";
11
+ import type { AssistantMessage, CacheRetention, Model, StreamFunction, StreamOptions, ThinkingBudgets, Tool, ToolChoice } from "../types";
12
12
  export type BedrockThinkingDisplay = "summarized" | "omitted";
13
13
  export interface BedrockOptions extends StreamOptions {
14
14
  region?: string;
@@ -51,7 +51,18 @@ interface WireToolConfig {
51
51
  tools: WireToolSpec[];
52
52
  toolChoice?: WireToolChoice;
53
53
  }
54
+ interface MetadataEvent {
55
+ usage?: {
56
+ inputTokens?: number;
57
+ outputTokens?: number;
58
+ cacheReadInputTokens?: number;
59
+ cacheWriteInputTokens?: number;
60
+ totalTokens?: number;
61
+ };
62
+ }
54
63
  export declare const streamBedrock: StreamFunction<"bedrock-converse-stream">;
64
+ /** Exported for unit tests of the usage-to-cost path. */
65
+ export declare function handleMetadata(event: MetadataEvent, model: Model<"bedrock-converse-stream">, output: AssistantMessage, cacheRetention: CacheRetention): void;
55
66
  /**
56
67
  * Check if the model supports prompt caching.
57
68
  * Supported: Claude 3.5 Haiku, Claude 3.7 Sonnet, and every later Claude
@@ -71,5 +82,9 @@ export declare function stripBedrockForcedToolChoiceForRetry<T extends {
71
82
  toolConfig?: {
72
83
  toolChoice?: unknown;
73
84
  };
74
- }>(body: T): T;
85
+ additionalModelRequestFields?: Record<string, unknown>;
86
+ }>(body: T, thinking?: {
87
+ forced: Record<string, unknown> | undefined;
88
+ restored: Record<string, unknown>;
89
+ }): T;
75
90
  export declare function convertToolConfig(tools: Tool[] | undefined, toolChoice: BedrockOptions["toolChoice"]): WireToolConfig | undefined;
@@ -10,10 +10,12 @@
10
10
  import { $credentialEnv, $env, $flag, extractHttpStatusFromError, fetchWithRetry } from "@gajae-code/utils";
11
11
  import { assertAwsRegionLabel } from "../adapter-internals/aws-region";
12
12
  import { parseBedrockClaudeGeneration, supportsBedrockClaudePromptCaching } from "../bedrock-claude-cache-policy";
13
+ import { getAnthropicCacheWriteOneHourRate } from "../model-pricing";
13
14
  import type { Effort } from "../model-thinking";
14
15
  import {
15
16
  mapEffortToAnthropicAdaptiveEffort,
16
17
  requireSupportedEffort,
18
+ requiresAnthropicThinkingDisableWhenOff,
17
19
  supportsAnthropicAdaptiveThinkingDisplay as supportsAdaptiveThinkingDisplay,
18
20
  } from "../model-thinking";
19
21
  import { calculateCost } from "../models";
@@ -52,6 +54,7 @@ import { decodeEventStream } from "./aws-eventstream";
52
54
  import type { AwsCredentials } from "./aws-sigv4";
53
55
  import { signRequest } from "./aws-sigv4";
54
56
  import { transformMessages } from "./transform-messages";
57
+ import { NON_VISION_IMAGE_PLACEHOLDER } from "./vision-guard";
55
58
 
56
59
  export type BedrockThinkingDisplay = "summarized" | "omitted";
57
60
 
@@ -212,12 +215,30 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
212
215
  const resolvedToolChoice = resolveToolChoice(model, options.toolChoice);
213
216
  const toolConfig = convertToolConfig(context.tools, resolvedToolChoice.resolvedChoice);
214
217
  let additionalModelRequestFields = buildAdditionalModelRequestFields(model, options);
218
+ // Selected reasoning fields before forced-tool normalization, restored if the forced
219
+ // tool choice is later dropped on retry (Haiku 5+ only; see below).
220
+ let unforcedModelRequestFields: Record<string, unknown> | undefined;
215
221
 
216
222
  // Bedrock rejects thinking + forced tool_choice ("any" or specific tool).
217
223
  // When the resolved tool_choice forces tool use, disable thinking to avoid API errors.
218
224
  if (toolConfig?.toolChoice && additionalModelRequestFields) {
219
225
  const tc = toolConfig.toolChoice;
220
- if (tc.any || tc.tool) additionalModelRequestFields = undefined;
226
+ if ((tc.any || tc.tool) && requiresAnthropicThinkingDisableWhenOff(model.id)) {
227
+ // Haiku 5+ enables adaptive thinking when the field is omitted, which a forced
228
+ // tool choice rejects, and applies the effort to tool-call arguments too: send an
229
+ // explicit `disabled` and keep the selected effort (capped at `high` while
230
+ // thinking is disabled).
231
+ const effort = (additionalModelRequestFields.output_config as { effort?: string } | undefined)?.effort;
232
+ unforcedModelRequestFields = additionalModelRequestFields;
233
+ additionalModelRequestFields = {
234
+ thinking: { type: "disabled" },
235
+ ...(effort
236
+ ? { output_config: { effort: effort === "xhigh" || effort === "max" ? "high" : effort } }
237
+ : {}),
238
+ };
239
+ } else if (tc.any || tc.tool) {
240
+ additionalModelRequestFields = undefined;
241
+ }
221
242
  }
222
243
 
223
244
  let commandInput: ConverseStreamRequest = {
@@ -306,7 +327,12 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
306
327
  reason,
307
328
  registryKey: resolvedToolChoice.registryKey,
308
329
  });
309
- stripBedrockForcedToolChoiceForRetry(commandInput);
330
+ stripBedrockForcedToolChoiceForRetry(
331
+ commandInput,
332
+ unforcedModelRequestFields
333
+ ? { forced: additionalModelRequestFields, restored: unforcedModelRequestFields }
334
+ : undefined,
335
+ );
310
336
  const retryBodyText = JSON.stringify(commandInput);
311
337
  const retryBody = new TextEncoder().encode(retryBodyText);
312
338
  if (rawRequestDump) rawRequestDump.body = commandInput;
@@ -428,7 +454,7 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
428
454
  break;
429
455
  }
430
456
  case "metadata": {
431
- handleMetadata(payload as MetadataEvent, model, output);
457
+ handleMetadata(payload as MetadataEvent, model, output, cacheRetention);
432
458
  break;
433
459
  }
434
460
  default:
@@ -579,13 +605,28 @@ function handleContentBlockDelta(
579
605
  }
580
606
  }
581
607
 
582
- function handleMetadata(event: MetadataEvent, model: Model<"bedrock-converse-stream">, output: AssistantMessage): void {
608
+ /** Exported for unit tests of the usage-to-cost path. */
609
+ export function handleMetadata(
610
+ event: MetadataEvent,
611
+ model: Model<"bedrock-converse-stream">,
612
+ output: AssistantMessage,
613
+ cacheRetention: CacheRetention,
614
+ ): void {
583
615
  if (event.usage) {
584
616
  output.usage.input = event.usage.inputTokens || 0;
585
617
  output.usage.output = event.usage.outputTokens || 0;
586
618
  output.usage.cacheRead = event.usage.cacheReadInputTokens || 0;
587
619
  output.usage.cacheWrite = event.usage.cacheWriteInputTokens || 0;
588
620
  output.usage.totalTokens = event.usage.totalTokens || output.usage.input + output.usage.output;
621
+ // Converse reports aggregate cache writes only. Where 1-hour writes have their own rate
622
+ // (Haiku 5.5), the request's `ttl: "1h"` cache points mean every write was 1-hour.
623
+ if (
624
+ cacheRetention === "long" &&
625
+ output.usage.cacheWrite > 0 &&
626
+ getAnthropicCacheWriteOneHourRate(model, 0) !== undefined
627
+ ) {
628
+ output.usage.cttl = { ephemeral1h: output.usage.cacheWrite };
629
+ }
589
630
  calculateCost(model, output.usage);
590
631
  }
591
632
  }
@@ -673,12 +714,24 @@ function buildSystemPrompt(
673
714
  return blocks;
674
715
  }
675
716
 
717
+ function convertToolResultContent(
718
+ content: ToolResultMessage["content"],
719
+ supportsImages: boolean,
720
+ ): Array<TextBlockWire | ImageBlockWire> {
721
+ return content.map(c => {
722
+ if (c.type !== "image") return { text: c.text.toWellFormed() };
723
+ if (!supportsImages) return { text: NON_VISION_IMAGE_PLACEHOLDER };
724
+ return { image: createImageBlock(c.mimeType, c.data) };
725
+ });
726
+ }
727
+
676
728
  function convertMessages(
677
729
  context: Context,
678
730
  model: Model<"bedrock-converse-stream">,
679
731
  cacheRetention: CacheRetention,
680
732
  ): WireMessage[] {
681
733
  const result: WireMessage[] = [];
734
+ const supportsImages = model.input.includes("image");
682
735
  const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
683
736
 
684
737
  for (let i = 0; i < transformedMessages.length; i++) {
@@ -702,7 +755,11 @@ function convertMessages(
702
755
  break;
703
756
  }
704
757
  case "image":
705
- contentBlocks.push({ image: createImageBlock(c.mimeType, c.data) });
758
+ contentBlocks.push(
759
+ supportsImages
760
+ ? { image: createImageBlock(c.mimeType, c.data) }
761
+ : { text: NON_VISION_IMAGE_PLACEHOLDER },
762
+ );
706
763
  break;
707
764
  default:
708
765
  throw new Error("Unknown user content type");
@@ -772,11 +829,7 @@ function convertMessages(
772
829
  toolResults.push({
773
830
  toolResult: {
774
831
  toolUseId: normalizeToolCallId(m.toolCallId),
775
- content: m.content.map(c =>
776
- c.type === "image"
777
- ? { image: createImageBlock(c.mimeType, c.data) }
778
- : { text: c.text.toWellFormed() },
779
- ),
832
+ content: convertToolResultContent(m.content, supportsImages),
780
833
  status: m.isError ? "error" : "success",
781
834
  },
782
835
  });
@@ -787,11 +840,7 @@ function convertMessages(
787
840
  toolResults.push({
788
841
  toolResult: {
789
842
  toolUseId: normalizeToolCallId(nextMsg.toolCallId),
790
- content: nextMsg.content.map(c =>
791
- c.type === "image"
792
- ? { image: createImageBlock(c.mimeType, c.data) }
793
- : { text: c.text.toWellFormed() },
794
- ),
843
+ content: convertToolResultContent(nextMsg.content, supportsImages),
795
844
  status: nextMsg.isError ? "error" : "success",
796
845
  },
797
846
  });
@@ -820,10 +869,18 @@ function convertMessages(
820
869
  return result;
821
870
  }
822
871
 
823
- export function stripBedrockForcedToolChoiceForRetry<T extends { toolConfig?: { toolChoice?: unknown } }>(body: T): T {
872
+ export function stripBedrockForcedToolChoiceForRetry<
873
+ T extends { toolConfig?: { toolChoice?: unknown }; additionalModelRequestFields?: Record<string, unknown> },
874
+ >(body: T, thinking?: { forced: Record<string, unknown> | undefined; restored: Record<string, unknown> }): T {
824
875
  if (body.toolConfig) {
825
876
  body.toolConfig = { ...body.toolConfig, toolChoice: undefined };
826
877
  }
878
+ // The forced request replaced the selected thinking fields with a disabled block; with the
879
+ // forced constraint gone, put the selected reasoning back. A payload the caller replaced via
880
+ // onPayload (not the object this provider sent) is left untouched.
881
+ if (thinking && body.additionalModelRequestFields === thinking.forced) {
882
+ body.additionalModelRequestFields = thinking.restored;
883
+ }
827
884
  return body;
828
885
  }
829
886
 
@@ -879,6 +936,9 @@ function buildAdditionalModelRequestFields(
879
936
  options: BedrockOptions,
880
937
  ): Record<string, unknown> | undefined {
881
938
  const reasoning = options.reasoning;
939
+ if (requiresAnthropicThinkingDisableWhenOff(model.id) && (!reasoning || !model.reasoning)) {
940
+ return { thinking: { type: "disabled" } };
941
+ }
882
942
  if (!reasoning || !model.reasoning) return undefined;
883
943
 
884
944
  const mode = model.thinking?.mode;
@@ -51,6 +51,16 @@ export declare function isAnthropicThinkingBlockMutationError(error: unknown): b
51
51
  * than only the latest one.
52
52
  */
53
53
  export declare function isAnthropicThinkingSignatureInvalidError(error: unknown): boolean;
54
+ /**
55
+ * 400 shape where Anthropic cannot decode one of the request's images:
56
+ * `{"type":"invalid_request_error","message":"Could not process image"}`.
57
+ * Observed for a truncated JPEG (no end-of-image marker) that local resizing
58
+ * also failed to decode and therefore forwarded unchanged. The body names no
59
+ * image, and the image stays in history, so without omission every later
60
+ * request of the session draws the same rejection.
61
+ */
62
+ export declare function isAnthropicImageUnprocessableError(error: unknown): boolean;
63
+ export declare const ANTHROPIC_OMITTED_IMAGE_PLACEHOLDER = "[Image omitted: Anthropic could not process this image. The file may be corrupt or truncated; re-export or convert it before attaching it again.]";
54
64
  /**
55
65
  * CLIProxyAPI replaces Anthropic's rejection body wholesale instead of forwarding
56
66
  * it: the client only ever sees
@@ -107,6 +117,14 @@ export interface CpaToolAliasRestoreFailure {
107
117
  */
108
118
  export declare function parseCpaToolAliasRestoreFailure(error: unknown): CpaToolAliasRestoreFailure | undefined;
109
119
  export declare function isCpaToolAliasRestoreFailure(error: unknown): boolean;
120
+ /**
121
+ * How many breakpoints we are still willing to generate after a gateway has
122
+ * rejected a previous attempt. `explicit` mode normally emits two (a reusable
123
+ * prefix anchor on the last assistant turn plus a refresh point on the current
124
+ * user turn), so stepping down to one still caches the prefix, and only the
125
+ * final step gives caching up entirely.
126
+ */
127
+ type GeneratedCacheBudget = 2 | 1 | 0;
110
128
  export declare const claudeCodeEntrypoint = "sdk-cli";
111
129
  export declare const claudeToolPrefix: string;
112
130
  export declare const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
@@ -218,6 +236,8 @@ export type AnthropicClientOptionsResult = {
218
236
  fetchOptions?: AnthropicSdkClientOptions["fetchOptions"];
219
237
  };
220
238
  export declare function resolveGlmZcodeAnthropicBaseUrl(): string;
239
+ /** Compresses request bodies with gzip for first-party Anthropic endpoints only. */
240
+ export declare function wrapAnthropicFetchForGzip(baseFetch: FetchImpl): FetchImpl;
221
241
  export declare function isProviderRetryableError(error: unknown, provider?: string): boolean;
222
242
  export type AnthropicUsageLike = {
223
243
  cache_creation?: {
@@ -251,6 +271,11 @@ type SystemBlockOptions = {
251
271
  export declare function buildAnthropicSystemBlocks(systemPrompt: readonly string[] | undefined, options?: SystemBlockOptions): AnthropicSystemBlock[] | undefined;
252
272
  export declare function normalizeExtraBetas(betas?: string[] | string): string[];
253
273
  export declare function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): AnthropicClientOptionsResult;
274
+ type AnthropicCacheParams = MessageCreateParamsStreaming & {
275
+ cache_control?: AnthropicCacheControl;
276
+ };
277
+ type AnthropicCacheMode = "automatic" | "explicit" | "none";
278
+ export declare function applyPromptCaching(params: AnthropicCacheParams, cacheMode: AnthropicCacheMode, cacheControl?: AnthropicCacheControl, budget?: GeneratedCacheBudget): void;
254
279
  export declare function normalizeCacheControlTtlOrdering(params: MessageCreateParamsStreaming): void;
255
280
  export declare function convertAnthropicMessages(messages: Message[], model: Model<"anthropic-messages">, isOAuthToken: boolean, options?: {
256
281
  repairLatestAssistantThinking?: boolean;