@opencow-ai/opencow-agent-sdk 0.4.3 → 0.4.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.
package/dist/client.js CHANGED
@@ -68866,12 +68866,12 @@ function getOpus46CostTier(fastMode) {
68866
68866
  return COST_TIER_5_25;
68867
68867
  }
68868
68868
  function tokensToUSDCost(modelCosts, usage) {
68869
- return usage.input_tokens / 1e6 * modelCosts.inputTokens + usage.output_tokens / 1e6 * modelCosts.outputTokens + (usage.cache_read_input_tokens ?? 0) / 1e6 * modelCosts.promptCacheReadTokens + (usage.cache_creation_input_tokens ?? 0) / 1e6 * modelCosts.promptCacheWriteTokens + (usage.server_tool_use?.web_search_requests ?? 0) * modelCosts.webSearchRequests;
68869
+ return (usage?.input_tokens ?? 0) / 1e6 * modelCosts.inputTokens + (usage?.output_tokens ?? 0) / 1e6 * modelCosts.outputTokens + (usage?.cache_read_input_tokens ?? 0) / 1e6 * modelCosts.promptCacheReadTokens + (usage?.cache_creation_input_tokens ?? 0) / 1e6 * modelCosts.promptCacheWriteTokens + (usage?.server_tool_use?.web_search_requests ?? 0) * modelCosts.webSearchRequests;
68870
68870
  }
68871
68871
  function getModelCosts(model, usage) {
68872
68872
  const shortName = getCanonicalName(model);
68873
68873
  if (shortName === firstPartyNameToCanonical(CLAUDE_OPUS_4_6_CONFIG.firstParty)) {
68874
- const isFastMode = usage.speed === "fast";
68874
+ const isFastMode = usage?.speed === "fast";
68875
68875
  return getOpus46CostTier(isFastMode);
68876
68876
  }
68877
68877
  const costs = MODEL_COSTS[shortName];
@@ -94949,10 +94949,25 @@ function sanitizeTypeField(record3) {
94949
94949
  record3.type = filtered;
94950
94950
  }
94951
94951
  }
