@shawnstack/quickforge 1.3.18 → 1.3.19

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 (133) hide show
  1. package/README.md +10 -10
  2. package/bin/quickforge.mjs +258 -49
  3. package/dist/assets/anthropic-Bj3HAZgj.js +39 -0
  4. package/dist/assets/azure-openai-responses-IdZZrSrI.js +1 -0
  5. package/dist/assets/github-copilot-headers-CMb2BbzT.js +1 -0
  6. package/dist/assets/google-Brt_lS1J.js +1 -0
  7. package/dist/assets/{google-shared-XhYUKiGZ.js → google-shared-CLc4ziON.js} +3 -3
  8. package/dist/assets/google-vertex-B6HsoZ34.js +1 -0
  9. package/dist/assets/{index-Dm7aEWvT.js → index-D0CVLdX_.js} +525 -489
  10. package/dist/assets/index-D0W9hAl_.css +3 -0
  11. package/dist/assets/{mistral-DxhS4Wkn.js → mistral-CenXqwPz.js} +3 -3
  12. package/dist/assets/openai-codex-responses-D9ffGwbj.js +7 -0
  13. package/dist/assets/openai-completions-eWdeSGBG.js +5 -0
  14. package/dist/assets/openai-responses-Cavpmjeu.js +1 -0
  15. package/dist/assets/{openai-responses-shared-f_P3e1nz.js → openai-responses-shared-DF3ZGaUx.js} +5 -3
  16. package/dist/assets/transform-messages-CmnxG9RB.js +1 -0
  17. package/dist/index.html +2 -2
  18. package/node_modules/@anthropic-ai/sdk/CHANGELOG.md +34 -0
  19. package/node_modules/@anthropic-ai/sdk/bin/migration-config.json +185 -0
  20. package/node_modules/@anthropic-ai/sdk/package.json +1 -1
  21. package/node_modules/@anthropic-ai/sdk/resources/beta/beta.js +4 -0
  22. package/node_modules/@anthropic-ai/sdk/resources/beta/beta.mjs +4 -0
  23. package/node_modules/@anthropic-ai/sdk/resources/beta/files.js +5 -5
  24. package/node_modules/@anthropic-ai/sdk/resources/beta/files.mjs +5 -5
  25. package/node_modules/@anthropic-ai/sdk/resources/beta/index.js +11 -9
  26. package/node_modules/@anthropic-ai/sdk/resources/beta/index.mjs +1 -0
  27. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/index.js +11 -0
  28. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/index.mjs +5 -0
  29. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memories.js +130 -0
  30. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memories.mjs +126 -0
  31. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-stores.js +145 -0
  32. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-stores.mjs +140 -0
  33. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-versions.js +81 -0
  34. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores/memory-versions.mjs +77 -0
  35. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores.js +6 -0
  36. package/node_modules/@anthropic-ai/sdk/resources/beta/memory-stores.mjs +3 -0
  37. package/node_modules/@anthropic-ai/sdk/tools/memory/node.js +12 -5
  38. package/node_modules/@anthropic-ai/sdk/tools/memory/node.mjs +12 -5
  39. package/node_modules/@anthropic-ai/sdk/version.js +1 -1
  40. package/node_modules/@anthropic-ai/sdk/version.mjs +1 -1
  41. package/node_modules/@aws-sdk/client-bedrock-runtime/package.json +5 -5
  42. package/node_modules/@aws-sdk/core/package.json +2 -2
  43. package/node_modules/@aws-sdk/credential-provider-env/package.json +2 -2
  44. package/node_modules/@aws-sdk/credential-provider-http/dist-cjs/fromHttp/fromHttp.js +12 -6
  45. package/node_modules/@aws-sdk/credential-provider-http/dist-es/fromHttp/fromHttp.js +12 -6
  46. package/node_modules/@aws-sdk/credential-provider-http/package.json +3 -2
  47. package/node_modules/@aws-sdk/credential-provider-ini/package.json +9 -9
  48. package/node_modules/@aws-sdk/credential-provider-login/package.json +3 -3
  49. package/node_modules/@aws-sdk/credential-provider-node/package.json +7 -7
  50. package/node_modules/@aws-sdk/credential-provider-process/package.json +2 -2
  51. package/node_modules/@aws-sdk/credential-provider-sso/package.json +4 -4
  52. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +3 -3
  53. package/node_modules/@aws-sdk/middleware-websocket/package.json +2 -2
  54. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
  55. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
  56. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
  57. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
  58. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
  59. package/node_modules/@aws-sdk/nested-clients/package.json +3 -3
  60. package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +1 -2
  61. package/node_modules/@aws-sdk/token-providers/package.json +3 -3
  62. package/node_modules/@aws-sdk/xml-builder/package.json +2 -2
  63. package/node_modules/@mariozechner/pi-agent-core/README.md +14 -0
  64. package/node_modules/@mariozechner/pi-agent-core/dist/agent-loop.js +9 -0
  65. package/node_modules/@mariozechner/pi-agent-core/dist/agent.js +1 -1
  66. package/node_modules/@mariozechner/pi-agent-core/package.json +2 -2
  67. package/node_modules/@mariozechner/pi-ai/README.md +20 -31
  68. package/node_modules/@mariozechner/pi-ai/dist/env-api-keys.js +7 -0
  69. package/node_modules/@mariozechner/pi-ai/dist/index.js +2 -0
  70. package/node_modules/@mariozechner/pi-ai/dist/models.generated.js +2420 -1213
  71. package/node_modules/@mariozechner/pi-ai/dist/models.js +28 -20
  72. package/node_modules/@mariozechner/pi-ai/dist/providers/amazon-bedrock.js +11 -11
  73. package/node_modules/@mariozechner/pi-ai/dist/providers/anthropic.js +43 -26
  74. package/node_modules/@mariozechner/pi-ai/dist/providers/azure-openai-responses.js +12 -6
  75. package/node_modules/@mariozechner/pi-ai/dist/providers/cloudflare.js +10 -3
  76. package/node_modules/@mariozechner/pi-ai/dist/providers/google-shared.js +4 -13
  77. package/node_modules/@mariozechner/pi-ai/dist/providers/google-vertex.js +4 -3
  78. package/node_modules/@mariozechner/pi-ai/dist/providers/google.js +4 -3
  79. package/node_modules/@mariozechner/pi-ai/dist/providers/mistral.js +8 -7
  80. package/node_modules/@mariozechner/pi-ai/dist/providers/openai-codex-responses.js +296 -41
  81. package/node_modules/@mariozechner/pi-ai/dist/providers/openai-completions.js +169 -153
  82. package/node_modules/@mariozechner/pi-ai/dist/providers/openai-responses-shared.js +14 -1
  83. package/node_modules/@mariozechner/pi-ai/dist/providers/openai-responses.js +22 -8
  84. package/node_modules/@mariozechner/pi-ai/dist/providers/register-builtins.js +0 -18
  85. package/node_modules/@mariozechner/pi-ai/dist/providers/simple-options.js +1 -0
  86. package/node_modules/@mariozechner/pi-ai/dist/session-resources.js +22 -0
  87. package/node_modules/@mariozechner/pi-ai/dist/utils/diagnostics.js +25 -0
  88. package/node_modules/@mariozechner/pi-ai/dist/utils/oauth/index.js +0 -10
  89. package/node_modules/@mariozechner/pi-ai/dist/utils/oauth/openai-codex.js +25 -14
  90. package/node_modules/@mariozechner/pi-ai/dist/utils/overflow.js +14 -0
  91. package/node_modules/@mariozechner/pi-ai/package.json +2 -6
  92. package/package.json +3 -3
  93. package/server/agent-manager.mjs +279 -12
  94. package/server/auto-compaction.mjs +1 -2
  95. package/server/conversation-compaction.mjs +0 -5
  96. package/server/index.mjs +1 -0
  97. package/server/routes/static.mjs +1 -0
  98. package/server/routes/tools.mjs +3 -1
  99. package/server/session-utils.mjs +6 -1
  100. package/server/share-store.mjs +27 -4
  101. package/server/subagents.mjs +101 -0
  102. package/server/system-prompt.mjs +30 -1
  103. package/server/tools/definitions.mjs +18 -0
  104. package/server/tools/index.mjs +1013 -911
  105. package/dist/assets/anthropic-Ck2DxOfr.js +0 -39
  106. package/dist/assets/azure-openai-responses-DIoz5q4Z.js +0 -1
  107. package/dist/assets/github-copilot-headers-CrI0CIJ7.js +0 -1
  108. package/dist/assets/google-Dau-4ve_.js +0 -1
  109. package/dist/assets/google-gemini-cli-DttMmbGb.js +0 -2
  110. package/dist/assets/google-vertex-BeukMl44.js +0 -1
  111. package/dist/assets/index-DgJVElbv.css +0 -3
  112. package/dist/assets/openai-codex-responses-X3sTzNAa.js +0 -7
  113. package/dist/assets/openai-completions-CRB9Vm0w.js +0 -5
  114. package/dist/assets/openai-responses-DXluu3oi.js +0 -1
  115. package/dist/assets/transform-messages-CV4kCtBB.js +0 -1
  116. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/LICENSE +0 -201
  117. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/README.md +0 -62
  118. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-cjs/index.js +0 -156
  119. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/constants.js +0 -2
  120. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/fromEnvSigningName.js +0 -16
  121. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/fromSso.js +0 -80
  122. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/fromStatic.js +0 -8
  123. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/getNewSsoOidcToken.js +0 -11
  124. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/getSsoOidcClient.js +0 -10
  125. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/index.js +0 -4
  126. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/nodeProvider.js +0 -5
  127. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/validateTokenExpiry.js +0 -7
  128. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/validateTokenKey.js +0 -7
  129. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/dist-es/writeSSOTokenToFile.js +0 -8
  130. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/package.json +0 -69
  131. package/node_modules/@mariozechner/pi-ai/dist/providers/google-gemini-cli.js +0 -779
  132. package/node_modules/@mariozechner/pi-ai/dist/utils/oauth/google-antigravity.js +0 -377
  133. package/node_modules/@mariozechner/pi-ai/dist/utils/oauth/google-gemini-cli.js +0 -482
