@earendil-works/pi-ai 1.3.0 → 1.5.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 (304) hide show
  1. package/README.md +113 -38
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +66 -81
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  6. package/dist/api/azure-openai-responses.js +8 -10
  7. package/dist/api/azure-openai-responses.js.map +1 -1
  8. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  9. package/dist/api/bedrock-converse-stream.js +137 -103
  10. package/dist/api/bedrock-converse-stream.js.map +1 -1
  11. package/dist/api/classifier-shared.d.ts.map +1 -1
  12. package/dist/api/classifier-shared.js +4 -3
  13. package/dist/api/classifier-shared.js.map +1 -1
  14. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  15. package/dist/api/cloudflare-workers-ai-system-one.js +8 -0
  16. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  17. package/dist/api/constrained-sampling.d.ts +15 -1
  18. package/dist/api/constrained-sampling.d.ts.map +1 -1
  19. package/dist/api/constrained-sampling.js +51 -18
  20. package/dist/api/constrained-sampling.js.map +1 -1
  21. package/dist/api/google-generative-ai.d.ts.map +1 -1
  22. package/dist/api/google-generative-ai.js +4 -10
  23. package/dist/api/google-generative-ai.js.map +1 -1
  24. package/dist/api/google-shared.d.ts +2 -1
  25. package/dist/api/google-shared.d.ts.map +1 -1
  26. package/dist/api/google-shared.js +58 -3
  27. package/dist/api/google-shared.js.map +1 -1
  28. package/dist/api/google-vertex.d.ts.map +1 -1
  29. package/dist/api/google-vertex.js +9 -15
  30. package/dist/api/google-vertex.js.map +1 -1
  31. package/dist/api/lazy.d.ts +7 -2
  32. package/dist/api/lazy.d.ts.map +1 -1
  33. package/dist/api/lazy.js +24 -5
  34. package/dist/api/lazy.js.map +1 -1
  35. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  36. package/dist/api/llama-cpp-classify.js +2 -1
  37. package/dist/api/llama-cpp-classify.js.map +1 -1
  38. package/dist/api/mistral-conversations.d.ts.map +1 -1
  39. package/dist/api/mistral-conversations.js +19 -46
  40. package/dist/api/mistral-conversations.js.map +1 -1
  41. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  42. package/dist/api/openai-codex-responses.js +17 -16
  43. package/dist/api/openai-codex-responses.js.map +1 -1
  44. package/dist/api/openai-completions.d.ts.map +1 -1
  45. package/dist/api/openai-completions.js +56 -62
  46. package/dist/api/openai-completions.js.map +1 -1
  47. package/dist/api/openai-responses-shared.d.ts +16 -0
  48. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  49. package/dist/api/openai-responses-shared.js +119 -66
  50. package/dist/api/openai-responses-shared.js.map +1 -1
  51. package/dist/api/openai-responses.d.ts.map +1 -1
  52. package/dist/api/openai-responses.js +17 -15
  53. package/dist/api/openai-responses.js.map +1 -1
  54. package/dist/api/openrouter-images.d.ts.map +1 -1
  55. package/dist/api/openrouter-images.js +5 -3
  56. package/dist/api/openrouter-images.js.map +1 -1
  57. package/dist/api/pi-messages.d.ts +2 -0
  58. package/dist/api/pi-messages.d.ts.map +1 -1
  59. package/dist/api/pi-messages.js +27 -12
  60. package/dist/api/pi-messages.js.map +1 -1
  61. package/dist/api/simple-options.d.ts.map +1 -1
  62. package/dist/api/simple-options.js +2 -1
  63. package/dist/api/simple-options.js.map +1 -1
  64. package/dist/api/system-one-shared.d.ts +29 -2
  65. package/dist/api/system-one-shared.d.ts.map +1 -1
  66. package/dist/api/system-one-shared.js +55 -3
  67. package/dist/api/system-one-shared.js.map +1 -1
  68. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  69. package/dist/api/typesafe-system-one.js +10 -0
  70. package/dist/api/typesafe-system-one.js.map +1 -1
  71. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  72. package/dist/auth/oauth/github-copilot.js +4 -11
  73. package/dist/auth/oauth/github-copilot.js.map +1 -1
  74. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  75. package/dist/auth/oauth/kimi-coding.js +4 -22
  76. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  77. package/dist/auth/oauth/meta.d.ts.map +1 -1
  78. package/dist/auth/oauth/meta.js +1 -14
  79. package/dist/auth/oauth/meta.js.map +1 -1
  80. package/dist/auth/oauth/radius.d.ts.map +1 -1
  81. package/dist/auth/oauth/radius.js +9 -3
  82. package/dist/auth/oauth/radius.js.map +1 -1
  83. package/dist/auth/oauth/trusted-url.d.ts +7 -0
  84. package/dist/auth/oauth/trusted-url.d.ts.map +1 -0
  85. package/dist/auth/oauth/trusted-url.js +12 -0
  86. package/dist/auth/oauth/trusted-url.js.map +1 -0
  87. package/dist/auth/types.d.ts +4 -0
  88. package/dist/auth/types.d.ts.map +1 -1
  89. package/dist/auth/types.js.map +1 -1
  90. package/dist/cli.js +6 -2
  91. package/dist/cli.js.map +1 -1
  92. package/dist/compat/extension-oauth-types.d.ts +4 -0
  93. package/dist/compat/extension-oauth-types.d.ts.map +1 -1
  94. package/dist/compat/extension-oauth-types.js.map +1 -1
  95. package/dist/index.d.ts +4 -3
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +4 -2
  98. package/dist/index.js.map +1 -1
  99. package/dist/models.d.ts +4 -0
  100. package/dist/models.d.ts.map +1 -1
  101. package/dist/models.generated.js +1 -1
  102. package/dist/models.generated.js.map +1 -1
  103. package/dist/models.js +20 -6
  104. package/dist/models.js.map +1 -1
  105. package/dist/providers/amazon-bedrock.models.js +1 -1
  106. package/dist/providers/amazon-bedrock.models.js.map +1 -1
  107. package/dist/providers/ant-ling.models.js +1 -1
  108. package/dist/providers/ant-ling.models.js.map +1 -1
  109. package/dist/providers/anthropic.models.js +1 -1
  110. package/dist/providers/anthropic.models.js.map +1 -1
  111. package/dist/providers/azure.models.js +1 -1
  112. package/dist/providers/azure.models.js.map +1 -1
  113. package/dist/providers/baseten.models.js +1 -1
  114. package/dist/providers/baseten.models.js.map +1 -1
  115. package/dist/providers/cerebras.models.js +1 -1
  116. package/dist/providers/cerebras.models.js.map +1 -1
  117. package/dist/providers/cloudflare-ai-gateway.models.js +1 -1
  118. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  119. package/dist/providers/cloudflare-workers-ai.models.js +1 -1
  120. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
  121. package/dist/providers/compat-schema.d.ts +444 -304
  122. package/dist/providers/compat-schema.d.ts.map +1 -1
  123. package/dist/providers/compat-schema.js +155 -109
  124. package/dist/providers/compat-schema.js.map +1 -1
  125. package/dist/providers/data/.manifest.json +1 -1
  126. package/dist/providers/data/amazon-bedrock.json +1 -1
  127. package/dist/providers/data/anthropic.json +1 -1
  128. package/dist/providers/data/azure.json +1 -1
  129. package/dist/providers/data/baseten.json +1 -1
  130. package/dist/providers/data/cerebras.json +1 -1
  131. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  132. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  133. package/dist/providers/data/fireworks.json +1 -1
  134. package/dist/providers/data/github-copilot.json +1 -1
  135. package/dist/providers/data/google-vertex.json +1 -1
  136. package/dist/providers/data/google.json +1 -1
  137. package/dist/providers/data/groq.json +1 -1
  138. package/dist/providers/data/huggingface.json +1 -1
  139. package/dist/providers/data/kimi-coding.json +1 -1
  140. package/dist/providers/data/minimax-cn.json +1 -1
  141. package/dist/providers/data/minimax.json +1 -1
  142. package/dist/providers/data/mistral.json +1 -1
  143. package/dist/providers/data/moonshotai-cn.json +1 -1
  144. package/dist/providers/data/moonshotai.json +1 -1
  145. package/dist/providers/data/nvidia.json +1 -1
  146. package/dist/providers/data/openai-codex.json +1 -1
  147. package/dist/providers/data/openai.json +1 -1
  148. package/dist/providers/data/opencode-go.json +1 -1
  149. package/dist/providers/data/opencode.json +1 -1
  150. package/dist/providers/data/openrouter.json +1 -1
  151. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  152. package/dist/providers/data/qwen-token-plan-individual.json +1 -1
  153. package/dist/providers/data/qwen-token-plan.json +1 -1
  154. package/dist/providers/data/radius.json +1 -1
  155. package/dist/providers/data/together.json +1 -1
  156. package/dist/providers/data/typesafe.json +1 -1
  157. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  158. package/dist/providers/data/xai.json +1 -1
  159. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  160. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  161. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  162. package/dist/providers/data/xiaomi.json +1 -1
  163. package/dist/providers/data/zai-coding-cn.json +1 -1
  164. package/dist/providers/data/zai.json +1 -1
  165. package/dist/providers/deepseek.models.js +1 -1
  166. package/dist/providers/deepseek.models.js.map +1 -1
  167. package/dist/providers/faux.d.ts.map +1 -1
  168. package/dist/providers/faux.js +7 -2
  169. package/dist/providers/faux.js.map +1 -1
  170. package/dist/providers/fireworks.models.js +1 -1
  171. package/dist/providers/fireworks.models.js.map +1 -1
  172. package/dist/providers/github-copilot.models.js +1 -1
  173. package/dist/providers/github-copilot.models.js.map +1 -1
  174. package/dist/providers/google-vertex.models.js +1 -1
  175. package/dist/providers/google-vertex.models.js.map +1 -1
  176. package/dist/providers/google.models.js +1 -1
  177. package/dist/providers/google.models.js.map +1 -1
  178. package/dist/providers/groq.models.js +1 -1
  179. package/dist/providers/groq.models.js.map +1 -1
  180. package/dist/providers/huggingface.models.js +1 -1
  181. package/dist/providers/huggingface.models.js.map +1 -1
  182. package/dist/providers/kimi-coding.models.js +1 -1
  183. package/dist/providers/kimi-coding.models.js.map +1 -1
  184. package/dist/providers/meta.models.js +1 -1
  185. package/dist/providers/meta.models.js.map +1 -1
  186. package/dist/providers/minimax-cn.models.js +1 -1
  187. package/dist/providers/minimax-cn.models.js.map +1 -1
  188. package/dist/providers/minimax.models.js +1 -1
  189. package/dist/providers/minimax.models.js.map +1 -1
  190. package/dist/providers/mistral.models.js +1 -1
  191. package/dist/providers/mistral.models.js.map +1 -1
  192. package/dist/providers/model-schema.d.ts +65 -65
  193. package/dist/providers/model-schema.d.ts.map +1 -1
  194. package/dist/providers/model-schema.js +44 -31
  195. package/dist/providers/model-schema.js.map +1 -1
  196. package/dist/providers/moonshotai-cn.models.js +1 -1
  197. package/dist/providers/moonshotai-cn.models.js.map +1 -1
  198. package/dist/providers/moonshotai.models.js +1 -1
  199. package/dist/providers/moonshotai.models.js.map +1 -1
  200. package/dist/providers/nvidia.models.js +1 -1
  201. package/dist/providers/nvidia.models.js.map +1 -1
  202. package/dist/providers/openai-codex.models.js +1 -1
  203. package/dist/providers/openai-codex.models.js.map +1 -1
  204. package/dist/providers/openai.models.js +1 -1
  205. package/dist/providers/openai.models.js.map +1 -1
  206. package/dist/providers/opencode-go.models.js +1 -1
  207. package/dist/providers/opencode-go.models.js.map +1 -1
  208. package/dist/providers/opencode.js +1 -1
  209. package/dist/providers/opencode.js.map +1 -1
  210. package/dist/providers/opencode.models.js +1 -1
  211. package/dist/providers/opencode.models.js.map +1 -1
  212. package/dist/providers/openrouter.models.js +1 -1
  213. package/dist/providers/openrouter.models.js.map +1 -1
  214. package/dist/providers/qwen-token-plan-cn.models.js +1 -1
  215. package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
  216. package/dist/providers/qwen-token-plan-individual.models.js +1 -1
  217. package/dist/providers/qwen-token-plan-individual.models.js.map +1 -1
  218. package/dist/providers/qwen-token-plan.models.js +1 -1
  219. package/dist/providers/qwen-token-plan.models.js.map +1 -1
  220. package/dist/providers/radius-config.d.ts.map +1 -1
  221. package/dist/providers/radius-config.js +2 -1
  222. package/dist/providers/radius-config.js.map +1 -1
  223. package/dist/providers/radius.models.js +1 -1
  224. package/dist/providers/radius.models.js.map +1 -1
  225. package/dist/providers/schema-builders.d.ts +10 -0
  226. package/dist/providers/schema-builders.d.ts.map +1 -0
  227. package/dist/providers/schema-builders.js +18 -0
  228. package/dist/providers/schema-builders.js.map +1 -0
  229. package/dist/providers/together.models.js +1 -1
  230. package/dist/providers/together.models.js.map +1 -1
  231. package/dist/providers/typesafe.models.js +1 -1
  232. package/dist/providers/typesafe.models.js.map +1 -1
  233. package/dist/providers/vercel-ai-gateway.models.js +1 -1
  234. package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  235. package/dist/providers/xai.models.js +1 -1
  236. package/dist/providers/xai.models.js.map +1 -1
  237. package/dist/providers/xiaomi-token-plan-ams.models.js +1 -1
  238. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  239. package/dist/providers/xiaomi-token-plan-cn.models.js +1 -1
  240. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  241. package/dist/providers/xiaomi-token-plan-sgp.models.js +1 -1
  242. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  243. package/dist/providers/xiaomi.models.js +1 -1
  244. package/dist/providers/xiaomi.models.js.map +1 -1
  245. package/dist/providers/zai-coding-cn.models.js +1 -1
  246. package/dist/providers/zai-coding-cn.models.js.map +1 -1
  247. package/dist/providers/zai.models.js +1 -1
  248. package/dist/providers/zai.models.js.map +1 -1
  249. package/dist/telemetry.d.ts +288 -0
  250. package/dist/telemetry.d.ts.map +1 -0
  251. package/dist/telemetry.js +192 -0
  252. package/dist/telemetry.js.map +1 -0
  253. package/dist/types.d.ts +25 -7
  254. package/dist/types.d.ts.map +1 -1
  255. package/dist/types.js.map +1 -1
  256. package/dist/utils/error-body.d.ts.map +1 -1
  257. package/dist/utils/error-body.js +2 -1
  258. package/dist/utils/error-body.js.map +1 -1
  259. package/dist/utils/identity.d.ts +65 -0
  260. package/dist/utils/identity.d.ts.map +1 -0
  261. package/dist/utils/identity.js +141 -0
  262. package/dist/utils/identity.js.map +1 -0
  263. package/dist/utils/openrouter-cost.d.ts +24 -0
  264. package/dist/utils/openrouter-cost.d.ts.map +1 -0
  265. package/dist/utils/openrouter-cost.js +34 -0
  266. package/dist/utils/openrouter-cost.js.map +1 -0
  267. package/dist/utils/overflow.d.ts +1 -0
  268. package/dist/utils/overflow.d.ts.map +1 -1
  269. package/dist/utils/overflow.js +8 -1
  270. package/dist/utils/overflow.js.map +1 -1
  271. package/dist/utils/retry.d.ts.map +1 -1
  272. package/dist/utils/retry.js +19 -7
  273. package/dist/utils/retry.js.map +1 -1
  274. package/dist/utils/streaming-json.d.ts +84 -0
  275. package/dist/utils/streaming-json.d.ts.map +1 -0
  276. package/dist/utils/streaming-json.js +513 -0
  277. package/dist/utils/streaming-json.js.map +1 -0
  278. package/dist/utils/{typebox-helpers.d.ts → string-enum.d.ts} +5 -5
  279. package/dist/utils/string-enum.d.ts.map +1 -0
  280. package/dist/utils/{typebox-helpers.js → string-enum.js} +5 -6
  281. package/dist/utils/string-enum.js.map +1 -0
  282. package/dist/utils/text.d.ts +7 -0
  283. package/dist/utils/text.d.ts.map +1 -1
  284. package/dist/utils/text.js +9 -0
  285. package/dist/utils/text.js.map +1 -1
  286. package/dist/utils/transcript.d.ts +23 -9
  287. package/dist/utils/transcript.d.ts.map +1 -1
  288. package/dist/utils/transcript.js +30 -19
  289. package/dist/utils/transcript.js.map +1 -1
  290. package/dist/utils/validation.d.ts +5 -3
  291. package/dist/utils/validation.d.ts.map +1 -1
  292. package/dist/utils/validation.js +27 -277
  293. package/dist/utils/validation.js.map +1 -1
  294. package/dist/utils/version.d.ts +2 -0
  295. package/dist/utils/version.d.ts.map +1 -0
  296. package/dist/utils/version.js +3 -0
  297. package/dist/utils/version.js.map +1 -0
  298. package/package.json +11 -10
  299. package/dist/utils/pi-user-agent.d.ts +0 -2
  300. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  301. package/dist/utils/pi-user-agent.js +0 -12
  302. package/dist/utils/pi-user-agent.js.map +0 -1
  303. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  304. package/dist/utils/typebox-helpers.js.map +0 -1