94952
- function makeSchemaNullable(schema) {
94952
+ function makeSchemaNullable(schema, style = "union") {
94953
94953
  if ("enum" in schema || "const" in schema)
94954
94954
  return schema;
94955
+ if (schema.nullable === true)
94956
+ return schema;
94955
94957
  const raw = schema.type;
94958
+ if (style === "nullable") {
94959
+ if (typeof raw === "string") {
94960
+ if (raw === "null")
94961
+ return schema;
94962
+ return { ...schema, nullable: true };
94963
+ }
94964
+ if (Array.isArray(raw)) {
94965
+ if (raw.includes("null"))
94966
+ return schema;
94967
+ return { ...schema, nullable: true };
94968
+ }
94969
+ return schema;
94970
+ }
94956
94971
  if (typeof raw === "string") {
94957
94972
  if (raw === "null")
94958
94973
  return schema;
@@ -94965,6 +94980,52 @@ function makeSchemaNullable(schema) {
94965
94980
  }
94966
94981
  return schema;
94967
94982
  }
94983
+ function splitTypeArrayToAnyOf(schema) {
94984
+ if (!Array.isArray(schema.type) || schema.type.length < 2)
94985
+ return schema;
94986
+ const types2 = schema.type;
94987
+ const hasNull = types2.includes("null");
94988
+ const nonNullTypes = types2.filter((t) => t !== "null");
94989
+ if (hasNull && nonNullTypes.length === 1) {
94990
+ const { type: _type, ...rest } = schema;
94991
+ return { ...rest, type: nonNullTypes[0], nullable: true };
94992
+ }
94993
+ const ARRAY_KEYS = new Set(["items"]);
94994
+ const OBJECT_KEYS = new Set(["properties", "required", "additionalProperties"]);
94995
+ const TYPE_SPECIFIC_KEYS = new Set([...ARRAY_KEYS, ...OBJECT_KEYS]);
94996
+ const base2 = {};
94997
+ const structural = {};
94998
+ for (const [key, value] of Object.entries(schema)) {
94999
+ if (key === "type")
95000
+ continue;
95001
+ if (TYPE_SPECIFIC_KEYS.has(key)) {
95002
+ structural[key] = value;
95003
+ } else {
95004
+ base2[key] = value;
95005
+ }
95006
+ }
95007
+ const variants = nonNullTypes.map((t) => {
95008
+ const variant = { type: t };
95009
+ if (t === "array") {
95010
+ for (const k of ARRAY_KEYS) {
95011
+ if (k in structural)
95012
+ variant[k] = structural[k];
95013
+ }
95014
+ } else if (t === "object") {
95015
+ for (const k of OBJECT_KEYS) {
95016
+ if (k in structural)
95017
+ variant[k] = structural[k];
95018
+ }
95019
+ }
95020
+ return variant;
95021
+ });
95022
+ if (hasNull)
95023
+ base2.nullable = true;
95024
+ if (variants.length === 1) {
95025
+ return { ...base2, ...variants[0] };
95026
+ }
95027
+ return { ...base2, anyOf: variants };
95028
+ }
94968
95029
  function sanitizeSchemaForOpenAICompat(schema) {
94969
95030
  const stripped = stripSchemaKeywords(schema, OPENAI_INCOMPATIBLE_SCHEMA_KEYWORDS);
94970
95031
  if (!isSchemaRecord(stripped)) {
@@ -95864,6 +95925,51 @@ var init_providerProfile = __esm(() => {
95864
95925
  ];
95865
95926
  });
95866
95927
 
95928
+ // src/providers/openai/capabilities.ts
95929
+ function supportsReasoningEffort(model) {
95930
+ return /^(o\d|gpt-5|gpt-4\.5)/i.test(model);
95931
+ }
95932
+ function isGeminiLikeModel(model) {
95933
+ const normalized = (model ?? "").trim().toLowerCase();
95934
+ return normalized.startsWith("gemini-") || normalized.includes("/gemini-");
95935
+ }
95936
+ function isGeminiTarget(model) {
95937
+ if (isEnvTruthy(getQueryEnvVar("CLAUDE_CODE_USE_GEMINI")))
95938
+ return true;
95939
+ return isGeminiLikeModel(model);
95940
+ }
95941
+ function supportsParallelToolCalls(model) {
95942
+ return !isGeminiTarget(model);
95943
+ }
95944
+ function getOpenAICompatMaxOutputTokens(model) {
95945
+ const max = getOpenAIMaxOutputTokens(model);
95946
+ if (max === undefined)
95947
+ return null;
95948
+ return { default: max, upperLimit: max };
95949
+ }
95950
+ function getOpenAICompatContextWindow(model) {
95951
+ return getOpenAIContextWindow(model) ?? null;
95952
+ }
95953
+ function openAICompatSupports(feature, model) {
95954
+ if (feature === "reasoning-effort")
95955
+ return supportsReasoningEffort(model);
95956
+ if (feature === "parallel-tool-calls")
95957
+ return supportsParallelToolCalls(model);
95958
+ return FEATURES_OPENAI_COMPAT.includes(feature);
95959
+ }
95960
+ var FEATURES_OPENAI_COMPAT;
95961
+ var init_capabilities2 = __esm(() => {
95962
+ init_openaiContextWindows();
95963
+ init_envUtils();
95964
+ init_state2();
95965
+ FEATURES_OPENAI_COMPAT = Object.freeze([
95966
+ "streaming",
95967
+ "tool-use",
95968
+ "image-input",
95969
+ "system-message-top-level"
95970
+ ]);
95971
+ });
95972
+
95867
95973
  // src/providers/openai/shim.ts
95868
95974
  var exports_shim = {};
95869
95975
  __export(exports_shim, {
@@ -96033,15 +96139,18 @@ function convertMessages(messages, system) {
96033
96139
  }
96034
96140
  }
96035
96141
  if (otherContent.length > 0) {
96036
- result.push({
96037
- role: "user",
96038
- content: convertContentBlocks(otherContent)
96039
- });
96142
+ const converted = convertContentBlocks(otherContent);
96143
+ if (converted !== "") {
96144
+ result.push({
96145
+ role: "user",
96146
+ content: converted
96147
+ });
96148
+ }
96040
96149
  }
96041
96150
  } else {
96042
96151
  result.push({
96043
96152
  role: "user",
96044
- content: convertContentBlocks(content)
96153
+ content: convertContentBlocks(content) || "."
96045
96154
  });
96046
96155
  }
96047
96156
  } else if (role === "assistant") {
@@ -96053,7 +96162,8 @@ function convertMessages(messages, system) {
96053
96162
  role: "assistant",
96054
96163
  content: (() => {
96055
96164
  const c5 = convertContentBlocks(textContent);
96056
- return typeof c5 === "string" ? c5 : Array.isArray(c5) ? c5.map((p) => p.text ?? "").join("") : "";
96165
+ const text = typeof c5 === "string" ? c5 : Array.isArray(c5) ? c5.map((p) => p.text ?? "").join("") : "";
96166
+ return text || null;
96057
96167
  })()
96058
96168
  };
96059
96169
  if (thinkingBlocks.length > 0) {
@@ -96076,26 +96186,28 @@ function convertMessages(messages, system) {
96076
96186
  }
96077
96187
  result.push(assistantMsg);
96078
96188
  } else {
96189
+ const c5 = convertContentBlocks(content);
96190
+ const text = typeof c5 === "string" ? c5 : Array.isArray(c5) ? c5.map((p) => p.text ?? "").join("") : "";
96079
96191
  result.push({
96080
96192
  role: "assistant",
96081
- content: (() => {
96082
- const c5 = convertContentBlocks(content);
96083
- return typeof c5 === "string" ? c5 : Array.isArray(c5) ? c5.map((p) => p.text ?? "").join("") : "";
96084
- })()
96193
+ content: text || null
96085
96194
  });
96086
96195
  }
96087
96196
  }
96088
96197
  }
96089
96198
  return result;
96090
96199
  }