@@ -27,29 +27,37 @@ export function calculateCost(model, usage) {
27
27
  usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
28
28
  return usage.cost;
29
29
  }
30
- /**
31
- * Check if a model supports xhigh thinking level.
32
- *
33
- * Supported today:
34
- * - GPT-5.2 / GPT-5.3 / GPT-5.4 / GPT-5.5 model families
35
- * - DeepSeek V4 Pro
36
- * - Opus 4.6+ models (xhigh maps to adaptive effort "max" on Anthropic-compatible providers)
37
- */
38
- export function supportsXhigh(model) {
39
- if (model.id.includes("gpt-5.2") ||
40
- model.id.includes("gpt-5.3") ||
41
- model.id.includes("gpt-5.4") ||
42
- model.id.includes("gpt-5.5") ||
43
- model.id.includes("deepseek-v4-pro")) {
30
+ const EXTENDED_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
31
+ export function getSupportedThinkingLevels(model) {
32
+ if (!model.reasoning)
33
+ return ["off"];
34
+ return EXTENDED_THINKING_LEVELS.filter((level) => {
35
+ const mapped = model.thinkingLevelMap?.[level];
36
+ if (mapped === null)
37
+ return false;
38
+ if (level === "xhigh")
39
+ return mapped !== undefined;
44
40
  return true;
41
+ });
42
+ }
43
+ export function clampThinkingLevel(model, level) {
44
+ const availableLevels = getSupportedThinkingLevels(model);
45
+ if (availableLevels.includes(level))
46
+ return level;
47
+ const requestedIndex = EXTENDED_THINKING_LEVELS.indexOf(level);
48
+ if (requestedIndex === -1)
49
+ return availableLevels[0] ?? "off";
50
+ for (let i = requestedIndex; i < EXTENDED_THINKING_LEVELS.length; i++) {
51
+ const candidate = EXTENDED_THINKING_LEVELS[i];
52
+ if (availableLevels.includes(candidate))
53
+ return candidate;
45
54
  }
46
- if (model.id.includes("opus-4-6") ||
47
- model.id.includes("opus-4.6") ||
48
- model.id.includes("opus-4-7") ||
49
- model.id.includes("opus-4.7")) {
50
- return true;
55
+ for (let i = requestedIndex - 1; i >= 0; i--) {
56
+ const candidate = EXTENDED_THINKING_LEVELS[i];
57
+ if (availableLevels.includes(candidate))
58
+ return candidate;
51
59
  }
52
- return false;
60
+ return availableLevels[0] ?? "off";
53
61
  }
54
62
  /**
55
63
  * Check if two models are equal by comparing both their id and provider.
@@ -358,8 +358,16 @@ function supportsAdaptiveThinking(modelId, modelName) {
358
358
  const candidates = getModelMatchCandidates(modelId, modelName);
359
359
  return candidates.some((s) => s.includes("opus-4-6") || s.includes("opus-4-7") || s.includes("sonnet-4-6"));
360
360
  }
361
- function mapThinkingLevelToEffort(level, modelId, modelName) {
362
- const candidates = getModelMatchCandidates(modelId, modelName);
361
+ function supportsNativeXhighEffort(model) {
362
+ const candidates = getModelMatchCandidates(model.id, model.name);
363
+ return candidates.some((s) => s.includes("opus-4-7"));
364
+ }
365
+ function mapThinkingLevelToEffort(model, level) {
366
+ if (level === "xhigh" && supportsNativeXhighEffort(model))
367
+ return "xhigh";
368
+ const mapped = level ? model.thinkingLevelMap?.[level] : undefined;
369
+ if (typeof mapped === "string")
370
+ return mapped;
363
371
  switch (level) {
364
372
  case "minimal":
365
373
  case "low":
@@ -368,14 +376,6 @@ function mapThinkingLevelToEffort(level, modelId, modelName) {
368
376
  return "medium";
369
377
  case "high":
370
378
  return "high";
371
- case "xhigh":
372
- if (candidates.some((s) => s.includes("opus-4-6"))) {
373
- return "max";
374
- }
375
- if (candidates.some((s) => s.includes("opus-4-7"))) {
376
- return "xhigh";
377
- }
378
- return "high";
379
379
  default:
380
380
  return "high";
381
381
  }
@@ -702,7 +702,7 @@ function buildAdditionalModelRequestFields(model, options) {
702
702
  const result = supportsAdaptiveThinking(model.id, model.name)
703
703
  ? {
704
704
  thinking: { type: "adaptive", ...(display !== undefined ? { display } : {}) },
705
- output_config: { effort: mapThinkingLevelToEffort(options.reasoning, model.id, model.name) },
705
+ output_config: { effort: mapThinkingLevelToEffort(model, options.reasoning) },
706
706
  }
707
707
  : (() => {
708
708
  const defaultBudgets = {
@@ -5,6 +5,7 @@ import { AssistantMessageEventStream } from "../utils/event-stream.js";
5
5
  import { headersToRecord } from "../utils/headers.js";
6
6
  import { parseJsonWithRepair, parseStreamingJson } from "../utils/json-parse.js";
7
7
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
8
+ import { resolveCloudflareBaseUrl } from "./cloudflare.js";
8
9
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
9
10
  import { adjustMaxTokensForThinking, buildBaseOptions } from "./simple-options.js";
10
11
  import { transformMessages } from "./transform-messages.js";
@@ -244,6 +245,8 @@ async function* iterateAnthropicEvents(response, signal) {
244
245
  if (!response.body) {
245
246
  throw new Error("Attempted to iterate over an Anthropic response with no body");
246
247
  }
248
+ let sawMessageStart = false;
249
+ let sawMessageEnd = false;
247
250
  for await (const sse of iterateSseMessages(response.body, signal)) {
248
251
  if (sse.event === "error") {
249
252
  throw new Error(sse.data);
@@ -252,13 +255,23 @@ async function* iterateAnthropicEvents(response, signal) {
252
255
  continue;
253
256
  }
254
257
  try {
255
- yield parseJsonWithRepair(sse.data);
258
+ const event = parseJsonWithRepair(sse.data);
259
+ if (event.type === "message_start") {
260
+ sawMessageStart = true;
261
+ }
262
+ else if (event.type === "message_stop") {
263
+ sawMessageEnd = true;
264
+ }
265
+ yield event;
256
266
  }
257
267
  catch (error) {
258
268
  const message = error instanceof Error ? error.message : String(error);
259
269
  throw new Error(`Could not parse Anthropic SSE event ${sse.event}: ${message}; data=${sse.data}; raw=${sse.raw.join("\\n")}`);
260
270
  }
261
271
  }
272
+ if (sawMessageStart && !sawMessageEnd) {
273
+ throw new Error("Anthropic stream ended before message_stop");
274
+ }
262
275
  }
263
276
  export const streamAnthropic = (model, context, options) => {
264
277
  const stream = new AssistantMessageEventStream();
@@ -523,24 +536,18 @@ function supportsAdaptiveThinking(modelId) {
523
536
  * Map ThinkingLevel to Anthropic effort levels for adaptive thinking.
524
537
  * Note: effort "max" is only valid on Opus 4.6, while Opus 4.7 supports "xhigh".
525
538
  */
526
- function mapThinkingLevelToEffort(level, modelId) {
539
+ function mapThinkingLevelToEffort(model, level) {
540
+ const mapped = level ? model.thinkingLevelMap?.[level] : undefined;
541
+ if (typeof mapped === "string")
542
+ return mapped;
527
543
  switch (level) {
528
544
  case "minimal":
529
- return "low";
530
545
  case "low":
531
546
  return "low";
532
547
  case "medium":
533
548
  return "medium";
534
549
  case "high":
535
550
  return "high";
536
- case "xhigh":
537
- if (modelId.includes("opus-4-6") || modelId.includes("opus-4.6")) {
538
- return "max";
539
- }
540
- if (modelId.includes("opus-4-7") || modelId.includes("opus-4.7")) {
541
- return "xhigh";
542
- }
543
- return "high";
544
551
  default:
545
552
  return "high";
546
553
  }
@@ -557,7 +564,7 @@ export const streamSimpleAnthropic = (model, context, options) => {
557
564
  // For Opus 4.6 and Sonnet 4.6: use adaptive thinking with effort level
558
565
  // For older models: use budget-based thinking
559
566
  if (supportsAdaptiveThinking(model.id)) {
560
- const effort = mapThinkingLevelToEffort(options.reasoning, model.id);
567
+ const effort = mapThinkingLevelToEffort(model, options.reasoning);
561
568
  return streamAnthropic(model, context, {
562
569
  ...base,
563
570
  thinkingEnabled: true,
@@ -579,15 +586,32 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
579
586
  // Adaptive thinking models (Opus 4.6, Sonnet 4.6) have interleaved thinking built-in.
580
587
  // The beta header is deprecated on Opus 4.6 and redundant on Sonnet 4.6, so skip it.
581
588
  const needsInterleavedBeta = interleavedThinking && !supportsAdaptiveThinking(model.id);
589
+ const betaFeatures = [];
590
+ if (useFineGrainedToolStreamingBeta) {
591
+ betaFeatures.push(FINE_GRAINED_TOOL_STREAMING_BETA);
592
+ }
593
+ if (needsInterleavedBeta) {
594
+ betaFeatures.push(INTERLEAVED_THINKING_BETA);
595
+ }
596
+ if (model.provider === "cloudflare-ai-gateway") {
597
+ const client = new Anthropic({
598
+ apiKey: null,
599
+ authToken: null,
600
+ baseURL: resolveCloudflareBaseUrl(model),
601
+ dangerouslyAllowBrowser: true,
602
+ defaultHeaders: mergeHeaders({
603
+ accept: "application/json",
604
+ "anthropic-dangerous-direct-browser-access": "true",
605
+ "cf-aig-authorization": `Bearer ${apiKey}`,
606
+ "x-api-key": null,
607
+ Authorization: null,
608
+ ...(betaFeatures.length > 0 ? { "anthropic-beta": betaFeatures.join(",") } : {}),
609
+ }, model.headers, optionsHeaders),
610
+ });
611
+ return { client, isOAuthToken: false };
612
+ }
582
613
  // Copilot: Bearer auth, selective betas.
583
614
  if (model.provider === "github-copilot") {
584
- const betaFeatures = [];
585
- if (useFineGrainedToolStreamingBeta) {
586
- betaFeatures.push(FINE_GRAINED_TOOL_STREAMING_BETA);
587
- }
588
- if (needsInterleavedBeta) {
589
- betaFeatures.push(INTERLEAVED_THINKING_BETA);
590
- }
591
615
  const client = new Anthropic({
592
616
  apiKey: null,
593
617
  authToken: apiKey,
@@ -601,13 +625,6 @@ function createClient(model, apiKey, interleavedThinking, useFineGrainedToolStre
601
625
  });
602
626
  return { client, isOAuthToken: false };
603
627
  }
604
- const betaFeatures = [];
605
- if (useFineGrainedToolStreamingBeta) {
606
- betaFeatures.push(FINE_GRAINED_TOOL_STREAMING_BETA);
607
- }
608
- if (needsInterleavedBeta) {
609
- betaFeatures.push(INTERLEAVED_THINKING_BETA);
610
- }
611
628
  // OAuth: Bearer auth, Claude Code identity headers
612
629
  if (isOAuthToken(apiKey)) {
613
630
  const client = new Anthropic({
@@ -1,10 +1,10 @@
1
1
  import { AzureOpenAI } from "openai";
2
2
  import { getEnvApiKey } from "../env-api-keys.js";
3
- import { supportsXhigh } from "../models.js";
3
+ import { clampThinkingLevel } from "../models.js";
4
4
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
5
5
  import { headersToRecord } from "../utils/headers.js";
6
6
  import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
7
- import { buildBaseOptions, clampReasoning } from "./simple-options.js";
7
+ import { buildBaseOptions } from "./simple-options.js";
8
8
  const DEFAULT_AZURE_API_VERSION = "v1";
9
9
  const AZURE_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode", "azure-openai-responses"]);
10
10
  function parseDeploymentNameMap(value) {
@@ -101,7 +101,8 @@ export const streamSimpleAzureOpenAIResponses = (model, context, options) => {
101
101
  throw new Error(`No API key for provider: ${model.provider}`);
102
102
  }
103
103
  const base = buildBaseOptions(model, options, apiKey);
104
- const reasoningEffort = supportsXhigh(model) ? options?.reasoning : clampReasoning(options?.reasoning);
104
+ const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
105
+ const reasoningEffort = clampedReasoning === "off" ? undefined : clampedReasoning;
105
106
  return streamAzureOpenAIResponses(model, context, {
106
107
  ...base,
107
108
  reasoningEffort,
@@ -187,14 +188,19 @@ function buildParams(model, context, options, deploymentName) {
187
188
  }
188
189
  if (model.reasoning) {
189
190
  if (options?.reasoningEffort || options?.reasoningSummary) {
191
+ const effort = options?.reasoningEffort
192
+ ? (model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort)
193
+ : "medium";
190
194
  params.reasoning = {
191
- effort: options?.reasoningEffort || "medium",
195
+ effort: effort,
192
196
  summary: options?.reasoningSummary || "auto",
193
197
  };
194
198
  params.include = ["reasoning.encrypted_content"];
195
199
  }
196
- else {
197
- params.reasoning = { effort: "none" };
200
+ else if (model.thinkingLevelMap?.off !== null) {
201
+ params.reasoning = {
202
+ effort: (model.thinkingLevelMap?.off ?? "none"),
203
+ };
198
204
  }
199
205
  }
200
206
  return params;
@@ -1,19 +1,26 @@
1
- /** Workers AI endpoint. `{CLOUDFLARE_ACCOUNT_ID}` is substituted at request time. */
1
+ /** Workers AI direct endpoint. */
2
2
  export const CLOUDFLARE_WORKERS_AI_BASE_URL = "https://api.cloudflare.com/client/v4/accounts/{CLOUDFLARE_ACCOUNT_ID}/ai/v1";
3
+ /** AI Gateway Unified API. https://developers.cloudflare.com/ai-gateway/usage/unified-api/ */
4
+ export const CLOUDFLARE_AI_GATEWAY_COMPAT_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/compat";
5
+ /** AI Gateway → OpenAI passthrough. Used until /compat supports /v1/responses. */
6
+ export const CLOUDFLARE_AI_GATEWAY_OPENAI_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/openai";
7
+ /** AI Gateway → Anthropic passthrough. */
8
+ export const CLOUDFLARE_AI_GATEWAY_ANTHROPIC_BASE_URL = "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/anthropic";
3
9
  export function isCloudflareProvider(provider) {
4
- return provider === "cloudflare-workers-ai";
10
+ return provider === "cloudflare-workers-ai" || provider === "cloudflare-ai-gateway";
5
11
  }
6
12
  /** Substitute `{VAR}` placeholders in a Cloudflare baseUrl from process.env. */
7
13
  export function resolveCloudflareBaseUrl(model) {
8
14
  const url = model.baseUrl;
9
15
  if (!url.includes("{"))
10
16
  return url;
11
- return url.replace(/\{([A-Z_][A-Z0-9_]*)\}/g, (_match, name) => {
17
+ const baseUrl = url.replace(/\{([A-Z_][A-Z0-9_]*)\}/g, (_match, name) => {
12
18
  const value = process.env[name];
13
19
  if (!value) {
14
20
  throw new Error(`${name} is required for provider ${model.provider} but is not set.`);
15
21
  }
16
22
  return value;
17
23
  });
24
+ return baseUrl;
18
25
  }
19
26
  //# sourceMappingURL=cloudflare.js.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Shared utilities for Google Generative AI and Google Cloud Code Assist providers.
2
+ * Shared utilities for Google Generative AI and Google Vertex providers.
3
3
  */
4
4
  import { FinishReason, FunctionCallingConfigMode } from "@google/genai";
5
5
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
@@ -38,10 +38,6 @@ export function retainThoughtSignature(existing, incoming) {
38
38
  }
39
39
  // Thought signatures must be base64 for Google APIs (TYPE_BYTES).
40
40
  const base64SignaturePattern = /^[A-Za-z0-9+/]+={0,2}$/;
41
- // Sentinel value that tells the Gemini API to skip thought signature validation.
42
- // Used for unsigned function call parts (e.g. replayed from providers without thought signatures).
43
- // See: https://ai.google.dev/gemini-api/docs/thought-signatures
44
- const SKIP_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
45
41
  function isValidThoughtSignature(signature) {
46
42
  if (!signature)
47
43
  return false;
@@ -121,7 +117,7 @@ export function convertMessages(model, context) {
121
117
  const isSameProviderAndModel = msg.provider === model.provider && msg.model === model.id;
122
118
  for (const block of msg.content) {
123
119
  if (block.type === "text") {
124
- // Skip empty text blocks - they can cause issues with some models (e.g. Claude via Antigravity)
120
+ // Skip empty text blocks
125
121
  if (!block.text || block.text.trim() === "")
126
122
  continue;
127
123
  const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.textSignature);
@@ -152,18 +148,13 @@ export function convertMessages(model, context) {
152
148
  }
153
149
  else if (block.type === "toolCall") {
154
150
  const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.thoughtSignature);
155
- // Gemini 3 requires thoughtSignature on all function calls when thinking mode is enabled.
156
- // Use the skip_thought_signature_validator sentinel for unsigned function calls
157
- // (e.g. replayed from providers without thought signatures like Claude via Antigravity).
158
- const isGemini3 = model.id.toLowerCase().includes("gemini-3");
159
- const effectiveSignature = thoughtSignature || (isGemini3 ? SKIP_THOUGHT_SIGNATURE : undefined);
160
151
  const part = {
161
152
  functionCall: {
162
153
  name: block.name,
163
154
  args: block.arguments ?? {},
164
155
  ...(requiresToolCallId(model.id) ? { id: block.id } : {}),
165
156
  },
166
- ...(effectiveSignature && { thoughtSignature: effectiveSignature }),
157
+ ...(thoughtSignature && { thoughtSignature }),
167
158
  };
168
159
  parts.push(part);
169
160
  }
@@ -186,7 +177,7 @@ export function convertMessages(model, context) {
186
177
  const hasImages = imageContent.length > 0;
187
178
  // Gemini 3+ models support multimodal function responses with images nested inside
188
179
  // functionResponse.parts. Claude and other non-Gemini models behind Cloud Code Assist /
189
- // Antigravity also accept this shape. Gemini < 3 still needs a separate user image turn.
180
+ // Gemini < 3 still needs a separate user image turn.
190
181
  const modelSupportsMultimodalFunctionResponse = supportsMultimodalFunctionResponse(model.id);
191
182
  // Use "output" key for success, "error" key for errors as per SDK documentation
192
183
  const responseValue = hasText ? sanitizeSurrogates(textResult) : hasImages ? "(see attached image)" : "";
@@ -1,9 +1,9 @@
1
1
  import { GoogleGenAI, ResourceScope, ThinkingLevel, } from "@google/genai";
2
- import { calculateCost } from "../models.js";
2
+ import { calculateCost, clampThinkingLevel } from "../models.js";
3
3
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
4
4
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
5
5
  import { convertMessages, convertTools, isThinkingPart, mapStopReason, mapToolChoice, retainThoughtSignature, } from "./google-shared.js";
6
- import { buildBaseOptions, clampReasoning } from "./simple-options.js";
6
+ import { buildBaseOptions } from "./simple-options.js";
7
7
  const API_VERSION = "v1";
8
8
  const GCP_VERTEX_CREDENTIALS_MARKER = "gcp-vertex-credentials";
9
9
  const THINKING_LEVEL_MAP = {
@@ -231,7 +231,8 @@ export const streamSimpleGoogleVertex = (model, context, options) => {
231
231
  thinking: { enabled: false },
232
232
  });
233
233
  }
234
- const effort = clampReasoning(options.reasoning);
234
+ const clampedReasoning = clampThinkingLevel(model, options.reasoning);
235
+ const effort = (clampedReasoning === "off" ? "high" : clampedReasoning);
235
236
  const geminiModel = model;
236
237
  if (isGemini3ProModel(geminiModel) || isGemini3FlashModel(geminiModel)) {
237
238
  return streamGoogleVertex(model, context, {
@@ -1,10 +1,10 @@
1
1
  import { GoogleGenAI, } from "@google/genai";
2
2
  import { getEnvApiKey } from "../env-api-keys.js";
3
- import { calculateCost } from "../models.js";
3
+ import { calculateCost, clampThinkingLevel } from "../models.js";
4
4
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
5
5
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
6
6
  import { convertMessages, convertTools, isThinkingPart, mapStopReason, mapToolChoice, retainThoughtSignature, } from "./google-shared.js";
7
- import { buildBaseOptions, clampReasoning } from "./simple-options.js";
7
+ import { buildBaseOptions } from "./simple-options.js";
8
8
  // Counter for generating unique tool call IDs
9
9
  let toolCallCounter = 0;
10
10
  export const streamGoogle = (model, context, options) => {
@@ -222,7 +222,8 @@ export const streamSimpleGoogle = (model, context, options) => {
222
222
  if (!options?.reasoning) {
223
223
  return streamGoogle(model, context, { ...base, thinking: { enabled: false } });
224
224
  }
225
- const effort = clampReasoning(options.reasoning);
225
+ const clampedReasoning = clampThinkingLevel(model, options.reasoning);
226
+ const effort = (clampedReasoning === "off" ? "high" : clampedReasoning);
226
227
  const googleModel = model;
227
228
  if (isGemini3ProModel(googleModel) || isGemini3FlashModel(googleModel) || isGemma4Model(googleModel)) {
228
229
  return streamGoogle(model, context, {
@@ -1,11 +1,11 @@
1
1
  import { Mistral } from "@mistralai/mistralai";
2
2
  import { getEnvApiKey } from "../env-api-keys.js";
3
- import { calculateCost } from "../models.js";
3
+ import { calculateCost, clampThinkingLevel } from "../models.js";
4
4
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
5
5
  import { shortHash } from "../utils/hash.js";
6
6
  import { parseStreamingJson } from "../utils/json-parse.js";
7
7
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
8
- import { buildBaseOptions, clampReasoning } from "./simple-options.js";
8
+ import { buildBaseOptions } from "./simple-options.js";
9
9
  import { transformMessages } from "./transform-messages.js";
10
10
  const MISTRAL_TOOL_CALL_ID_LENGTH = 9;
11
11
  const MAX_MISTRAL_ERROR_BODY_CHARS = 4000;
@@ -67,12 +67,13 @@ export const streamSimpleMistral = (model, context, options) => {
67
67
  throw new Error(`No API key for provider: ${model.provider}`);
68
68
  }
69
69
  const base = buildBaseOptions(model, options, apiKey);
70
- const reasoning = clampReasoning(options?.reasoning);
70
+ const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
71
+ const reasoning = clampedReasoning === "off" ? undefined : clampedReasoning;
71
72
  const shouldUseReasoning = model.reasoning && reasoning !== undefined;
72
73
  return streamMistral(model, context, {
73
74
  ...base,
74
75
  promptMode: shouldUseReasoning && usesPromptModeReasoning(model) ? "reasoning" : undefined,
75
- reasoningEffort: shouldUseReasoning && usesReasoningEffort(model) ? mapReasoningEffort(reasoning) : undefined,
76
+ reasoningEffort: shouldUseReasoning && usesReasoningEffort(model) ? mapReasoningEffort(model, reasoning) : undefined,
76
77
  });
77
78
  };
78
79
  function createOutput(model) {
@@ -495,13 +496,13 @@ function buildToolResultText(text, hasImages, supportsImages, isError) {
495
496
  return isError ? "[tool error] (no tool output)" : "(no tool output)";
496
497
  }
497
498
  function usesReasoningEffort(model) {
498
- return model.id === "mistral-small-2603" || model.id === "mistral-small-latest";
499
+ return model.id === "mistral-small-2603" || model.id === "mistral-small-latest" || model.id === "mistral-medium-3.5";
499
500
  }
500
501
  function usesPromptModeReasoning(model) {
501
502
  return model.reasoning && !usesReasoningEffort(model);
502
503
  }
503
- function mapReasoningEffort(_level) {
504
- return "high";
504
+ function mapReasoningEffort(model, level) {
505
+ return (model.thinkingLevelMap?.[level] ?? "high");
505
506
  }
506
507
  function mapToolChoice(choice) {
507
508
  if (!choice)