@kenkaiiii/gg-ai 5.68.1 → 5.69.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.
package/dist/index.cjs CHANGED
@@ -35,14 +35,17 @@ __export(index_exports, {
35
35
  ProviderError: () => ProviderError,
36
36
  REDACTION_MARKER: () => REDACTED,
37
37
  StreamResult: () => StreamResult,
38
+ TOOL_CALL_NAME_RULES: () => TOOL_CALL_NAME_RULES,
38
39
  clampProviderContextImages: () => clampProviderContextImages,
39
40
  classifyProviderError: () => classifyProviderError,
41
+ dropInvalidToolCalls: () => dropInvalidToolCalls,
40
42
  environmentSecrets: () => environmentSecrets,
41
43
  formatError: () => formatError,
42
44
  formatErrorForDisplay: () => formatErrorForDisplay,
43
45
  hasLoneSurrogate: () => hasLoneSurrogate,
44
46
  isHardBillingMessage: () => isHardBillingMessage,
45
47
  isUsageLimitError: () => isUsageLimitError,
48
+ isValidToolCallName: () => isValidToolCallName,
46
49
  localWireModelId: () => localWireModelId,
47
50
  palsuAssistantMessage: () => palsuAssistantMessage,
48
51
  palsuText: () => palsuText,
@@ -61,7 +64,8 @@ __export(index_exports, {
61
64
  stream: () => stream,
62
65
  toAnthropicMessages: () => toAnthropicMessages,
63
66
  toOpenAIMessages: () => toOpenAIMessages,
64
- toWellFormedText: () => toWellFormedText
67
+ toWellFormedText: () => toWellFormedText,
68
+ toolCallNameRuleFor: () => toolCallNameRuleFor
65
69
  });
66
70
  module.exports = __toCommonJS(index_exports);
67
71
 
@@ -515,7 +519,11 @@ function zodToJsonSchema(schema) {
515
519
  return normalized;
516
520
  }
517
521
  function resolveToolSchema(tool) {
518
- return tool.rawInputSchema ?? zodToJsonSchema(tool.parameters);
522
+ const schema = tool.rawInputSchema ?? zodToJsonSchema(tool.parameters);
523
+ if (schema.type === "object" && schema.properties === void 0) {
524
+ return { ...schema, properties: {} };
525
+ }
526
+ return schema;
519
527
  }
520
528
  function normalizeRootForAnthropic(schema) {
521
529
  const branches = schema.oneOf ?? schema.anyOf;
@@ -722,6 +730,10 @@ var ANTHROPIC_INPUT_BLOCK_TYPES = /* @__PURE__ */ new Set([
722
730
  "connector_text",
723
731
  "container_upload",
724
732
  "document",
733
+ // Server-side refusal fallback marker. Only reaches the wire when the request
734
+ // carries the server-side-fallback beta; otherwise applyServerFallbackReplay
735
+ // strips it first (see toAnthropicMessages' `fallbackBlocks` option).
736
+ "fallback",
725
737
  "image",
726
738
  "mid_conv_system",
727
739
  "redacted_thinking",
@@ -743,6 +755,42 @@ function isPositionSensitiveThinking(part) {
743
755
  if (part.type === "thinking") return hasValidThinkingSignature(part);
744
756
  return isRawThinking(part);
745
757
  }
758
+ function isServerFallbackBlock(part) {
759
+ return part.type === "raw" && part.data.type === "fallback";
760
+ }
761
+ function applyServerFallbackReplay(content, keepMarkers, droppedToolCallIds) {
762
+ let lastFallbackIdx = -1;
763
+ content.forEach((part, idx) => {
764
+ if (isServerFallbackBlock(part)) lastFallbackIdx = idx;
765
+ });
766
+ if (lastFallbackIdx === -1) return content;
767
+ const resultIds = /* @__PURE__ */ new Set();
768
+ for (const part of content) {
769
+ if (part.type === "server_tool_result") resultIds.add(part.toolUseId);
770
+ else if (part.type === "raw" && typeof part.data.tool_use_id === "string")
771
+ resultIds.add(part.data.tool_use_id);
772
+ }
773
+ const out = [];
774
+ content.forEach((part, idx) => {
775
+ if (isServerFallbackBlock(part)) {
776
+ if (keepMarkers) out.push(part);
777
+ return;
778
+ }
779
+ if (idx < lastFallbackIdx) {
780
+ if (part.type === "thinking" || isRawThinking(part)) return;
781
+ if (part.type === "raw" && part.data.type === "connector_text") return;
782
+ if (part.type === "tool_call") {
783
+ droppedToolCallIds?.add(part.id);
784
+ return;
785
+ }
786
+ if (part.type === "server_tool_call" && !resultIds.has(part.id)) return;
787
+ if (part.type === "raw" && part.data.type === "server_tool_use" && !resultIds.has(String(part.data.id)))
788
+ return;
789
+ }
790
+ out.push(part);
791
+ });
792
+ return out.every(isServerFallbackBlock) ? [] : out;
793
+ }
746
794
  function toAnthropicAssistantPart(part, idMap) {
747
795
  if (part.type === "text") return { type: "text", text: part.text };
748
796
  if (part.type === "thinking") {
@@ -854,6 +902,144 @@ function clampProviderContextImages(messages, provider, supportsImages) {
854
902
  return message;
855
903
  });
856
904
  }
905
+ var TOOL_CALL_NAME_RULES = {
906
+ // Anthropic Messages API: tool / tool_use name `^[a-zA-Z0-9_-]{1,128}$`.
907
+ anthropic: { id: "anthropic", maxLength: 128, pattern: /^[a-zA-Z0-9_-]{1,128}$/ },
908
+ // OpenAI Chat Completions: function name a-z A-Z 0-9 _ -, max length 64.
909
+ "openai-chat": { id: "openai-chat", maxLength: 64, pattern: /^[a-zA-Z0-9_-]{1,64}$/ },
910
+ // OpenAI Responses (Codex): same charset; replayed `input[N].name` over 128
911
+ // chars is rejected with `string_above_max_length`.
912
+ "openai-responses": {
913
+ id: "openai-responses",
914
+ maxLength: 128,
915
+ pattern: /^[a-zA-Z0-9_-]{1,128}$/
916
+ },
917
+ // Gemini FunctionDeclaration name: starts with a letter or underscore, then
918
+ // a-z A-Z 0-9 _ . : -, max length 128.
919
+ gemini: { id: "gemini", maxLength: 128, pattern: /^[a-zA-Z_][a-zA-Z0-9_.:-]{0,127}$/ },
920
+ // OpenAI-compatible third parties (GLM, Kimi, DeepSeek, OpenRouter, xAI,
921
+ // local servers, MiniMax, …) don't share one documented charset, so only
922
+ // reject what no declared tool name can contain: blank, >128 chars, or any
923
+ // whitespace / control character (the signature of invocation text).
924
+ generic: { id: "generic", maxLength: 128 }
925
+ };
926
+ var TOOL_NAME_FORBIDDEN_CHARS = /[\s\p{Cc}]/u;
927
+ function isValidToolCallName(name, rule) {
928
+ if (typeof name !== "string" || name.trim().length === 0) return false;
929
+ if (name.length > rule.maxLength) return false;
930
+ if (TOOL_NAME_FORBIDDEN_CHARS.test(name)) return false;
931
+ return rule.pattern ? rule.pattern.test(name) : true;
932
+ }
933
+ function isDefaultOrHost(baseUrl, host) {
934
+ if (!baseUrl) return true;
935
+ try {
936
+ return new URL(baseUrl).hostname === host;
937
+ } catch {
938
+ return false;
939
+ }
940
+ }
941
+ function toolCallNameRuleFor(provider, options) {
942
+ switch (provider) {
943
+ case "anthropic":
944
+ return TOOL_CALL_NAME_RULES.anthropic;
945
+ case "openai":
946
+ if (options?.accountId) return TOOL_CALL_NAME_RULES["openai-responses"];
947
+ return isDefaultOrHost(options?.baseUrl, "api.openai.com") ? TOOL_CALL_NAME_RULES["openai-chat"] : TOOL_CALL_NAME_RULES.generic;
948
+ case "gemini":
949
+ return TOOL_CALL_NAME_RULES.gemini;
950
+ default:
951
+ return TOOL_CALL_NAME_RULES.generic;
952
+ }
953
+ }
954
+ function isValidToolCallId(id) {
955
+ return typeof id === "string" && id.trim().length > 0;
956
+ }
957
+ function isRawReasoning(part) {
958
+ return part.type === "raw" && part.data.type === "reasoning";
959
+ }
960
+ function isDanglingReasoning(parts, idx) {
961
+ for (let i = idx + 1; i < parts.length; i++) {
962
+ const next = parts[i];
963
+ if (isRawReasoning(next)) return true;
964
+ if (next.type === "text" || next.type === "tool_call") return false;
965
+ }
966
+ return true;
967
+ }
968
+ function hasReplayableAssistantContent(parts) {
969
+ return parts.some((part) => {
970
+ if (part.type === "text") return part.text.length > 0;
971
+ if (part.type === "thinking") return false;
972
+ if (part.type === "raw") {
973
+ const t = part.data.type;
974
+ return !(t === "thinking" || t === "redacted_thinking" || t === "reasoning" || t === "fallback");
975
+ }
976
+ return true;
977
+ });
978
+ }
979
+ function dropInvalidToolCalls(messages, rule) {
980
+ const isInvalid = (part) => part.type === "tool_call" && (!isValidToolCallName(part.name, rule) || !isValidToolCallId(part.id));
981
+ const hasInvalid = messages.some(
982
+ (m) => m.role === "assistant" && Array.isArray(m.content) && m.content.some(isInvalid)
983
+ );
984
+ if (!hasInvalid) return messages;
985
+ const out = [];
986
+ let pending = /* @__PURE__ */ new Map();
987
+ let prunedAssistant = null;
988
+ for (const msg of messages) {
989
+ if (msg.role === "user" || msg.role === "system") {
990
+ if (msg.role === "user") pending = /* @__PURE__ */ new Map();
991
+ out.push(msg);
992
+ continue;
993
+ }
994
+ if (msg.role === "assistant") {
995
+ pending = /* @__PURE__ */ new Map();
996
+ if (typeof msg.content === "string") {
997
+ out.push(msg);
998
+ continue;
999
+ }
1000
+ const original = msg.content;
1001
+ let dropped = false;
1002
+ const kept = [];
1003
+ for (const part of original) {
1004
+ if (part.type === "tool_call") {
1005
+ const keep = !isInvalid(part);
1006
+ const queue = pending.get(part.id) ?? [];
1007
+ queue.push(keep);
1008
+ pending.set(part.id, queue);
1009
+ if (!keep) {
1010
+ dropped = true;
1011
+ continue;
1012
+ }
1013
+ }
1014
+ kept.push(part);
1015
+ }
1016
+ if (!dropped) {
1017
+ out.push(msg);
1018
+ continue;
1019
+ }
1020
+ const content = kept.filter(
1021
+ (part, idx) => !isRawReasoning(part) || !isDanglingReasoning(kept, idx) || isDanglingReasoning(original, original.indexOf(part))
1022
+ );
1023
+ if (hasReplayableAssistantContent(content)) {
1024
+ const pruned = { ...msg, content };
1025
+ out.push(pruned);
1026
+ prunedAssistant = pruned;
1027
+ }
1028
+ continue;
1029
+ }
1030
+ let changed = false;
1031
+ const results = msg.content.filter((result) => {
1032
+ const queue = pending.get(result.toolCallId);
1033
+ const keep = queue && queue.length > 0 ? queue.shift() : true;
1034
+ if (!keep) changed = true;
1035
+ return keep;
1036
+ });
1037
+ if (!changed) out.push(msg);
1038
+ else if (results.length > 0) out.push({ ...msg, content: results });
1039
+ }
1040
+ if (prunedAssistant && out[out.length - 1] === prunedAssistant) out.pop();
1041
+ return out;
1042
+ }
857
1043
  var NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
858
1044
  var NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
859
1045
  var NON_VIDEO_USER_PLACEHOLDER = "(video omitted: model does not support video)";
@@ -960,10 +1146,12 @@ function remapAnthropicToolCallId(id, idMap) {
960
1146
  idMap.set(id, mapped);
961
1147
  return mapped;
962
1148
  }
963
- function toAnthropicMessages(messages, cacheControl) {
1149
+ function toAnthropicMessages(messages, cacheControl, options) {
964
1150
  let systemText;
965
1151
  const out = [];
966
1152
  const idMap = /* @__PURE__ */ new Map();
1153
+ const keepFallbackBlocks = options?.fallbackBlocks === true;
1154
+ const droppedToolCallIds = /* @__PURE__ */ new Set();
967
1155
  const trajectoryStartIdx = messages.reduce(
968
1156
  (last, m, i) => m.role === "user" ? i : last,
969
1157
  -1
@@ -1009,17 +1197,23 @@ function toAnthropicMessages(messages, cacheControl) {
1009
1197
  }
1010
1198
  if (msg.role === "assistant") {
1011
1199
  if (typeof msg.content === "string" && msg.content === "") continue;
1012
- const content = typeof msg.content === "string" ? msg.content : toAnthropicAssistantContent(msg.content, msgIdx > trajectoryStartIdx, idMap);
1200
+ const content = typeof msg.content === "string" ? msg.content : toAnthropicAssistantContent(
1201
+ applyServerFallbackReplay(msg.content, keepFallbackBlocks, droppedToolCallIds),
1202
+ msgIdx > trajectoryStartIdx,
1203
+ idMap
1204
+ );
1013
1205
  if (Array.isArray(content) && content.length === 0) continue;
1014
1206
  out.push({ role: "assistant", content });
1015
1207
  continue;
1016
1208
  }
1017
1209
  if (msg.role === "tool") {
1210
+ const results = droppedToolCallIds.size ? msg.content.filter((r) => !droppedToolCallIds.has(r.toolCallId)) : msg.content;
1211
+ if (results.length === 0) continue;
1018
1212
  out.push({
1019
1213
  role: "user",
1020
1214
  // Cast covers the video block (used by the Anthropic-compatible MiniMax
1021
1215
  // API), which isn't in the first-party Anthropic tool_result types.
1022
- content: msg.content.map((result) => ({
1216
+ content: results.map((result) => ({
1023
1217
  type: "tool_result",
1024
1218
  tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
1025
1219
  content: toAnthropicToolResultContent(result.content),
@@ -1359,6 +1553,48 @@ function fineGrainedToolStreamingEnabled() {
1359
1553
  const v = raw.trim().toLowerCase();
1360
1554
  return v === "1" || v === "true" || v === "yes" || v === "on";
1361
1555
  }
1556
+ var SERVER_FALLBACK_BETA = "server-side-fallback-2026-07-01";
1557
+ var anthropicServerFallback = {
1558
+ disabled: /* @__PURE__ */ new Set(),
1559
+ reset() {
1560
+ this.disabled.clear();
1561
+ }
1562
+ };
1563
+ function serverFallbackKey(options) {
1564
+ const auth = options.apiKey?.startsWith("sk-ant-oat") ? "oauth" : "key";
1565
+ return `${options.baseUrl ?? process.env.ANTHROPIC_BASE_URL ?? ""}|${auth}`;
1566
+ }
1567
+ function isDirectAnthropicApi(baseUrl) {
1568
+ const effective = baseUrl ?? process.env.ANTHROPIC_BASE_URL;
1569
+ if (!effective) return true;
1570
+ try {
1571
+ const url = new URL(effective);
1572
+ return url.protocol === "https:" && url.hostname === "api.anthropic.com";
1573
+ } catch {
1574
+ return false;
1575
+ }
1576
+ }
1577
+ function isServerFallbackRejection(err) {
1578
+ const status = err?.status;
1579
+ if (status !== 400) return false;
1580
+ const message = err instanceof Error ? err.message : String(err);
1581
+ return /fallbacks|server-side-fallback/i.test(message);
1582
+ }
1583
+ function sumUsageIterations(usage) {
1584
+ const iterations = usage?.iterations;
1585
+ if (!Array.isArray(iterations) || iterations.length === 0) return null;
1586
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
1587
+ const totals = { inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0 };
1588
+ for (const it of iterations) {
1589
+ if (!it || typeof it !== "object") continue;
1590
+ const rec = it;
1591
+ totals.inputTokens += num(rec.input_tokens);
1592
+ totals.outputTokens += num(rec.output_tokens);
1593
+ totals.cacheRead += num(rec.cache_read_input_tokens);
1594
+ totals.cacheWrite += num(rec.cache_creation_input_tokens);
1595
+ }
1596
+ return totals;
1597
+ }
1362
1598
  function createClient(options) {
1363
1599
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
1364
1600
  const userAgent = isOAuth ? options.userAgent ?? "claude-cli/2.1.75 (external, cli)" : "";
@@ -1455,7 +1691,11 @@ async function* runStream(options) {
1455
1691
  const supportsFirstPartyToolExtras = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
1456
1692
  const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
1457
1693
  const downgradedMessages = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
1458
- const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl);
1694
+ const fallbackKey = serverFallbackKey(options);
1695
+ const useServerFallback = options.provider !== "minimax" && isDirectAnthropicApi(options.baseUrl) && !anthropicServerFallback.disabled.has(fallbackKey);
1696
+ const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl, {
1697
+ fallbackBlocks: useServerFallback
1698
+ });
1459
1699
  const system = isOAuth ? [
1460
1700
  {
1461
1701
  type: "text",
@@ -1525,6 +1765,7 @@ async function* runStream(options) {
1525
1765
  ];
1526
1766
  return contextEdits.length ? { context_management: { edits: contextEdits } } : {};
1527
1767
  })(),
1768
+ ...useServerFallback ? { fallbacks: "default" } : {},
1528
1769
  stream: useStreaming
1529
1770
  };
1530
1771
  const hasAdaptiveThinking = isAdaptiveThinkingModel(options.model);
@@ -1541,20 +1782,41 @@ async function* runStream(options) {
1541
1782
  // it Anthropic silently ignores ttl:"1h" and falls back to the 5-min default,
1542
1783
  // so a pre-warmed cache expires before the user's first turn. cacheControl.ttl
1543
1784
  // is only "1h" on the first-party endpoint (see toAnthropicCacheControl).
1544
- ...cacheControl?.ttl === "1h" ? ["extended-cache-ttl-2025-04-11"] : []
1785
+ ...cacheControl?.ttl === "1h" ? ["extended-cache-ttl-2025-04-11"] : [],
1786
+ ...useServerFallback ? [SERVER_FALLBACK_BETA] : []
1545
1787
  ];
1546
- const requestOptions = {
1788
+ const toRequestOptions = (betas) => ({
1547
1789
  signal: options.signal ?? void 0,
1548
- ...betaHeaders.length ? { headers: { "anthropic-beta": betaHeaders.join(",") } } : {}
1790
+ ...betas.length ? { headers: { "anthropic-beta": betas.join(",") } } : {}
1791
+ });
1792
+ const requestOptions = toRequestOptions(betaHeaders);
1793
+ const send = async (create) => {
1794
+ try {
1795
+ return await create(params, requestOptions);
1796
+ } catch (err) {
1797
+ if (!useServerFallback || !isServerFallbackRejection(err)) throw err;
1798
+ anthropicServerFallback.disabled.add(fallbackKey);
1799
+ const { fallbacks: _dropped, ...rest } = params;
1800
+ const retryParams = {
1801
+ ...rest,
1802
+ messages: toAnthropicMessages(downgradedMessages, cacheControl, { fallbackBlocks: false }).messages
1803
+ };
1804
+ return create(
1805
+ retryParams,
1806
+ toRequestOptions(betaHeaders.filter((b) => b !== SERVER_FALLBACK_BETA))
1807
+ );
1808
+ }
1549
1809
  };
1550
1810
  if (!useStreaming) {
1551
1811
  try {
1552
1812
  const nonStreamingClient = client.withOptions({
1553
1813
  timeout: NON_STREAMING_REQUEST_TIMEOUT_MS
1554
1814
  });
1555
- const message = await nonStreamingClient.messages.create(
1556
- { ...params, stream: false },
1557
- requestOptions
1815
+ const message = await send(
1816
+ (p, o) => nonStreamingClient.messages.create(
1817
+ { ...p, stream: false },
1818
+ o
1819
+ )
1558
1820
  );
1559
1821
  yield* synthesizeEventsFromMessage(message);
1560
1822
  return messageToResponse(message);
@@ -1572,9 +1834,8 @@ async function* runStream(options) {
1572
1834
  const keepalive = { type: "keepalive" };
1573
1835
  let receivedAnyEvent = false;
1574
1836
  try {
1575
- const stream2 = await client.messages.create(
1576
- params,
1577
- requestOptions
1837
+ const stream2 = await send(
1838
+ (p, o) => client.messages.create(p, o)
1578
1839
  );
1579
1840
  for await (const event of stream2) {
1580
1841
  receivedAnyEvent = true;
@@ -1752,6 +2013,13 @@ async function* runStream(options) {
1752
2013
  if (usage?.output_tokens != null) {
1753
2014
  outputTokens = usage.output_tokens;
1754
2015
  }
2016
+ const totals = sumUsageIterations(usage);
2017
+ if (totals) {
2018
+ inputTokens = totals.inputTokens;
2019
+ outputTokens = totals.outputTokens;
2020
+ cacheRead = totals.cacheRead;
2021
+ cacheWrite = totals.cacheWrite;
2022
+ }
1755
2023
  yield keepalive;
1756
2024
  break;
1757
2025
  }
@@ -1881,10 +2149,11 @@ function messageToResponse(message) {
1881
2149
  }
1882
2150
  }
1883
2151
  const usage = message.usage;
1884
- const inputTokens = usage.input_tokens ?? 0;
1885
- const outputTokens = usage.output_tokens ?? 0;
1886
- const cacheRead = usage.cache_read_input_tokens;
1887
- const cacheWrite = usage.cache_creation_input_tokens;
2152
+ const totals = sumUsageIterations(usage);
2153
+ const inputTokens = totals?.inputTokens ?? usage.input_tokens ?? 0;
2154
+ const outputTokens = totals?.outputTokens ?? usage.output_tokens ?? 0;
2155
+ const cacheRead = totals?.cacheRead ?? usage.cache_read_input_tokens;
2156
+ const cacheWrite = totals?.cacheWrite ?? usage.cache_creation_input_tokens;
1888
2157
  return {
1889
2158
  message: {
1890
2159
  role: "assistant",
@@ -4092,15 +4361,23 @@ function stream(options) {
4092
4361
  throw new VideoUnsupportedError();
4093
4362
  }
4094
4363
  const wireMessages = stripMessageProvenance(options.messages);
4095
- const messages = clampProviderContextImages(
4096
- sanitizeMessagesForWire(wireMessages),
4097
- options.provider,
4098
- options.supportsImages
4364
+ const sanitized = sanitizeMessagesForWire(wireMessages);
4365
+ const replayable = dropInvalidToolCalls(
4366
+ sanitized,
4367
+ toolCallNameRuleFor(options.provider, {
4368
+ accountId: options.accountId,
4369
+ baseUrl: options.baseUrl
4370
+ })
4099
4371
  );
4372
+ const messages = clampProviderContextImages(replayable, options.provider, options.supportsImages);
4100
4373
  if (options.onContextPrepared) {
4101
4374
  try {
4102
4375
  options.onContextPrepared(
4103
- observePreparedContext(wireMessages, messages, options.tools ?? [])
4376
+ observePreparedContext(
4377
+ replayable === sanitized ? wireMessages : replayable,
4378
+ messages,
4379
+ options.tools ?? []
4380
+ )
4104
4381
  );
4105
4382
  } catch {
4106
4383
  }
@@ -4516,14 +4793,17 @@ function registerPalsuProvider(config) {
4516
4793
  ProviderError,
4517
4794
  REDACTION_MARKER,
4518
4795
  StreamResult,
4796
+ TOOL_CALL_NAME_RULES,
4519
4797
  clampProviderContextImages,
4520
4798
  classifyProviderError,
4799
+ dropInvalidToolCalls,
4521
4800
  environmentSecrets,
4522
4801
  formatError,
4523
4802
  formatErrorForDisplay,
4524
4803
  hasLoneSurrogate,
4525
4804
  isHardBillingMessage,
4526
4805
  isUsageLimitError,
4806
+ isValidToolCallName,
4527
4807
  localWireModelId,
4528
4808
  palsuAssistantMessage,
4529
4809
  palsuText,
@@ -4542,6 +4822,7 @@ function registerPalsuProvider(config) {
4542
4822
  stream,
4543
4823
  toAnthropicMessages,
4544
4824
  toOpenAIMessages,
4545
- toWellFormedText
4825
+ toWellFormedText,
4826
+ toolCallNameRuleFor
4546
4827
  });
4547
4828
  //# sourceMappingURL=index.cjs.map