@oh-my-pi/pi-ai 17.1.2 → 17.1.4

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 (54) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +1 -1
  3. package/dist/types/auth-broker/client.d.ts +7 -1
  4. package/dist/types/auth-broker/remote-store.d.ts +4 -1
  5. package/dist/types/auth-broker/types.d.ts +6 -1
  6. package/dist/types/auth-broker/wire-schemas.d.ts +41 -0
  7. package/dist/types/auth-storage.d.ts +55 -0
  8. package/dist/types/providers/anthropic.d.ts +1 -7
  9. package/dist/types/providers/claude-code-fingerprint.d.ts +15 -0
  10. package/dist/types/providers/cursor.d.ts +30 -7
  11. package/dist/types/providers/openai-codex/request-transformer.d.ts +5 -4
  12. package/dist/types/providers/openai-codex-responses.d.ts +3 -0
  13. package/dist/types/registry/alibaba-token-plan.d.ts +11 -0
  14. package/dist/types/registry/oauth/anthropic-constants.d.ts +12 -0
  15. package/dist/types/registry/oauth/anthropic.d.ts +1 -0
  16. package/dist/types/registry/oauth/index.d.ts +1 -0
  17. package/dist/types/registry/oauth/types.d.ts +8 -0
  18. package/dist/types/types.d.ts +71 -1
  19. package/dist/types/usage/minimax-code.d.ts +1 -0
  20. package/dist/types/usage.d.ts +10 -0
  21. package/dist/types/utils/idle-iterator.d.ts +6 -4
  22. package/package.json +4 -4
  23. package/src/auth-broker/client.ts +24 -1
  24. package/src/auth-broker/remote-store.ts +12 -0
  25. package/src/auth-broker/server.ts +7 -0
  26. package/src/auth-broker/types.ts +7 -0
  27. package/src/auth-broker/wire-schemas.ts +23 -0
  28. package/src/auth-storage.ts +279 -29
  29. package/src/error/flags.ts +1 -1
  30. package/src/providers/__tests__/kimi-code-thinking.test.ts +40 -5
  31. package/src/providers/amazon-bedrock.ts +3 -4
  32. package/src/providers/anthropic.ts +98 -32
  33. package/src/providers/azure-openai-responses.ts +36 -5
  34. package/src/providers/claude-code-fingerprint.ts +19 -0
  35. package/src/providers/cursor.ts +525 -40
  36. package/src/providers/openai-codex/request-transformer.ts +27 -7
  37. package/src/providers/openai-codex-responses.ts +37 -14
  38. package/src/providers/openai-completions.ts +2 -1
  39. package/src/providers/openai-responses.ts +16 -9
  40. package/src/providers/openai-shared.ts +12 -1
  41. package/src/registry/alibaba-token-plan.ts +103 -21
  42. package/src/registry/oauth/anthropic-constants.ts +12 -0
  43. package/src/registry/oauth/anthropic.ts +3 -1
  44. package/src/registry/oauth/index.ts +1 -0
  45. package/src/registry/oauth/types.ts +8 -0
  46. package/src/stream.ts +7 -2
  47. package/src/types.ts +79 -1
  48. package/src/usage/alibaba-token-plan.ts +34 -6
  49. package/src/usage/claude.ts +7 -2
  50. package/src/usage/minimax-code.ts +280 -19
  51. package/src/usage/openai-codex.ts +61 -14
  52. package/src/usage.ts +10 -0
  53. package/src/utils/idle-iterator.ts +8 -6
  54. package/src/utils.ts +5 -5
@@ -271,7 +271,7 @@ describe("Kimi K2.7 Code thinking policy", () => {
271
271
  expect(model.compat.disableReasoningOnForcedToolChoice).toBe(true);
272
272
  });
273
273
 