@@ -4,14 +4,16 @@ import { calculateCost } from "../models.js";
4
4
  import { appendAssistantMessageDiagnostic } from "../utils/diagnostics.js";
5
5
  import { AssistantMessageEventStream } from "../utils/event-stream.js";
6
6
  import { headersToRecord } from "../utils/headers.js";
7
- import { parseJsonWithRepair, parseStreamingJson, parseToolCallArguments } from "../utils/json-parse.js";
8
- import { getPiUserAgent } from "../utils/pi-user-agent.js";
7
+ import { getIdentityHeaders } from "../utils/identity.js";
8
+ import { parseJsonWithRepair } from "../utils/json-parse.js";
9
+ import { applyOpenRouterReportedCost, isOpenRouterModel, } from "../utils/openrouter-cost.js";
9
10
  import { getProviderEnvValue } from "../utils/provider-env.js";
10
11
  import { retryProviderRequest } from "../utils/provider-retry.js";
11
12
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
13
+ import { ToolCallArgumentsStream } from "../utils/streaming-json.js";
12
14
  import { getSystemMessageText, renderSystemMessageUpdate } from "../utils/text.js";
13
15
  import { getCurrentTools, getInitialSystemMessage, resolveTranscript, } from "../utils/transcript.js";
14
- import { getJsonSchemaToolParameters, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
16
+ import { getJsonSchemaToolParameters, isStrictToolLimitErrorMessage, prefersJsonSchemaStrictSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
15
17
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "./github-copilot-headers.js";
16
18
  import { adjustMaxTokensForThinking, buildBaseOptions, clampMaxTokensToContext } from "./simple-options.js";
17
19
  import { splitErrorToolResultImages, transformMessages } from "./transform-messages.js";
@@ -160,9 +162,6 @@ function mergeHeaders(...headerSources) {
160
162
  }
161
163
  return merged;
162
164
  }
163
- function mergeClientHeaders(...headerSources) {
164
- return mergeHeaders({ "User-Agent": getPiUserAgent() }, ...headerSources);
165
- }
166
165
  function hasHeader(headers, name) {
167
166
  if (!headers)
168
167
  return false;
@@ -422,7 +421,7 @@ export const stream = (model, context, options) => {
422
421
  }
423
422
  const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
424
423
  const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
425
- const created = createClient(model, apiKey, options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, federation);
424
+ const created = createClient(model, apiKey, options?.headers, options?.fetch, copilotDynamicHeaders, cacheSessionId, federation, options?.identity);
426
425
  client = created.client;
427
426
  isOAuth = created.isOAuthToken;
428
427
  }
@@ -459,11 +458,7 @@ export const stream = (model, context, options) => {
459
458
  !(error instanceof Anthropic.APIError) ||
460
459
  error.status !== 400 ||
461
460
  error.type !== "invalid_request_error" ||
462
- ![
463
- "The compiled grammar is too large",
464
- "Too many strict tools (",
465
- "Schemas contains too many parameters with union types (",
466
- ].some((message) => error.message.includes(message))) {
461
+ !isStrictToolLimitErrorMessage(error.message)) {
467
462
  throw error;
468
463
  }
469
464
  disablePreferredStrictTools = true;
@@ -477,7 +472,12 @@ export const stream = (model, context, options) => {
477
472
  }
478
473
  await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
479
474
  stream.push({ type: "start", partial: output });
480
- const blocks = output.content;
475
+ // Stream state per Anthropic block index, kept off the content blocks so it never reaches partials.
476
+ const openBlocks = new Map();
477
+ const openBlock = (anthropicIndex) => {
478
+ const open = openBlocks.get(anthropicIndex);
479
+ return open === undefined ? undefined : { ...open, block: output.content[open.contentIndex] };
480
+ };
481
481
  for await (const event of iterateAnthropicEvents(response, options?.signal)) {
482
482
  await options?.onProviderStreamEvent?.(event, model);
483
483
  if (event.type === "message_start") {
@@ -512,105 +512,92 @@ export const stream = (model, context, options) => {
512
512
  continue;
513
513
  }
514
514
  if (event.content_block.type === "text") {
515
- const block = {
516
- type: "text",
517
- text: event.content_block.text ?? "",
518
- index: event.index,
519
- };
520
- output.content.push(block);
515
+ output.content.push({ type: "text", text: event.content_block.text ?? "" });
516
+ openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
521
517
  stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
522
518
  }
523
519
  else if (event.content_block.type === "thinking") {
524
- const block = {
520
+ output.content.push({
525
521
  type: "thinking",
526
522
  thinking: event.content_block.thinking ?? "",
527
523
  thinkingSignature: event.content_block.signature ?? "",
528
- index: event.index,
529
- };
530
- output.content.push(block);
524
+ });
525
+ openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
531
526
  stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
532
527
  }
533
528
  else if (event.content_block.type === "redacted_thinking") {
534
- const block = {
529
+ output.content.push({
535
530
  type: "thinking",
536
531
  thinking: "[Reasoning redacted]",
537
532
  thinkingSignature: event.content_block.data,
538
533
  redacted: true,
539
- index: event.index,
540
- };
541
- output.content.push(block);
534
+ });
535
+ openBlocks.set(event.index, { contentIndex: output.content.length - 1 });
542
536
  stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
543
537
  }
544
538
  else if (event.content_block.type === "tool_use") {
545
- const block = {
539
+ output.content.push({
546
540
  type: "toolCall",
547
541
  id: event.content_block.id,
548
542
  name: isOAuth
549
543
  ? fromClaudeCodeName(event.content_block.name, currentTools)
550
544
  : event.content_block.name,
551
545
  arguments: event.content_block.input ?? {},
552
- partialJson: "",
553
- index: event.index,
554
- };
555
- output.content.push(block);
546
+ });
547
+ openBlocks.set(event.index, {
548
+ contentIndex: output.content.length - 1,
549
+ arguments: new ToolCallArgumentsStream(),
550
+ });
556
551
  stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
557
552
  }
558
553
  }
559
554
  else if (event.type === "content_block_delta") {
555
+ const open = openBlock(event.index);
560
556
  if (event.delta.type === "text_delta") {
561
- const index = blocks.findIndex((b) => b.index === event.index);
562
- const block = blocks[index];
563
- if (block && block.type === "text") {
564
- block.text += event.delta.text;
557
+ if (open?.block.type === "text") {
558
+ open.block.text += event.delta.text;
565
559
  stream.push({
566
560
  type: "text_delta",
567
- contentIndex: index,
561
+ contentIndex: open.contentIndex,
568
562
  delta: event.delta.text,
569
563
  partial: output,
570
564
  });
571
565
  }
572
566
  }
573
567
  else if (event.delta.type === "thinking_delta") {
574
- const index = blocks.findIndex((b) => b.index === event.index);
575
- const block = blocks[index];
576
- if (block && block.type === "thinking") {
577
- block.thinking += event.delta.thinking;
568
+ if (open?.block.type === "thinking") {
569
+ open.block.thinking += event.delta.thinking;
578
570
  stream.push({
579
571
  type: "thinking_delta",
580
- contentIndex: index,
572
+ contentIndex: open.contentIndex,
581
573
  delta: event.delta.thinking,
582
574
  partial: output,
583
575
  });
584
576
  }
585
577
  }
586
578
  else if (event.delta.type === "input_json_delta") {
587
- const index = blocks.findIndex((b) => b.index === event.index);
588
- const block = blocks[index];
589
- if (block && block.type === "toolCall") {
590
- block.partialJson += event.delta.partial_json;
591
- block.arguments = parseStreamingJson(block.partialJson);
579
+ if (open?.block.type === "toolCall" && open.arguments !== undefined) {
580
+ open.block.arguments = open.arguments.append(event.delta.partial_json);
592
581
  stream.push({
593
582
  type: "toolcall_delta",
594
- contentIndex: index,
583
+ contentIndex: open.contentIndex,
595
584
  delta: event.delta.partial_json,
596
585
  partial: output,
597
586
  });
598
587
  }
599
588
  }
600
589
  else if (event.delta.type === "signature_delta") {
601
- const index = blocks.findIndex((b) => b.index === event.index);
602
- const block = blocks[index];
603
- if (block && block.type === "thinking") {
604
- block.thinkingSignature = block.thinkingSignature || "";
605
- block.thinkingSignature += event.delta.signature;
590
+ if (open?.block.type === "thinking") {
591
+ open.block.thinkingSignature = open.block.thinkingSignature || "";
592
+ open.block.thinkingSignature += event.delta.signature;
606
593
  }
607
594
  }
608
595
  }
609
596
  else if (event.type === "content_block_stop") {
610
- const index = blocks.findIndex((b) => b.index === event.index);
611
- const block = blocks[index];
612
- if (block) {
613
- delete block.index;
597
+ const open = openBlock(event.index);
598
+ openBlocks.delete(event.index);
599
+ if (open) {
600
+ const { block, contentIndex: index } = open;
614
601
  if (block.type === "text") {
615
602
  stream.push({
616
603
  type: "text_end",
@@ -627,11 +614,8 @@ export const stream = (model, context, options) => {
627
614
  partial: output,
628
615
  });
629
616
  }
630
- else if (block.type === "toolCall") {
631
- Object.assign(block, parseToolCallArguments(block.partialJson));
632
- // Finalize in-place and strip the scratch buffer so replay only
633
- // carries parsed arguments.
634
- delete block.partialJson;
617
+ else if (block.type === "toolCall" && open.arguments !== undefined) {
618
+ Object.assign(block, open.arguments.finish());
635
619
  stream.push({
636
620
  type: "toolcall_end",
637
621
  contentIndex: index,
@@ -683,6 +667,11 @@ export const stream = (model, context, options) => {
683
667
  output.usage.totalTokens =
684
668
  output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
685
669
  calculateCost(usageModel, output.usage);
670
+ if (isOpenRouterModel(model) && event.usage) {
671
+ // OpenRouter adds billing fields to streamed message_delta usage at runtime,
672
+ // although its OpenAPI schema and the Anthropic SDK do not type those fields.
673
+ applyOpenRouterReportedCost(output.usage, event.usage);
674
+ }
686
675
  }
687
676
  }
688
677
  if (options?.signal?.aborted) {
@@ -711,11 +700,6 @@ export const stream = (model, context, options) => {
711
700
  stream.end();
712
701
  }
713
702
  catch (error) {
714
- for (const block of output.content) {
715
- delete block.index;
716
- // partialJson is only a streaming scratch buffer; never persist it.
717
- delete block.partialJson;
718
- }
719
703
  output.stopReason = options?.signal?.aborted ? "aborted" : "error";
720
704
  output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
721
705
  stream.push({ type: "error", reason: output.stopReason, error: output });
@@ -783,7 +767,7 @@ export const streamSimple = (model, context, options) => {
783
767
  function isOAuthToken(apiKey) {
784
768
  return apiKey.includes("sk-ant-oat");
785
769
  }
786
- function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sessionId, federation) {
770
+ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sessionId, federation, identity) {
787
771
  // Copilot: Bearer auth.
788
772
  if (model.provider === "github-copilot") {
789
773
  const client = new PiAnthropic({
@@ -792,14 +776,14 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
792
776
  baseURL: model.baseUrl,
793
777
  dangerouslyAllowBrowser: true,
794
778
  fetch,
795
- defaultHeaders: mergeClientHeaders({
779
+ defaultHeaders: mergeHeaders(getIdentityHeaders(model, identity), {
796
780
  accept: "application/json",
797
781
  "anthropic-dangerous-direct-browser-access": "true",
798
782
  }, model.headers, dynamicHeaders, optionsHeaders),
799
783
  });
800
784
  return { client, isOAuthToken: false };
801
785
  }
802
- // OAuth: Bearer auth, Claude Code identity headers
786
+ // OAuth: Bearer auth, Claude Code identity headers instead of the client identity.
803
787
  if (apiKey && isOAuthToken(apiKey)) {
804
788
  const client = new PiAnthropic({
805
789
  apiKey: null,
@@ -807,7 +791,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
807
791
  baseURL: model.baseUrl,
808
792
  dangerouslyAllowBrowser: true,
809
793
  fetch,
810
- defaultHeaders: mergeClientHeaders({
794
+ defaultHeaders: mergeHeaders({
811
795
  accept: "application/json",
812
796
  "anthropic-dangerous-direct-browser-access": "true",
813
797
  "user-agent": `claude-cli/${claudeCodeVersion}`,
@@ -823,7 +807,7 @@ function createClient(model, apiKey, optionsHeaders, fetch, dynamicHeaders, sess
823
807
  const header = compat.sessionAffinityFormat === "openrouter" ? "x-session-id" : "x-session-affinity";
824
808
  sessionAffinityHeaders[header] = sessionId;
825
809
  }
826
- const defaultHeaders = mergeClientHeaders({
810
+ const defaultHeaders = mergeHeaders(getIdentityHeaders(model, identity), {
827
811
  accept: "application/json",
828
812
  "anthropic-dangerous-direct-browser-access": "true",
829
813
  }, sessionAffinityHeaders, model.headers, optionsHeaders);
@@ -904,10 +888,7 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
904
888
  let hasPreferredStrictTools = false;
905
889
  const convertToolDefinitions = (tools, toolCacheControl) => {
906
890
  const definitions = convertTools(tools, isOAuthToken, compat.supportsEagerToolInputStreaming, compat.supportsStrictTools, toolCacheControl, disablePreferredStrictTools);
907
- if (definitions.some((definition, index) => {
908
- const config = tools[index].constrainedSampling;
909
- return definition.strict === true && config && config.type === "json_schema" && config.strict === "prefer";
910
- })) {
891
+ if (definitions.some((definition, index) => definition.strict === true && prefersJsonSchemaStrictSampling(tools[index]))) {
911
892
  hasPreferredStrictTools = true;
912
893
  }
913
894
  return definitions;
@@ -988,7 +969,10 @@ function buildParams(model, context, isOAuthToken, options, disablePreferredStri
988
969
  display: options?.thinkingDisplay ?? "summarized",
989
970
  block_binding: { prefix_mismatch_behavior: "drop_block" },
990
971
  };
991
- params.output_config = { effort: "high" };
972
+ // Effort markers set the level for every reply. The top-level effort is part of the cached
973
+ // prompt, so it stays fixed. It is "low" because organizations can cap effort and reject
974
+ // requests containing any level above the cap.
975
+ params.output_config = { effort: "low" };
992
976
  }
993
977
  else if (model.reasoning && options?.thinkingEnabled) {
994
978
  // Default to "summarized" so Opus 4.7 and Mythos Preview behave like
@@ -1048,7 +1032,9 @@ convertToolDefinitions) {
1048
1032
  const params = [];
1049
1033
  const assistantLevels = new Map();
1050
1034
  // Later system messages are held back and emitted directly before the next assistant
1051
- // message (or at the end of the transcript). Anthropic requires `tool_result` blocks to
1035
+ // message that is sent (or at the end of the transcript). Assistant messages that convert to
1036
+ // no blocks are dropped, so holding past them keeps an update from landing before a user
1037
+ // message (#11091). Anthropic requires `tool_result` blocks to
1052
1038
  // immediately follow their `tool_use`, so a system message between them is rejected; this
1053
1039
  // also mirrors where the managed-effort system messages are inserted. As a result an
1054
1040
  // update placed before a user message in the transcript lands after it on the wire.
@@ -1121,7 +1107,6 @@ convertToolDefinitions) {
1121
1107
  }
1122
1108
  }
1123
1109
  else if (msg.role === "assistant") {
1124
- flushPendingSystemMessages();
1125
1110
  const blocks = [];
1126
1111
  for (const block of msg.content) {
1127
1112
  if (block.type === "text") {
@@ -1179,6 +1164,7 @@ convertToolDefinitions) {
1179
1164
  }
1180
1165
  if (blocks.length === 0)
1181
1166
  continue;
1167
+ flushPendingSystemMessages();
1182
1168
  const messageIndex = params.length;
1183
1169
  params.push({
1184
1170
  role: "assistant",
@@ -1302,8 +1288,7 @@ function convertTools(tools, isOAuthToken, supportsEagerToolInputStreaming, supp
1302
1288
  if (!tools)
1303
1289
  return [];
1304
1290
  return tools.map((tool, index) => {
1305
- const config = tool.constrainedSampling;
1306
- const strict = disablePreferredStrictTools && config && config.type === "json_schema" && config.strict === "prefer"
1291
+ const strict = disablePreferredStrictTools && prefersJsonSchemaStrictSampling(tool)
1307
1292
  ? undefined
1308
1293
  : resolveJsonSchemaStrictSampling(tool, supportsStrictTools, isAnthropicStrictUnsupportedKeyword);
1309
1294
  const parameters = getJsonSchemaToolParameters(tool, strict);