96091
- function normalizeSchemaForOpenAI(schema, strict = true, topLevel = true) {
96092
- const record3 = sanitizeSchemaForOpenAICompat(schema);
96200
+ function normalizeSchemaForOpenAI(schema, strict = true, topLevel = true, geminiTarget = false) {
96201
+ let record3 = sanitizeSchemaForOpenAICompat(schema);
96202
+ if (geminiTarget) {
96203
+ record3 = splitTypeArrayToAnyOf(record3);
96204
+ }
96093
96205
  if (record3.type === "object" && record3.properties) {
96094
96206
  const properties = record3.properties;
96095
96207
  const existingRequired = Array.isArray(record3.required) ? record3.required : [];
96096
96208
  const normalizedProps = {};
96097
96209
  for (const [key, value] of Object.entries(properties)) {
96098
- normalizedProps[key] = normalizeSchemaForOpenAI(value, strict, false);
96210
+ normalizedProps[key] = normalizeSchemaForOpenAI(value, strict, false, geminiTarget);
96099
96211
  }
96100
96212
  record3.properties = normalizedProps;
96101
96213
  if (strict) {
@@ -96103,9 +96215,10 @@ function normalizeSchemaForOpenAI(schema, strict = true, topLevel = true) {
96103
96215
  record3.required = Array.from(new Set([...existingRequired, ...allKeys]));
96104
96216
  record3.additionalProperties = false;
96105
96217
  if (topLevel) {
96218
+ const style = geminiTarget ? "nullable" : "union";
96106
96219
  for (const key of allKeys) {
96107
96220
  if (!existingRequired.includes(key)) {
96108
- normalizedProps[key] = makeSchemaNullable(normalizedProps[key]);
96221
+ normalizedProps[key] = makeSchemaNullable(normalizedProps[key], style);
96109
96222
  }
96110
96223
  }
96111
96224
  }
@@ -96115,20 +96228,21 @@ function normalizeSchemaForOpenAI(schema, strict = true, topLevel = true) {
96115
96228
  }
96116
96229
  if ("items" in record3) {
96117
96230
  if (Array.isArray(record3.items)) {
96118
- record3.items = record3.items.map((item) => normalizeSchemaForOpenAI(item, strict, false));
96231
+ record3.items = record3.items.map((item) => normalizeSchemaForOpenAI(item, strict, false, geminiTarget));
96119
96232
  } else {
96120
- record3.items = normalizeSchemaForOpenAI(record3.items, strict, false);
96233
+ record3.items = normalizeSchemaForOpenAI(record3.items, strict, false, geminiTarget);
96121
96234
  }
96122
96235
  }
96123
96236
  for (const key of ["anyOf", "oneOf", "allOf"]) {
96124
96237
  if (key in record3 && Array.isArray(record3[key])) {
96125
- record3[key] = record3[key].map((item) => normalizeSchemaForOpenAI(item, strict, false));
96238
+ record3[key] = record3[key].map((item) => normalizeSchemaForOpenAI(item, strict, false, geminiTarget));
96126
96239
  }
96127
96240
  }
96128
96241
  return record3;
96129
96242
  }
96130
- function convertTools(tools) {
96243
+ function convertTools(tools, model = "") {
96131
96244
  const isGemini = isEnvTruthy(getQueryEnvVar("CLAUDE_CODE_USE_GEMINI"));
96245
+ const geminiTarget = isGeminiTarget(model);
96132
96246
  return tools.filter((t) => t.name !== "ToolSearchTool").map((t) => {
96133
96247
  const schema = { ...t.input_schema ?? { type: "object", properties: {} } };
96134
96248
  if (t.name === "Agent" && schema.properties) {
@@ -96146,7 +96260,7 @@ function convertTools(tools) {
96146
96260
  function: {
96147
96261
  name: t.name,
96148
96262
  description: t.description ?? "",
96149
- parameters: normalizeSchemaForOpenAI(schema, !isGemini)
96263
+ parameters: normalizeSchemaForOpenAI(schema, !isGemini, true, geminiTarget)
96150
96264
  }
96151
96265
  };
96152
96266
  });
@@ -96167,8 +96281,63 @@ function convertChunkUsage(usage) {
96167
96281
  function toOpenAIChatReasoningEffort(effort) {
96168
96282
  return effort === "xhigh" ? "high" : effort;
96169
96283
  }
96284
+ function getOpenAIChatProviderCapabilities(model) {
96285
+ return {
96286
+ supportsParallelToolCalls: openAICompatSupports("parallel-tool-calls", model)
96287
+ };
96288
+ }
96289
+ function collectAdjacentToolMessages(messages, startIndex) {
96290
+ const toolMessages = [];
96291
+ for (let i2 = startIndex;i2 < messages.length && messages[i2]?.role === "tool"; i2++) {
96292
+ toolMessages.push(messages[i2]);
96293
+ }
96294
+ return toolMessages;
96295
+ }
96296
+ function indexToolMessagesById(toolMessages) {
96297
+ const byId = new Map;
96298
+ for (const toolMessage of toolMessages) {
96299
+ if (typeof toolMessage.tool_call_id === "string") {
96300
+ byId.set(toolMessage.tool_call_id, toolMessage);
96301
+ }
96302
+ }
96303
+ return byId;
96304
+ }
96305
+ function splitParallelToolCallTurn(assistantMessage, toolCalls, toolMessagesById) {
96306
+ const serialized = [];
96307
+ toolCalls.forEach((toolCall, index) => {
96308
+ serialized.push({
96309
+ ...assistantMessage,
96310
+ tool_calls: [toolCall],
96311
+ content: index === 0 ? assistantMessage.content : null
96312
+ });
96313
+ const toolResponse = toolMessagesById.get(toolCall.id);
96314
+ if (toolResponse)
96315
+ serialized.push(toolResponse);
96316
+ });
96317
+ return serialized;
96318
+ }
96319
+ function serializeParallelToolCalls(messages, capabilities) {
96320
+ if (capabilities.supportsParallelToolCalls)
96321
+ return messages;
96322
+ const result = [];
96323
+ for (let i2 = 0;i2 < messages.length; i2++) {
96324
+ const message = messages[i2];
96325
+ const toolCalls = message.tool_calls;
96326
+ const isParallelToolTurn = message.role === "assistant" && (toolCalls?.length ?? 0) > 1;
96327
+ if (!isParallelToolTurn || !toolCalls) {
96328
+ result.push(message);
96329
+ continue;
96330
+ }
96331
+ const toolMessages = collectAdjacentToolMessages(messages, i2 + 1);
96332
+ const toolMessagesById = indexToolMessagesById(toolMessages);
96333
+ result.push(...splitParallelToolCallTurn(message, toolCalls, toolMessagesById));
96334
+ i2 += toolMessages.length;
96335
+ }
96336
+ return result;
96337
+ }
96170
96338
  function buildOpenAIRequestBody(params, ctx) {
96171
- const openaiMessages = convertMessages(params.messages, params.system);
96339
+ const capabilities = getOpenAIChatProviderCapabilities(ctx.resolvedModel);
96340
+ const openaiMessages = serializeParallelToolCalls(convertMessages(params.messages, params.system), capabilities);
96172
96341
  const body = {
96173
96342
  model: ctx.resolvedModel,
96174
96343
  messages: openaiMessages,
@@ -96196,9 +96365,12 @@ function buildOpenAIRequestBody(params, ctx) {
96196
96365
  body.reasoning_effort = toOpenAIChatReasoningEffort(ctx.reasoning.effort);
96197
96366
  }
96198
96367
  if (params.tools && params.tools.length > 0) {
96199
- const converted = convertTools(params.tools);
96368
+ const converted = convertTools(params.tools, ctx.resolvedModel);
96200
96369
  if (converted.length > 0) {
96201
96370
  body.tools = converted;
96371
+ if (!capabilities.supportsParallelToolCalls) {
96372
+ body.parallel_tool_calls = false;
96373
+ }
96202
96374
  if (params.tool_choice) {
96203
96375
  const tc = params.tool_choice;
96204
96376
  if (tc.type === "auto") {
@@ -96294,7 +96466,13 @@ async function* openaiStreamToAnthropic(response, model) {
96294
96466
  })}`);
96295
96467
  }
96296
96468
  }
96297
- if (delta.reasoning_content != null) {
96469
+ let reasoningText = delta.reasoning_content ?? delta.reasoning;
96470
+ if (reasoningText == null && Array.isArray(delta.reasoning_details)) {
96471
+ const parts = delta.reasoning_details.map((d) => d.content ?? d.summary ?? "").filter(Boolean);
96472
+ if (parts.length > 0)
96473
+ reasoningText = parts.join("");
96474
+ }
96475
+ if (reasoningText != null) {
96298
96476
  if (reasoningBlockIndex === null) {
96299
96477
  reasoningBlockIndex = contentBlockIndex;
96300
96478
  contentBlockIndex++;
@@ -96316,7 +96494,7 @@ async function* openaiStreamToAnthropic(response, model) {
96316
96494
  index: reasoningBlockIndex,
96317
96495
  delta: {
96318
96496
  type: "thinking_delta",
96319
- thinking: delta.reasoning_content
96497
+ thinking: reasoningText
96320
96498
  }
96321
96499
  };
96322
96500
  continue;
@@ -96679,6 +96857,22 @@ class OpenAIShimMessages {
96679
96857
  function convertOpenAIResponseToAnthropic(data, model) {
96680
96858
  const choice = data.choices?.[0];
96681
96859
  const content = [];
96860
+ const msg = choice?.message;
96861
+ let reasoningText = msg?.reasoning_content ?? msg?.reasoning;
96862
+ if (reasoningText == null && Array.isArray(msg?.reasoning_details)) {
96863
+ const parts = msg.reasoning_details.map((d) => d.content ?? d.summary ?? "").filter(Boolean);
96864
+ if (parts.length > 0)
96865
+ reasoningText = parts.join(`
96866
+ `);
96867
+ }
96868
+ if (typeof reasoningText === "string" && reasoningText) {
96869
+ content.push({
96870
+ type: "thinking",
96871
+ thinking: reasoningText,
96872
+ signature: "",
96873
+ extra_content: { provenance: "openai-chat" }
96874
+ });
96875
+ }
96682
96876
  const rawContent = choice?.message?.content;
96683
96877
  if (typeof rawContent === "string" && rawContent) {
96684
96878
  content.push({ type: "text", text: rawContent });
@@ -96782,6 +96976,7 @@ var init_shim2 = __esm(() => {
96782
96976
  init_config2();
96783
96977
  init_schemaSanitizer();
96784
96978
  init_providerProfile();
96979
+ init_capabilities2();
96785
96980
  OpenAIShimStream = class OpenAIShimStream {
96786
96981
  generator;
96787
96982
  controller = new AbortController;
@@ -97054,36 +97249,6 @@ var init_anthropic = __esm(() => {
97054
97249
  init_errors6();
97055
97250
  });
97056
97251
 
97057
- // src/providers/openai/capabilities.ts
97058
- function supportsReasoningEffort(model) {
97059
- return /^(o\d|gpt-5|gpt-4\.5)/i.test(model);
97060
- }
97061
- function getOpenAICompatMaxOutputTokens(model) {
97062
- const max = getOpenAIMaxOutputTokens(model);
97063
- if (max === undefined)
97064
- return null;
97065
- return { default: max, upperLimit: max };
97066
- }
97067
- function getOpenAICompatContextWindow(model) {
97068
- return getOpenAIContextWindow(model) ?? null;
97069
- }
97070
- function openAICompatSupports(feature, model) {
97071
- if (feature === "reasoning-effort")
97072
- return supportsReasoningEffort(model);
97073
- return FEATURES_OPENAI_COMPAT.includes(feature);
97074
- }
97075
- var FEATURES_OPENAI_COMPAT;
97076
- var init_capabilities2 = __esm(() => {
97077
- init_openaiContextWindows();
97078
- FEATURES_OPENAI_COMPAT = Object.freeze([
97079
- "streaming",
97080
- "tool-use",
97081
- "image-input",
97082
- "parallel-tool-calls",
97083
- "system-message-top-level"
97084
- ]);
97085
- });
97086
-
97087
97252
  // src/providers/openai/errors.ts
97088
97253
  function readErrorMessage(body) {
97089
97254
  if (!body || typeof body !== "object")
@@ -106603,7 +106768,7 @@ function isValidAdvisorModel(model) {
106603
106768
  return m.includes("opus-4-6") || m.includes("sonnet-4-6") || process.env.USER_TYPE === "ant";
106604
106769
  }
106605
106770
  function getAdvisorUsage(usage) {
106606
- const iterations = usage.iterations;
106771
+ const iterations = usage?.iterations;
106607
106772
  if (!iterations) {
106608
106773
  return [];
106609
106774
  }
@@ -106644,11 +106809,11 @@ function addToTotalModelUsage(cost, usage, model) {
106644
106809
  contextWindow: 0,
106645
106810
  maxOutputTokens: 0
106646
106811
  };
106647
- modelUsage.inputTokens += usage.input_tokens;
106648
- modelUsage.outputTokens += usage.output_tokens;
106649
- modelUsage.cacheReadInputTokens += usage.cache_read_input_tokens ?? 0;
106650
- modelUsage.cacheCreationInputTokens += usage.cache_creation_input_tokens ?? 0;
106651
- modelUsage.webSearchRequests += usage.server_tool_use?.web_search_requests ?? 0;
106812
+ modelUsage.inputTokens += usage?.input_tokens ?? 0;
106813
+ modelUsage.outputTokens += usage?.output_tokens ?? 0;
106814
+ modelUsage.cacheReadInputTokens += usage?.cache_read_input_tokens ?? 0;
106815
+ modelUsage.cacheCreationInputTokens += usage?.cache_creation_input_tokens ?? 0;
106816
+ modelUsage.webSearchRequests += usage?.server_tool_use?.web_search_requests ?? 0;
106652
106817
  modelUsage.costUSD += cost;
106653
106818
  modelUsage.contextWindow = getContextWindowForModel(model, getSdkBetas());
106654
106819
  modelUsage.maxOutputTokens = getModelMaxOutputTokens(model).default;
@@ -106657,15 +106822,18 @@ function addToTotalModelUsage(cost, usage, model) {
106657
106822
  function addToTotalSessionCost(cost, usage, model) {
106658
106823
  const modelUsage = addToTotalModelUsage(cost, usage, model);
106659
106824
  addToTotalCostState(cost, modelUsage, model);
106660
- const attrs = isFastModeEnabled() && usage.speed === "fast" ? { model, speed: "fast" } : { model };
106825
+ const attrs = isFastModeEnabled() && usage?.speed === "fast" ? { model, speed: "fast" } : { model };
106661
106826
  getCostCounter()?.add(cost, attrs);
106662
- getTokenCounter()?.add(usage.input_tokens, { ...attrs, type: "input" });
106663
- getTokenCounter()?.add(usage.output_tokens, { ...attrs, type: "output" });
106664
- getTokenCounter()?.add(usage.cache_read_input_tokens ?? 0, {
106827
+ getTokenCounter()?.add(usage?.input_tokens ?? 0, { ...attrs, type: "input" });
106828
+ getTokenCounter()?.add(usage?.output_tokens ?? 0, {
106829
+ ...attrs,
106830
+ type: "output"
106831
+ });
106832
+ getTokenCounter()?.add(usage?.cache_read_input_tokens ?? 0, {
106665
106833
  ...attrs,
106666
106834
  type: "cacheRead"
106667
106835
  });
106668
- getTokenCounter()?.add(usage.cache_creation_input_tokens ?? 0, {
106836
+ getTokenCounter()?.add(usage?.cache_creation_input_tokens ?? 0, {
106669
106837
  ...attrs,
106670
106838
  type: "cacheCreation"
106671
106839
  });
@@ -106944,10 +107112,10 @@ function getCLISyspromptPrefix(options) {
106944
107112
  return DEFAULT_PREFIX;
106945
107113
  }
106946
107114
  function isAttributionHeaderEnabled() {
106947
- if (isEnvDefinedFalsy(resolveEnvVar("ATTRIBUTION_HEADER"))) {
106948
- return false;
107115
+ if (isEnvTruthy(resolveEnvVar("ATTRIBUTION_HEADER"))) {
107116
+ return true;
106949
107117
  }
106950
- return getFeatureValue_CACHED_MAY_BE_STALE("tengu_attribution_header", true);
107118
+ return getFeatureValue_CACHED_MAY_BE_STALE("tengu_attribution_header", false);
106951
107119
  }
106952
107120
  function getAttributionHeader(fingerprint) {
106953
107121
  if (!isAttributionHeaderEnabled()) {
@@ -106958,7 +107126,7 @@ function getAttributionHeader(fingerprint) {
106958
107126
  const cch = "";
106959
107127
  const workload = getWorkload();
106960
107128
  const workloadPair = workload ? ` cc_workload=${workload};` : "";
106961
- const header = `x-anthropic-billing-header: cc_version=${version2}; cc_entrypoint=${entrypoint};${cch}${workloadPair}`;
107129
+ const header = `x-opencow-billing-header: cc_version=${version2}; cc_entrypoint=${entrypoint};${cch}${workloadPair}`;
106962
107130
  logForDebugging(`attribution header ${header}`);
106963
107131
  return header;
106964
107132
  }
@@ -282088,7 +282256,7 @@ function getAnthropicEnvMetadata() {
282088
282256
  function getBuildAgeMinutes() {
282089
282257
  if (false)
282090
282258
  ;
282091
- const buildTime = new Date("2026-05-25T12:12:24.294Z").getTime();
282259
+ const buildTime = new Date("2026-06-03T08:42:39.310Z").getTime();
282092
282260
  if (isNaN(buildTime))
282093
282261
  return;
282094
282262
  return Math.floor((Date.now() - buildTime) / 60000);
@@ -292368,7 +292536,7 @@ function splitSysPromptPrefix(systemPrompt, options2) {
292368
292536
  continue;
292369
292537
  if (prompt === SYSTEM_PROMPT_DYNAMIC_BOUNDARY)
292370
292538
  continue;
292371
- if (prompt.startsWith("x-anthropic-billing-header")) {
292539
+ if (prompt.startsWith("x-opencow-billing-header")) {
292372
292540
  attributionHeader2 = prompt;
292373
292541
  } else if (CLI_SYSPROMPT_PREFIXES.has(prompt)) {
292374
292542
  systemPromptPrefix2 = prompt;
@@ -292402,7 +292570,7 @@ function splitSysPromptPrefix(systemPrompt, options2) {
292402
292570
  const block2 = systemPrompt[i3];
292403
292571
  if (!block2 || block2 === SYSTEM_PROMPT_DYNAMIC_BOUNDARY)
292404
292572
  continue;
292405
- if (block2.startsWith("x-anthropic-billing-header")) {
292573
+ if (block2.startsWith("x-opencow-billing-header")) {
292406
292574
  attributionHeader2 = block2;
292407
292575
  } else if (CLI_SYSPROMPT_PREFIXES.has(block2)) {
292408
292576
  systemPromptPrefix2 = block2;
@@ -292445,7 +292613,7 @@ function splitSysPromptPrefix(systemPrompt, options2) {
292445
292613
  for (const block2 of systemPrompt) {
292446
292614
  if (!block2)
292447
292615
  continue;
292448
- if (block2.startsWith("x-anthropic-billing-header")) {
292616
+ if (block2.startsWith("x-opencow-billing-header")) {
292449
292617
  attributionHeader = block2;
292450
292618
  } else if (CLI_SYSPROMPT_PREFIXES.has(block2)) {
292451
292619
  systemPromptPrefix = block2;
@@ -335268,4 +335436,4 @@ export {
335268
335436
  AbortError2 as AbortError
335269
335437
  };
335270
335438
 
335271
- //# debugId=5554484DB05DB0BC64756E2164756E21
335439
+ //# debugId=5C1C50C89C84EDAD64756E2164756E21
@@ -6,13 +6,34 @@
6
6
  * issue #77:gpt 用 read 工具频繁出错)。让字段可空后,模型可以用 `null` 明确表达
7
7
  * 「未提供」,再由入参解析层把 `null` 还原为缺省。
8
8
  *
9
- * 仅处理能安全增补的形态,限制 blast radius:
10
- * - 具名 `type`(string/number/...)→ 追加 `"null"`
11
- * - `type` 数组 追加 `"null"`
12
- * `enum`/`const` 的字段保持不动(避免 type 允许 null 而枚举/常量不含 null 的
13
- * 自相矛盾);仅含 anyOf/oneOf 等组合子或无明确 `type` 的字段也保持不动(零回归)。
9
+ * 可空的两种写法没有「最大公约数」,必须按目标 provider 选择:
10
+ * - `'union'`(默认):`type: [T, "null"]`。OpenAI / Codex 的 **strict 结构化输出
11
+ * 只接受这种**(`nullable` OpenAPI 扩展,不在其 JSON-Schema 子集里,会被
12
+ * strict 校验拒绝 400);DeepSeek / Kimi / Qwen 同样接受。
13
+ * - `'nullable'`:`nullable: true`。Gemini / Vertex 只接受这种,拒绝 type 数组。
14
+ *
15
+ * 注意 OpenAI chat(非 strict)会忽略 `nullable`,从而丢掉「可传 null」的提示,
16
+ * 退回 issue #77 的形态——所以对 OpenAI 路径必须用 `'union'`。Gemini 路径由调用方
17
+ * (normalizeSchemaForOpenAI)传 `'nullable'`,或经 splitTypeArrayToAnyOf 转换。
18
+ *
19
+ * 带 `enum`/`const` 的字段保持不动(避免自相矛盾);
20
+ * 无明确 `type` 的字段也保持不动(零回归)。
21
+ */
22
+ export declare function makeSchemaNullable(schema: Record<string, unknown>, style?: 'union' | 'nullable'): Record<string, unknown>;
23
+ /**
24
+ * 把 type 数组改写为 **Gemini/Vertex** 能接受的形式——仅在目标是 Gemini 时调用
25
+ * (见 normalizeSchemaForOpenAI 的 geminiTarget 门控)。OpenAI/DeepSeek/Kimi 等接受
26
+ * type 数组、且对 OpenAI 而言 `nullable` 会被忽略,故对它们不应调用本函数。
27
+ *
28
+ * Gemini 侧约束:拒绝 type 数组,拒绝 anyOf + null,只接受 nullable: true。
29
+ *
30
+ * 转换策略:
31
+ * - `type: ["T", "null"]` → `{ type: "T", nullable: true, ...结构性关键字 }`
32
+ * - `type: ["T1", "T2", ...]`(>1 种非 null 类型)→ 拆为 anyOf,null 以 nullable 表达
33
+ *
34
+ * 不递归——由调用方(normalizeSchemaForOpenAI)的递归遍历负责嵌套层级。
14
35
  */
15
- export declare function makeSchemaNullable(schema: Record<string, unknown>): Record<string, unknown>;
36
+ export declare function splitTypeArrayToAnyOf(schema: Record<string, unknown>): Record<string, unknown>;
16
37
  /**
17
38
  * Sanitize JSON Schema into a shape OpenAI-compatible providers and Codex
18
39
  * strict-mode tooling are more likely to accept. This strips provider-rejected
@@ -1,4 +1,12 @@
1
1
  import type { ModelOutputTokenCap, ProviderFeature } from '../types.js';
2
+ /**
3
+ * 判断最终目标是否为 Gemini/Vertex 系列。
4
+ *
5
+ * 这是 Gemini 专属适配(关闭并行工具调用、schema 用 nullable:true 而非 type 数组)的
6
+ * 统一判定入口。后端纯透传时 SDK 侧 family 恒为 openai,无法据此识别最终 provider,
7
+ * 因此依据 CLAUDE_CODE_USE_GEMINI 环境标志 + model 名两路信号判断。
8
+ */
9
+ export declare function isGeminiTarget(model: string): boolean;
2
10
  export declare function getOpenAICompatMaxOutputTokens(model: string): ModelOutputTokenCap | null;
3
11
  export declare function getOpenAICompatContextWindow(model: string): number | null;
4
12
  export declare function openAICompatSupports(feature: ProviderFeature, model: string): boolean;
@@ -23,7 +23,7 @@
23
23
  import { type AnthropicStreamEvent, type ShimCreateParams } from '../../providers/codex/shim.js';
24
24
  interface OpenAIMessage {
25
25
  role: 'system' | 'user' | 'assistant' | 'tool';
26
- content?: string | Array<{
26
+ content?: string | null | Array<{
27
27
  type: string;
28
28
  text?: string;
29
29
  image_url?: {
@@ -65,7 +65,7 @@ export declare function convertTools(tools: Array<{
65
65
  name: string;
66
66
  description?: string;
67
67
  input_schema?: Record<string, unknown>;
68
- }>): OpenAITool[];
68
+ }>, model?: string): OpenAITool[];
69
69
  export declare function makeMessageId(): string;
70
70
  /**
71
71
  * Async generator that transforms an OpenAI SSE stream into
@@ -146,6 +146,13 @@ export declare function convertOpenAIResponseToAnthropic(data: {
146
146
  };
147
147
  extra_content?: Record<string, unknown>;
148
148
  }>;
149
+ reasoning_content?: string | null;
150
+ reasoning?: string | null;
151
+ reasoning_details?: Array<{
152
+ type?: string;
153
+ content?: string;
154
+ summary?: string;
155
+ }> | null;
149
156
  };
150
157
  finish_reason?: string;
151
158
  }>;