274
- it("keeps the forced tool choice and omits thinking on Kimi Code's Anthropic endpoint", async () => {
274
+ it("downgrades the forced tool choice on Kimi Code's Anthropic endpoint", async () => {
275
275
  const model = getBundledModel<"openai-completions">("kimi-code", "kimi-for-coding");
276
276
  let payload: MessageCreateParamsStreaming | undefined;
277
277
  const stream = streamOpenAIAnthropicShim(
@@ -295,10 +295,45 @@ describe("Kimi K2.7 Code thinking policy", () => {
295
295
 
296
296
  await stream.result();
297
297
 
298
- // With reasoning disabled the Anthropic wire carries no thinking block,
299
- // and the forced tool choice survives (thinking yields to the choice).
300
- expect(payload?.thinking).toBeUndefined();
301
- expect(payload?.tool_choice).toEqual({ type: "tool", name: "set_title" });
298
+ // api.kimi.com keeps thinking enabled server-side no matter what the
299
+ // request carries: an omitted thinking block defaults to enabled, and an
300
+ // explicit disabled block is rejected (#3852). Either way a forced
301
+ // tool_choice 400s (`tool_choice 'specified' is incompatible with
302
+ // thinking enabled`), so the only viable path is downgrading the choice
303
+ // to auto while keeping the tool available — thinking stays on.
304
+ expect(payload?.tool_choice).toEqual({ type: "auto" });
305
+ expect(payload?.thinking).toBeDefined();
306
+ });
307
+
308
+ it("downgrades forced tool choice for every thinking-locked Kimi Code alias", async () => {
309
+ // The kimi-code catalog aliases the mandatory-thinking K2.7 Code family
310
+ // as `kimi-for-coding[-highspeed]` and K3 as `k3`; none match the native
311
+ // `kimi-k2.7-code*` id pattern, so each must be recognised explicitly.
312
+ for (const id of ["k3", "kimi-for-coding", "kimi-for-coding-highspeed"]) {
313
+ const model = getBundledModel<"openai-completions">("kimi-code", id);
314
+ let payload: MessageCreateParamsStreaming | undefined;
315
+ const stream = streamOpenAIAnthropicShim(
316
+ model,
317
+ TITLE_CONTEXT,
318
+ {
319
+ apiKey: "test-key",
320
+ maxTokens: 1024,
321
+ toolChoice: { type: "tool", name: "set_title" },
322
+ onPayload: body => {
323
+ payload = body as MessageCreateParamsStreaming;
324
+ throw new Error("stop after payload capture");
325
+ },
326
+ },
327
+ {
328
+ anthropicBaseUrl: "https://api.kimi.com/coding",
329
+ defaultFormat: "anthropic",
330
+ },
331
+ );
332
+
333
+ await stream.result();
334
+
335
+ expect(payload?.tool_choice).toEqual({ type: "auto" });
336
+ }
302
337
  });
303
338
 
304
339
  it("uses the configured Kimi base URL for Anthropic requests", async () => {
@@ -830,10 +830,9 @@ function convertMessages(
830
830
  case "thinking":
831
831
  // Skip empty thinking blocks
832
832
  if (c.thinking.trim().length === 0) continue;
833
- // Thinking blocks require a valid signature when sent as reasoningContent.
834
- // If the signature is missing (e.g., from an aborted stream), or the model
835
- // doesn't support signatures, convert to plain text instead.
836
- if (supportsThinkingSignature(model) && c.thinkingSignature) {
833
+ // A captured signature is authoritative even when the model id is an opaque ARN.
834
+ // Without one, known non-Claude families use unsigned reasoning; known Claude ids demote to text.
835
+ if (c.thinkingSignature) {
837
836
  contentBlocks.push({
838
837
  reasoningContent: {
839
838
  reasoningText: { text: c.thinking.toWellFormed(), signature: c.thinkingSignature },
@@ -63,6 +63,7 @@ import { COMBINATOR_KEYS, NO_STRICT, toolWireSchema } from "../utils/schema";
63
63
  import { spillToDescription } from "../utils/schema/spill";
64
64
  import { createSdkStreamRequestOptions } from "../utils/sdk-stream-timeout";
65
65
  import { notifyRawSseEvent } from "../utils/sse-debug";
66
+ import { isForcedToolChoice } from "../utils/tool-choice";
66
67
  import {
67
68
  AnthropicApiError,
68
69
  AnthropicConnectionTimeoutError,
@@ -84,6 +85,14 @@ import {
84
85
  type RawMessageStreamEvent,
85
86
  type TextBlockParam,
86
87
  } from "./anthropic-wire";
88
+ import {
89
+ CLAUDE_CODE_MAX_OUTPUT_TOKENS,
90
+ claudeAgentSdkVersion,
91
+ claudeClientVersion,
92
+ claudeCodeSystemInstruction,
93
+ claudeCodeVersion,
94
+ claudeToolPrefix,
95
+ } from "./claude-code-fingerprint";
87
96
  import {
88
97
  buildCopilotDynamicHeaders,
89
98
  hasCopilotVisionInput,
@@ -129,6 +138,22 @@ export function buildBetaHeader(baseBetas: readonly string[], extraBetas: readon
129
138
  return result.join(",");
130
139
  }
131
140
 
141
+ /**
142
+ * Merge an extra Anthropic beta into a caller-provided `anthropic-beta` header,
143
+ * preserving the caller's key casing and deduping the tokens. Returns a
144
+ * single-entry header record for a per-request `headers` override — used to
145
+ * attach a required beta to injected SDK clients that bypass the client-level
146
+ * beta construction.
147
+ */
148
+ function mergeAnthropicBetaHeader(callerHeaders: Record<string, string>, beta: string): Record<string, string> {
149
+ for (const key in callerHeaders) {
150
+ if (key.toLowerCase() === "anthropic-beta") {
151
+ return { [key]: buildBetaHeader(normalizeExtraBetas(callerHeaders[key]), [beta]) };
152
+ }
153
+ }
154
+ return { "anthropic-beta": beta };
155
+ }
156
+
132
157
  const midConversationSystemBeta = "mid-conversation-system-2026-04-07";
133
158
  const contextManagementBeta = "context-management-2025-06-27";
134
159
  const structuredOutputsBeta = "structured-outputs-2025-12-15";
@@ -463,16 +488,9 @@ function getCacheControl(
463
488
  };
464
489
  }
465
490
 
466
- // Stealth mode: mimic Claude Code's request fingerprint.
467
- export const claudeCodeVersion = "2.1.165";
468
- export const claudeAgentSdkVersion = "0.3.165";
469
- export const claudeClientVersion = "1.11187.4";
470
- export const claudeToolPrefix: string = "_";
471
- export const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
472
- // Claude Code caps requested output at 64k tokens even when the model ceiling is
473
- // higher (e.g. Opus 4.8 supports 128k); OAuth requests clamp to match the wire
474
- // fingerprint. API-key requests keep the full model ceiling.
475
- export const CLAUDE_CODE_MAX_OUTPUT_TOKENS = 64000;
491
+ // Stealth mode: mimic Claude Code's request fingerprint. Constants live in the
492
+ // leaf module so registry/usage consumers avoid an init cycle through this file.
493
+ export * from "./claude-code-fingerprint";
476
494
 
477
495
  export function mapStainlessOs(platform: string): "MacOS" | "Windows" | "Linux" | "FreeBSD" | `Other::${string}` {
478
496
  switch (platform.toLowerCase()) {
@@ -1840,20 +1858,19 @@ const streamAnthropicOnce = (
1840
1858
  if (options?.taskBudget && !extraBetas.includes(taskBudgetBeta)) {
1841
1859
  extraBetas.push(taskBudgetBeta);
1842
1860
  }
1843
- // `output_config.effort` ships on thinking-on requests AND on the
1844
- // thinking-off adaptive pin (adaptive-only models get effort:"low" so
1845
- // the toggle cannot 400); the beta must accompany the field in both.
1846
- // MiniMax uses `thinking.type:"adaptive"` itself as the control surface,
1847
- // so the sentinel "adaptive" value intentionally sends no output_config.
1848
- // Skip Vertex rawPredict: that adapter needs betas in the body
1849
- // (`anthropic_beta`), not as an `anthropic-beta` HTTP header, so the
1850
- // effort field is dropped from the body there too (see buildParams) and
1851
- // advertising the beta would only earn a 400 (#5614).
1861
+ // `output_config.effort` ships on thinking-on requests, explicit
1862
+ // thinking-off adaptive pins, and forced-tool adaptive pins. The beta
1863
+ // must accompany the field even when direct streamAnthropic callers omit
1864
+ // thinkingEnabled (#6589). MiniMax uses `thinking.type:"adaptive"` itself
1865
+ // as the control surface, so the sentinel "adaptive" value intentionally
1866
+ // sends no output_config. Skip Vertex rawPredict: that adapter needs betas
1867
+ // in the body (`anthropic_beta`), not as an `anthropic-beta` HTTP header,
1868
+ // so the effort field is dropped from the body there too (see buildParams)
1869
+ // and advertising the beta would only earn a 400 (#5614).
1852
1870
  const sendsAdaptiveEffortPin =
1853
- options?.thinkingEnabled === false &&
1854
- model.thinking?.mode === "anthropic-adaptive" &&
1855
- !model.compat.disableAdaptiveThinking &&
1856
- !usesAdaptiveThinkingTagOnly(model);
1871
+ isAdaptiveOnlyThinking(model) &&
1872
+ (options?.thinkingEnabled === false ||
1873
+ (model.compat.supportsForcedToolChoice && isForcedToolChoice(options?.toolChoice)));
1857
1874
  if (
1858
1875
  model.reasoning &&
1859
1876
  model.provider !== "google-vertex" &&
@@ -2064,10 +2081,27 @@ const streamAnthropicOnce = (
2064
2081
  // to zero even when no watchdog timeout is configured (the helper only
2065
2082
  // pins it alongside a timeout; a client retry budget of 5 would otherwise
2066
2083
  // multiply with PROVIDER_MAX_RETRIES into up to 66 wire attempts).
2084
+ // Injected SDK clients (`options.client`) bypass the client-level
2085
+ // `anthropic-beta` construction below, so any `output_config.effort` the
2086
+ // body carries — the adaptive-only thinking-off / forced-tool pins and
2087
+ // enabled-effort turns alike — would reach Anthropic without the required
2088
+ // `effort-2025-11-24` beta and 400. `create()` accepts per-request headers
2089
+ // (already used for the gateway web-search header), so merge the beta with
2090
+ // any caller-provided `anthropic-beta` (deduped) and attach it there. Vertex
2091
+ // never carries the effort field (dropped in buildParams), so it is unaffected.
2092
+ const injectedClientEffortHeaders =
2093
+ options?.client !== undefined &&
2094
+ (params.output_config as AnthropicOutputConfig | undefined)?.effort !== undefined
2095
+ ? mergeAnthropicBetaHeader(mergedCallerHeaders, effortBeta)
2096
+ : undefined;
2097
+ const perRequestHeaders =
2098
+ umansGatewayWebSearchHeader || injectedClientEffortHeaders
2099
+ ? { ...umansGatewayWebSearchHeader, ...injectedClientEffortHeaders }
2100
+ : undefined;
2067
2101
  const requestOptions = {
2068
2102
  ...createSdkStreamRequestOptions(requestSignal, requestTimeoutMs),
2069
2103
  maxRetries: 0,
2070
- ...(umansGatewayWebSearchHeader ? { headers: umansGatewayWebSearchHeader } : {}),
2104
+ ...(perRequestHeaders ? { headers: perRequestHeaders } : {}),
2071
2105
  };
2072
2106
  const anthropicRequest: unknown =
2073
2107
  isOAuthToken && client.beta
@@ -2960,13 +2994,32 @@ function createClient(
2960
2994
  return { client, isOAuthToken: oauthToken };
2961
2995
  }
2962
2996
 
2963
- function disableThinkingIfToolChoiceForced(params: MessageCreateParamsStreaming): void {
2997
+ function disableThinkingIfToolChoiceForced(
2998
+ params: MessageCreateParamsStreaming,
2999
+ model: Model<"anthropic-messages">,
3000
+ ): void {
2964
3001
  const toolChoice = params.tool_choice;
2965
3002
  if (!toolChoice) return;
2966
3003
  if (toolChoice.type !== "any" && toolChoice.type !== "tool") return;
2967
3004
 
2968
3005
  delete params.thinking;
2969
3006
  delete params.context_management;
3007
+
3008
+ // Adaptive-only models can't be switched off by omitting `thinking` — a bare
3009
+ // omission defaults to adaptive thinking ON, so a forced-tool turn would still
3010
+ // reason instead of calling the tool (#6589). Pin the lowest adaptive effort
3011
+ // instead of dropping it, mirroring the disable branch in buildParams. Vertex
3012
+ // rawPredict is the sole exception: it can only carry the effort beta in the
3013
+ // body (dropped there too, see buildParams), so it keeps the delete behavior.
3014
+ // The effort beta itself is attached at the request site — including per-request
3015
+ // for injected SDK clients that bypass client-level beta construction.
3016
+ if (isAdaptiveOnlyThinking(model) && model.provider !== "google-vertex") {
3017
+ const outputConfig = (params.output_config as AnthropicOutputConfig | undefined) ?? {};
3018
+ outputConfig.effort = "low";
3019
+ params.output_config = outputConfig;
3020
+ return;
3021
+ }
3022
+
2970
3023
  const outputConfig = params.output_config as AnthropicOutputConfig | undefined;
2971
3024
  if (!outputConfig) return;
2972
3025
 
@@ -3229,6 +3282,22 @@ function usesAdaptiveThinkingTagOnly(model: Model<"anthropic-messages">): boolea
3229
3282
  return thinking.efforts.length > 0;
3230
3283
  }
3231
3284
 
3285
+ /**
3286
+ * True for adaptive-only Claude models (Opus 4.6+, Sonnet 4.6+, Fable/Mythos 5)
3287
+ * that reject `thinking.type: "disabled"`. Turning thinking off on these models
3288
+ * means omitting the `thinking` field entirely and pinning the lowest adaptive
3289
+ * effort — a bare omission defaults to adaptive thinking ON. Excludes MiniMax,
3290
+ * which drives adaptive thinking through the `thinking.type: "adaptive"` tag
3291
+ * itself rather than `output_config.effort`.
3292
+ */
3293
+ function isAdaptiveOnlyThinking(model: Model<"anthropic-messages">): boolean {
3294
+ return (
3295
+ model.thinking?.mode === "anthropic-adaptive" &&
3296
+ !model.compat.disableAdaptiveThinking &&
3297
+ !usesAdaptiveThinkingTagOnly(model)
3298
+ );
3299
+ }
3300
+
3232
3301
  function resolveAnthropicAdaptiveEffort(
3233
3302
  model: Model<"anthropic-messages">,
3234
3303
  options: AnthropicOptions,
@@ -3352,17 +3421,14 @@ function buildParams(
3352
3421
  if (mode === "anthropic-budget-effort" && effort && effort !== "adaptive") outputConfigEffort = effort;
3353
3422
  }
3354
3423
  } else if (options?.thinkingEnabled === false) {
3355
- const compat = model.compat;
3356
- if (
3357
- model.thinking?.mode === "anthropic-adaptive" &&
3358
- !compat.disableAdaptiveThinking &&
3359
- !usesAdaptiveThinkingTagOnly(model)
3360
- ) {
3424
+ if (isAdaptiveOnlyThinking(model)) {
3361
3425
  // Adaptive-only Claude models (Opus 4.6+, Sonnet 4.6+, Fable/Mythos 5) reject
3362
3426
  // `thinking.type: "disabled"` — adaptive thinking cannot be switched off.
3363
3427
  // Omit the thinking field (the API defaults to adaptive) and pin the
3364
3428
  // lowest effort so "thinking off" calls stay cheap instead of failing
3365
3429
  // the request with a 400 (a hidden-thinking toggle must never break it).
3430
+ // The effort field requires the `effort-2025-11-24` beta; it is attached
3431
+ // at the request site, including per-request for injected SDK clients.
3366
3432
  outputConfigEffort = "low";
3367
3433
  } else {
3368
3434
  thinking = { type: "disabled" };
@@ -3484,7 +3550,7 @@ function buildParams(
3484
3550
  }
3485
3551
  }
3486
3552
 
3487
- disableThinkingIfToolChoiceForced(params);
3553
+ disableThinkingIfToolChoiceForced(params, model);
3488
3554
  ensureMaxTokensForThinking(params, maxOutputTokens);
3489
3555
  applyPromptCaching(params, cacheControl);
3490
3556
  enforceCacheControlLimit(params, 4);
@@ -131,9 +131,10 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
131
131
 
132
132
  try {
133
133
  const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
134
- const { url, headers } = buildAzureResponsesRequest(model, apiKey, options);
135
- let params = buildParams(model, context, options, deploymentName);
136
- const replacementPayload = await options?.onPayload?.(params, model);
134
+ const { url, headers, baseUrl } = buildAzureResponsesRequest(model, apiKey, options);
135
+ const requestModel = modelForAzureEndpoint(model, baseUrl);
136
+ let params = buildParams(requestModel, context, options, deploymentName);
137
+ const replacementPayload = await options?.onPayload?.(params, requestModel);
137
138
  if (replacementPayload !== undefined) {
138
139
  params = replacementPayload as typeof params;
139
140
  }
@@ -294,6 +295,23 @@ function resolveAzureConfig(
294
295
  };
295
296
  }
296
297
 
298
+ function modelForAzureEndpoint(
299
+ model: Model<"azure-openai-responses">,
300
+ baseUrl: string,
301
+ ): Model<"azure-openai-responses"> {
302
+ if (model.supportsComputerUseConfig !== undefined || model.supportsComputerUse !== true) return model;
303
+ try {
304
+ const url = new URL(baseUrl);
305
+ if (
306
+ url.protocol === "https:" &&
307
+ (url.hostname.endsWith(".openai.azure.com") || url.hostname === "models.inference.ai.azure.com")
308
+ ) {
309
+ return model;
310
+ }
311
+ } catch {}
312
+ return { ...model, supportsComputerUse: false };
313
+ }
314
+
297
315
  /**
298
316
  * Replicates the `AzureOpenAI` SDK client's request shape for `/responses`:
299
317
  * a string api key becomes a single `api-key` header (azure.mjs `authHeaders`;
@@ -307,7 +325,7 @@ function buildAzureResponsesRequest(
307
325
  model: Model<"azure-openai-responses">,
308
326
  apiKey: string,
309
327
  options?: AzureOpenAIResponsesOptions,
310
- ): { url: string; headers: Record<string, string> } {
328
+ ): { url: string; headers: Record<string, string>; baseUrl: string } {
311
329
  if (!apiKey) {
312
330
  const envKey = $env.AZURE_OPENAI_API_KEY;
313
331
  if (!envKey) {
@@ -329,6 +347,7 @@ function buildAzureResponsesRequest(
329
347
  return {
330
348
  url: `${baseUrl}/responses?api-version=${encodeURIComponent(apiVersion)}`,
331
349
  headers,
350
+ baseUrl,
332
351
  };
333
352
  }
334
353
 
@@ -386,8 +405,20 @@ function buildParams(
386
405
  if (serializedTools.length > 0) {
387
406
  params.tools = serializedTools;
388
407
  if (options?.toolChoice) {
389
- const toolChoice = mapToOpenAIResponsesToolChoice(options.toolChoice);
408
+ let toolChoice = mapToOpenAIResponsesToolChoice(options.toolChoice);
390
409
  const hasComputerTool = serializedTools.some(tool => tool.type === "computer");
410
+ if (toolChoice && typeof toolChoice !== "string" && toolChoice.type === "computer" && !hasComputerTool) {
411
+ const computer = context.tools.find(tool => tool.native?.type === "computer");
412
+ if (computer && serializedTools.some(tool => tool.type === "function" && tool.name === computer.name)) {
413
+ toolChoice = { type: "function", name: computer.name };
414
+ }
415
+ }
416
+ if (toolChoice && typeof toolChoice !== "string" && toolChoice.type === "function" && hasComputerTool) {
417
+ const computer = context.tools.find(tool => tool.native?.type === "computer");
418
+ if (computer?.name === toolChoice.name) {
419
+ toolChoice = { type: "computer" };
420
+ }
421
+ }
391
422
  if (
392
423
  toolChoice &&
393
424
  (typeof toolChoice === "string" ||
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Claude Code stealth-fingerprint constants, kept in a leaf module so
3
+ * fingerprint consumers outside the provider (`registry/oauth/anthropic`,
4
+ * `usage/claude`) don't import the heavy `providers/anthropic` module.
5
+ * That import edge was a live init cycle: `providers/anthropic` → `stream` →
6
+ * `registry` → `registry/oauth/anthropic` → back into the still-initializing
7
+ * provider module, which threw a TDZ ReferenceError whenever
8
+ * `providers/anthropic` was the first module loaded.
9
+ */
10
+
11
+ export const claudeCodeVersion = "2.1.165";
12
+ export const claudeAgentSdkVersion = "0.3.165";
13
+ export const claudeClientVersion = "1.11187.4";
14
+ export const claudeToolPrefix: string = "_";
15
+ export const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
16
+ // Claude Code caps requested output at 64k tokens even when the model ceiling is
17
+ // higher (e.g. Opus 4.8 supports 128k); OAuth requests clamp to match the wire
18
+ // fingerprint. API-key requests keep the full model ceiling.
19
+ export const CLAUDE_CODE_MAX_OUTPUT_TOKENS = 64000;