@kenkaiiii/gg-ai 5.87.3 → 5.88.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,6 +35,7 @@ __export(index_exports, {
35
35
  EventStream: () => EventStream,
36
36
  FAST_MODE_SUFFIX: () => FAST_MODE_SUFFIX,
37
37
  GGAIError: () => GGAIError,
38
+ PRECONNECT_INTERVAL_MS: () => PRECONNECT_INTERVAL_MS,
38
39
  ProviderError: () => ProviderError,
39
40
  REDACTION_MARKER: () => REDACTED,
40
41
  ResponseStalledError: () => ResponseStalledError,
@@ -66,6 +67,8 @@ __export(index_exports, {
66
67
  palsuToolCall: () => palsuToolCall,
67
68
  parseFastModeRejection: () => parseFastModeRejection,
68
69
  pendingClaudeCodeVersionRefresh: () => pendingClaudeCodeVersionRefresh,
70
+ preconnectOrigin: () => preconnectOrigin,
71
+ preconnectProvider: () => preconnectProvider,
69
72
  prewarmAnthropicCache: () => prewarmAnthropicCache,
70
73
  providerRegistry: () => providerRegistry,
71
74
  redactText: () => redactText,
@@ -488,6 +491,26 @@ function providerGuidance(provider, message, statusCode) {
488
491
  // src/providers/anthropic.ts
489
492
  var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
490
493
 
494
+ // src/providers/cache-minimums.ts
495
+ var ANTHROPIC_MIN_CACHEABLE_TOKENS = [
496
+ [/haiku-4-5|opus-4-[56]/, 4096],
497
+ [/haiku-3-5|haiku-3\b/, 2048],
498
+ [/[a-z]-(?:[5-9]|\d{2})(?:-|$)/, 512],
499
+ // 5.x and later families (family name, then major)
500
+ [/sonnet-4|opus-4|sonnet-3-7/, 1024]
501
+ ];
502
+ function anthropicMinCacheableTokens(model) {
503
+ for (const [pattern, min] of ANTHROPIC_MIN_CACHEABLE_TOKENS) {
504
+ if (pattern.test(model)) return min;
505
+ }
506
+ return 1024;
507
+ }
508
+ function estimatePrefixTokens(...parts) {
509
+ let chars = 0;
510
+ for (const part of parts) if (part !== void 0) chars += JSON.stringify(part).length;
511
+ return Math.ceil(chars / 3.5);
512
+ }
513
+
491
514
  // src/utils/event-stream.ts
492
515
  var EventStream = class {
493
516
  queue = [];
@@ -1485,6 +1508,41 @@ function toolResultVideos(content) {
1485
1508
  if (typeof content === "string") return [];
1486
1509
  return content.filter((b) => b.type === "video");
1487
1510
  }
1511
+ function planDeferredTools(tools, catalog, messages) {
1512
+ if (!catalog?.length) return void 0;
1513
+ const byName = /* @__PURE__ */ new Map();
1514
+ for (const tool of catalog) if (!byName.has(tool.name)) byName.set(tool.name, tool);
1515
+ const loaded = /* @__PURE__ */ new Set();
1516
+ for (const message of messages) {
1517
+ if (message.role !== "tool") continue;
1518
+ for (const result of message.content) {
1519
+ for (const name of result.loadedTools ?? []) if (byName.has(name)) loaded.add(name);
1520
+ }
1521
+ }
1522
+ const live = (tools ?? []).filter((tool) => !(byName.has(tool.name) && loaded.has(tool.name)));
1523
+ if (live.length === 0) return void 0;
1524
+ const liveNames = new Set(live.map((tool) => tool.name));
1525
+ const deferred = [...byName.values()].filter((tool) => !liveNames.has(tool.name));
1526
+ return { live, deferred, loaded, byName };
1527
+ }
1528
+ function supportsAnthropicDeferredTools(model) {
1529
+ return /opus-4[-.][5-9]|sonnet-4[-.][5-9]|haiku-4[-.]5|opus-5|sonnet-5|haiku-5|fable-5|mythos-5/.test(
1530
+ model
1531
+ );
1532
+ }
1533
+ function supportsResponsesToolSearch(model) {
1534
+ return /^gpt-(5\.[4-9]|[6-9])/.test(model);
1535
+ }
1536
+ function usesDeferredToolLoading(options) {
1537
+ if (options.provider === "anthropic") {
1538
+ const firstParty = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
1539
+ return firstParty && supportsAnthropicDeferredTools(options.model);
1540
+ }
1541
+ if (options.provider === "openai" && options.accountId) {
1542
+ return supportsResponsesToolSearch(options.model);
1543
+ }
1544
+ return false;
1545
+ }
1488
1546
  function toAnthropicCacheControl(retention, baseUrl) {
1489
1547
  const resolved = retention ?? "short";
1490
1548
  if (resolved === "none") return void 0;
@@ -1527,6 +1585,8 @@ function toAnthropicMessages(messages, cacheControl, options) {
1527
1585
  const keepFallbackBlocks = options?.fallbackBlocks === true;
1528
1586
  const droppedToolCallIds = /* @__PURE__ */ new Set();
1529
1587
  const unresolvedServerToolIds = unresolvedServerToolUseIds(messages);
1588
+ const toolReferences = options?.toolReferences;
1589
+ const referencedTools = /* @__PURE__ */ new Set();
1530
1590
  const trajectoryStartIdx = messages.reduce(
1531
1591
  (last, m, i) => opensTurn(m) ? i : last,
1532
1592
  -1
@@ -1589,16 +1649,43 @@ function toAnthropicMessages(messages, cacheControl, options) {
1589
1649
  if (msg.role === "tool") {
1590
1650
  const results = droppedToolCallIds.size ? msg.content.filter((r) => !droppedToolCallIds.has(r.toolCallId)) : msg.content;
1591
1651
  if (results.length === 0) continue;
1592
- out.push({
1593
- role: "user",
1594
- // Cast covers the video block (used by the Anthropic-compatible MiniMax
1595
- // API), which isn't in the first-party Anthropic tool_result types.
1596
- content: results.map((result) => ({
1652
+ const trailing = [];
1653
+ const blocks = results.map((result) => {
1654
+ const toolUseId = remapAnthropicToolCallId(result.toolCallId, idMap);
1655
+ const refs = toolReferences ? (result.loadedTools ?? []).filter(
1656
+ (name) => toolReferences.has(name) && !referencedTools.has(name)
1657
+ ) : [];
1658
+ if (refs.length === 0) {
1659
+ return {
1660
+ type: "tool_result",
1661
+ tool_use_id: toolUseId,
1662
+ content: toAnthropicToolResultContent(result.content),
1663
+ is_error: result.isError
1664
+ };
1665
+ }
1666
+ for (const name of refs) referencedTools.add(name);
1667
+ const original = toAnthropicToolResultContent(result.content);
1668
+ const parts = typeof original === "string" ? [{ type: "text", text: original }] : original;
1669
+ const first = parts.findIndex((part) => part.type === "text" && part.text !== "");
1670
+ parts.forEach((part, index) => {
1671
+ if (part.type === "text" && part.text === "") return;
1672
+ trailing.push(
1673
+ index === first && part.type === "text" ? { type: "text", text: `Result of tool call ${toolUseId}:
1674
+ ${part.text}` } : part
1675
+ );
1676
+ });
1677
+ return {
1597
1678
  type: "tool_result",
1598
- tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
1599
- content: toAnthropicToolResultContent(result.content),
1679
+ tool_use_id: toolUseId,
1680
+ content: refs.map((name) => ({ type: "tool_reference", tool_name: name })),
1600
1681
  is_error: result.isError
1601
- }))
1682
+ };
1683
+ });
1684
+ out.push({
1685
+ role: "user",
1686
+ // Cast covers the video and tool_reference blocks, which aren't in this
1687
+ // SDK's first-party tool_result types.
1688
+ content: [...blocks, ...trailing]
1602
1689
  });
1603
1690
  }
1604
1691
  }
@@ -1614,7 +1701,8 @@ function toAnthropicMessages(messages, cacheControl, options) {
1614
1701
  markAnthropicCacheBreakpoint(out, tail, cacheControl);
1615
1702
  } else {
1616
1703
  if (anchor !== void 0) markAnthropicCacheBreakpoint(out, anchor, cacheControl);
1617
- markAnthropicCacheBreakpoint(out, tail, { type: "ephemeral" });
1704
+ const longRun = isLongToolRun(messages, trajectoryStartIdx);
1705
+ markAnthropicCacheBreakpoint(out, tail, longRun ? cacheControl : { type: "ephemeral" });
1618
1706
  }
1619
1707
  } else {
1620
1708
  markAnthropicCacheBreakpoint(out, tail, cacheControl);
@@ -1644,6 +1732,28 @@ function toAnthropicMessages(messages, cacheControl, options) {
1644
1732
  }
1645
1733
  return { system, messages: out };
1646
1734
  }
1735
+ var BLOCKING_TOOL_NAMES = /* @__PURE__ */ new Set([
1736
+ "wait_agent",
1737
+ "task_output",
1738
+ "subagent",
1739
+ "ask_user"
1740
+ ]);
1741
+ var LONG_RUN_ROUNDS = 6;
1742
+ function isLongToolRun(messages, trajectoryStartIdx) {
1743
+ let rounds = 0;
1744
+ for (let i = Math.max(0, trajectoryStartIdx + 1); i < messages.length; i++) {
1745
+ const m = messages[i];
1746
+ if (m?.role !== "assistant" || typeof m.content === "string") continue;
1747
+ let called = false;
1748
+ for (const part of m.content) {
1749
+ if (part.type !== "tool_call") continue;
1750
+ called = true;
1751
+ if (BLOCKING_TOOL_NAMES.has(part.name)) return true;
1752
+ }
1753
+ if (called && ++rounds >= LONG_RUN_ROUNDS) return true;
1754
+ }
1755
+ return false;
1756
+ }
1647
1757
  function markAnthropicCacheBreakpoint(out, index, cacheControl) {
1648
1758
  const message = out[index];
1649
1759
  if (!message) return;
@@ -1664,6 +1774,18 @@ function markAnthropicCacheBreakpoint(out, index, cacheControl) {
1664
1774
  }
1665
1775
  }
1666
1776
  function toAnthropicTools(tools, options) {
1777
+ const deferred = (options?.deferred ?? []).map(
1778
+ (tool) => ({
1779
+ name: tool.name,
1780
+ description: tool.description,
1781
+ input_schema: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters),
1782
+ ...options?.enableFineGrainedToolStreaming ? { eager_input_streaming: true } : {},
1783
+ defer_loading: true
1784
+ })
1785
+ );
1786
+ return [...toAnthropicLiveTools(tools, options), ...deferred];
1787
+ }
1788
+ function toAnthropicLiveTools(tools, options) {
1667
1789
  return tools.map((tool, index) => {
1668
1790
  const anthropicTool = {
1669
1791
  name: tool.name,
@@ -1950,6 +2072,7 @@ function parseToolArguments(argsJson) {
1950
2072
  // src/utils/provider-fetch.ts
1951
2073
  var import_undici = require("undici");
1952
2074
  var PROVIDER_TRANSPORT_TIMEOUT_MS = 45 * 6e4;
2075
+ var PROVIDER_KEEP_ALIVE_TIMEOUT_MS = 3e4;
1953
2076
  function nodeUsesEnvProxy({
1954
2077
  env = process.env,
1955
2078
  execArgv = process.execArgv,
@@ -1962,7 +2085,11 @@ function nodeUsesEnvProxy({
1962
2085
  return major >= 24 || major === 22 && minor >= 21;
1963
2086
  }
1964
2087
  function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {}) {
1965
- const timeouts = { headersTimeout: timeoutMs, bodyTimeout: timeoutMs };
2088
+ const timeouts = {
2089
+ headersTimeout: timeoutMs,
2090
+ bodyTimeout: timeoutMs,
2091
+ keepAliveTimeout: PROVIDER_KEEP_ALIVE_TIMEOUT_MS
2092
+ };
1966
2093
  const dispatcher = useEnvProxy ? new import_undici.EnvHttpProxyAgent(timeouts) : new import_undici.Agent(timeouts);
1967
2094
  const builtinFetch = globalThis.fetch;
1968
2095
  const providerFetch2 = async (input, init2) => {
@@ -2117,6 +2244,9 @@ async function prewarmAnthropicCache(options) {
2117
2244
  // prewarmed prompt cache actually hits — both are gated by the flag.
2118
2245
  enableFineGrainedToolStreaming: fineGrainedToolStreamingEnabled()
2119
2246
  }) : void 0;
2247
+ if (estimatePrefixTokens(fullSystem, tools) < anthropicMinCacheableTokens(options.model)) {
2248
+ return;
2249
+ }
2120
2250
  await client.messages.create(
2121
2251
  {
2122
2252
  model: options.model,
@@ -2157,8 +2287,11 @@ async function* runStream(options) {
2157
2287
  const downgradedMessages = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
2158
2288
  const fallbackKey = serverFallbackKey(options);
2159
2289
  const useServerFallback = options.provider !== "minimax" && isDirectAnthropicApi(options.baseUrl) && !anthropicServerFallback.disabled.has(fallbackKey);
2290
+ const deferredPlan = options.provider === "anthropic" && isDirectAnthropicApi(options.baseUrl) && supportsAnthropicDeferredTools(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgradedMessages) : void 0;
2291
+ const toolReferences = deferredPlan?.loaded;
2160
2292
  const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl, {
2161
- fallbackBlocks: useServerFallback
2293
+ fallbackBlocks: useServerFallback,
2294
+ toolReferences
2162
2295
  });
2163
2296
  const useFastMode = options.speed === "fast" && options.provider === "anthropic" && isDirectAnthropicApi(options.baseUrl);
2164
2297
  const system = isOAuth ? [
@@ -2200,20 +2333,20 @@ async function* runStream(options) {
2200
2333
  ...options.temperature != null && !thinking ? { temperature: options.temperature } : {},
2201
2334
  ...options.topP != null ? { top_p: options.topP } : {},
2202
2335
  ...options.stop ? { stop_sequences: options.stop } : {},
2203
- ...options.tools?.length || options.serverTools?.length || options.webSearch ? (() => {
2336
+ ...(deferredPlan?.live ?? options.tools)?.length || options.serverTools?.length || options.webSearch ? (() => {
2204
2337
  const reservedServerNames = /* @__PURE__ */ new Set();
2205
2338
  if (options.webSearch) reservedServerNames.add("web_search");
2206
2339
  for (const t of options.serverTools ?? []) {
2207
2340
  const name = t.name;
2208
2341
  if (name) reservedServerNames.add(name);
2209
2342
  }
2210
- const clientTools = options.tools?.length ? toAnthropicTools(
2211
- options.tools.filter((t) => !reservedServerNames.has(t.name)),
2212
- {
2213
- ...supportsFirstPartyToolExtras && cacheControl ? { cacheControl } : {},
2214
- ...supportsFirstPartyToolExtras && fineGrainedToolStreamingEnabled() ? { enableFineGrainedToolStreaming: true } : {}
2215
- }
2216
- ) : [];
2343
+ const notReserved = (t) => !reservedServerNames.has(t.name);
2344
+ const liveTools = deferredPlan?.live ?? options.tools ?? [];
2345
+ const clientTools = liveTools.length ? toAnthropicTools(liveTools.filter(notReserved), {
2346
+ ...supportsFirstPartyToolExtras && cacheControl ? { cacheControl } : {},
2347
+ ...supportsFirstPartyToolExtras && fineGrainedToolStreamingEnabled() ? { enableFineGrainedToolStreaming: true } : {},
2348
+ ...deferredPlan ? { deferred: deferredPlan.deferred.filter(notReserved) } : {}
2349
+ }) : [];
2217
2350
  return {
2218
2351
  tools: [
2219
2352
  ...clientTools,
@@ -2234,6 +2367,13 @@ async function* runStream(options) {
2234
2367
  ...useFastMode ? { speed: "fast" } : {},
2235
2368
  stream: useStreaming
2236
2369
  };
2370
+ if (options.prewarm && estimatePrefixTokens(baseParams.system, baseParams.tools) < anthropicMinCacheableTokens(options.model)) {
2371
+ return {
2372
+ message: { role: "assistant", content: [] },
2373
+ stopReason: "end_turn",
2374
+ usage: { inputTokens: 0, outputTokens: 0 }
2375
+ };
2376
+ }
2237
2377
  const params = mergeProviderOptions(baseParams, options.providerOptions);
2238
2378
  const hasAdaptiveThinking = isAdaptiveThinkingModel(options.model);
2239
2379
  const betaHeaders = [
@@ -2267,7 +2407,10 @@ async function* runStream(options) {
2267
2407
  const { fallbacks: _dropped, ...rest } = params;
2268
2408
  const retryParams = {
2269
2409
  ...rest,
2270
- messages: toAnthropicMessages(downgradedMessages, cacheControl, { fallbackBlocks: false }).messages
2410
+ messages: toAnthropicMessages(downgradedMessages, cacheControl, {
2411
+ fallbackBlocks: false,
2412
+ toolReferences
2413
+ }).messages
2271
2414
  };
2272
2415
  return create(
2273
2416
  retryParams,
@@ -2843,6 +2986,10 @@ function createClient2(options) {
2843
2986
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
2844
2987
  // Default transport lifts undici's 300s header/body caps (see provider-fetch.ts).
2845
2988
  fetch: options.fetch ?? providerFetch,
2989
+ // The agent loop owns retries (classified backoff, Retry-After, UI retry
2990
+ // events). The SDK default of 2 silent retries stacked under it: one 5xx
2991
+ // cost up to 3 hidden requests plus SDK sleeps before the loop even saw it.
2992
+ maxRetries: 0,
2846
2993
  ...options.defaultHeaders ? { defaultHeaders: options.defaultHeaders } : {}
2847
2994
  });
2848
2995
  if (!options.fetch) {
@@ -3617,6 +3764,15 @@ async function* consumeResponsesEvents(events, ctx) {
3617
3764
  droppedToolCalls
3618
3765
  });
3619
3766
  }
3767
+ const addedOrder = [...toolCalls.keys()];
3768
+ const toolSlots = orderedItems.flatMap((e, i) => e.kind === "tool" ? [i] : []);
3769
+ const sortedTools = toolSlots.map((i) => orderedItems[i]).sort(
3770
+ (a, b) => a?.kind === "tool" && b?.kind === "tool" ? addedOrder.indexOf(a.id) - addedOrder.indexOf(b.id) : 0
3771
+ );
3772
+ toolSlots.forEach((slot, k) => {
3773
+ const entry = sortedTools[k];
3774
+ if (entry) orderedItems[slot] = entry;
3775
+ });
3620
3776
  const seenTool = /* @__PURE__ */ new Set();
3621
3777
  let textInserted = false;
3622
3778
  for (const entry of orderedItems) {
@@ -3693,6 +3849,8 @@ function toResponsesInput(messages, options) {
3693
3849
  let system;
3694
3850
  const input = [];
3695
3851
  const idMap = /* @__PURE__ */ new Map();
3852
+ const loadedTools = /* @__PURE__ */ new Set();
3853
+ let toolLoads = 0;
3696
3854
  for (const msg of messages) {
3697
3855
  if (msg.role === "system") {
3698
3856
  system = msg.content;
@@ -3746,6 +3904,28 @@ function toResponsesInput(messages, options) {
3746
3904
  call_id: mapId(callId, idMap),
3747
3905
  output: text.length > 0 ? text : "(see attached image)"
3748
3906
  });
3907
+ const loads = options.toolSearchLoads;
3908
+ const names = loads ? (result.loadedTools ?? []).filter((name) => loads.has(name) && !loadedTools.has(name)) : [];
3909
+ if (loads && names.length > 0) {
3910
+ for (const name of names) loadedTools.add(name);
3911
+ const loadId = `gg_tool_load_${++toolLoads}`;
3912
+ input.push(
3913
+ {
3914
+ type: "tool_search_call",
3915
+ call_id: loadId,
3916
+ execution: "client",
3917
+ status: "completed",
3918
+ arguments: { query: names.join(" "), limit: names.length }
3919
+ },
3920
+ {
3921
+ type: "tool_search_output",
3922
+ call_id: loadId,
3923
+ execution: "client",
3924
+ status: "completed",
3925
+ tools: names.map((name) => loads.get(name))
3926
+ }
3927
+ );
3928
+ }
3749
3929
  if (options.supportsImages !== false && Array.isArray(result.content)) {
3750
3930
  for (const block of result.content) {
3751
3931
  if (block.type === "image") toolImages.push(block);
@@ -3844,8 +4024,19 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
3844
4024
  const url = `${baseUrl}/codex/responses`;
3845
4025
  const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
3846
4026
  const downgraded = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
4027
+ const deferredPlan = supportsResponsesToolSearch(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgraded) : void 0;
4028
+ const strictTools = options.strictTools ?? true;
4029
+ const toolSearchLoads = deferredPlan ? new Map(
4030
+ [...deferredPlan.loaded].map((name) => {
4031
+ const tool = deferredPlan.byName.get(name);
4032
+ const [wire] = tool ? toCodexTools([tool], strictTools) : [];
4033
+ return [name, { ...wire, defer_loading: true }];
4034
+ })
4035
+ ) : void 0;
4036
+ const liveTools = deferredPlan?.live ?? options.tools;
3847
4037
  const { system, input } = toResponsesInput(downgraded, {
3848
4038
  supportsImages: options.supportsImages,
4039
+ toolSearchLoads,
3849
4040
  mapId: remapCodexId,
3850
4041
  // Stamped reasoning belongs to another transport (e.g. Grok's) and would
3851
4042
  // be rejected here; Codex's own items are never stamped.
@@ -3859,12 +4050,12 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
3859
4050
  stream: true,
3860
4051
  instructions: system,
3861
4052
  input,
3862
- tool_choice: toCodexToolChoice(options.toolChoice, options.tools),
4053
+ tool_choice: toCodexToolChoice(options.toolChoice, liveTools),
3863
4054
  parallel_tool_calls: !liteShape,
3864
4055
  include: ["reasoning.encrypted_content"]
3865
4056
  };
3866
- if (options.tools?.length) {
3867
- body.tools = toCodexTools(options.tools, options.strictTools ?? true);
4057
+ if (liveTools?.length) {
4058
+ body.tools = toCodexTools(liveTools, strictTools);
3868
4059
  }
3869
4060
  body.prompt_cache_key = normalizePromptCacheKey(options.promptCacheKey ?? "ggcoder");
3870
4061
  if (options.temperature != null && !options.thinking) {
@@ -5606,7 +5797,7 @@ function stream(requested) {
5606
5797
  observePreparedContext(
5607
5798
  replayable === sanitized ? wireMessages : replayable,
5608
5799
  messages,
5609
- options.tools ?? []
5800
+ observedTools(options, messages)
5610
5801
  )
5611
5802
  );
5612
5803
  } catch {
@@ -5614,6 +5805,11 @@ function stream(requested) {
5614
5805
  }
5615
5806
  return entry.stream(messages === options.messages ? options : { ...options, messages });
5616
5807
  }
5808
+ function observedTools(options, messages) {
5809
+ if (!usesDeferredToolLoading(options)) return options.tools ?? [];
5810
+ const plan = planDeferredTools(options.tools, options.deferredTools, messages);
5811
+ return plan ? [...plan.live, ...plan.deferred] : options.tools ?? [];
5812
+ }
5617
5813
  function stripMessageProvenance(messages) {
5618
5814
  let stripped;
5619
5815
  for (let index = 0; index < messages.length; index++) {
@@ -5753,6 +5949,85 @@ function l2Normalize(vector) {
5753
5949
  return vector.map((value) => value / norm);
5754
5950
  }
5755
5951
 
5952
+ // src/utils/preconnect.ts
5953
+ var PRECONNECT_INTERVAL_MS = 3e4;
5954
+ function defaultBaseUrl(provider, accountId) {
5955
+ switch (provider) {
5956
+ case "anthropic":
5957
+ return "https://api.anthropic.com";
5958
+ case "openai":
5959
+ return accountId ? "https://chatgpt.com/backend-api" : "https://api.openai.com/v1";
5960
+ case "gemini":
5961
+ return "https://cloudcode-pa.googleapis.com";
5962
+ case "glm":
5963
+ return "https://api.z.ai/api/coding/paas/v4";
5964
+ case "moonshot":
5965
+ return "https://api.moonshot.ai/v1";
5966
+ case "deepseek":
5967
+ return "https://api.deepseek.com/v1";
5968
+ case "openrouter":
5969
+ return "https://openrouter.ai/api/v1";
5970
+ case "xiaomi":
5971
+ return "https://token-plan-sgp.xiaomimimo.com/v1";
5972
+ case "minimax":
5973
+ return "https://api.minimax.io/anthropic";
5974
+ case "xai":
5975
+ return "https://api.x.ai/v1";
5976
+ case "huggingface":
5977
+ return "https://router.huggingface.co/v1";
5978
+ case "sakana":
5979
+ return "https://api.sakana.ai/v1";
5980
+ case "local":
5981
+ case "palsu":
5982
+ return void 0;
5983
+ }
5984
+ }
5985
+ function isLocalHost(hostname) {
5986
+ const host = hostname.replace(/^\[|\]$/g, "").toLowerCase();
5987
+ if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) return true;
5988
+ if (host === "::1" || host === "0.0.0.0") return true;
5989
+ if (host.includes(":")) return /^(f[cd]|fe[89ab])/.test(host);
5990
+ const octets = host.split(".").map(Number);
5991
+ if (octets.length !== 4 || octets.some((n) => !Number.isInteger(n))) return false;
5992
+ const [a = 0, b = 0] = octets;
5993
+ return a === 127 || a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31;
5994
+ }
5995
+ function preconnectTarget(options) {
5996
+ const raw = options.baseUrl ?? defaultBaseUrl(options.provider, options.accountId);
5997
+ if (!raw || options.provider === "local" || options.provider === "palsu") return void 0;
5998
+ let url;
5999
+ try {
6000
+ url = new URL(raw);
6001
+ } catch {
6002
+ return void 0;
6003
+ }
6004
+ if (url.protocol !== "https:" || isLocalHost(url.hostname)) return void 0;
6005
+ url.pathname = url.pathname.replace(/\/+$/, "") || "/";
6006
+ url.search = "";
6007
+ url.hash = "";
6008
+ return url;
6009
+ }
6010
+ function preconnectOrigin(options) {
6011
+ return preconnectTarget(options)?.origin;
6012
+ }
6013
+ function preconnectProvider(options) {
6014
+ const target = preconnectTarget(options);
6015
+ if (!target) return void 0;
6016
+ const origin = target.origin;
6017
+ const now = options.now ?? Date.now();
6018
+ const last = options.warmedAt.get(origin);
6019
+ if (last !== void 0 && now - last < PRECONNECT_INTERVAL_MS) return void 0;
6020
+ options.warmedAt.set(origin, now);
6021
+ const fetchImpl = options.fetch ?? providerFetch;
6022
+ void fetchImpl(target.href, {
6023
+ method: "HEAD",
6024
+ redirect: "manual",
6025
+ signal: AbortSignal.timeout(1e4)
6026
+ }).then((response) => response.body?.cancel()).catch(() => {
6027
+ });
6028
+ return origin;
6029
+ }
6030
+
5756
6031
  // src/chat-error.ts
5757
6032
  function causeCode(error) {
5758
6033
  if (!(error instanceof Error) || !error.cause || typeof error.cause !== "object") return "";
@@ -6231,6 +6506,7 @@ function palsuRerankScore(query, document) {
6231
6506
  EventStream,
6232
6507
  FAST_MODE_SUFFIX,
6233
6508
  GGAIError,
6509
+ PRECONNECT_INTERVAL_MS,
6234
6510
  ProviderError,
6235
6511
  REDACTION_MARKER,
6236
6512
  ResponseStalledError,
@@ -6262,6 +6538,8 @@ function palsuRerankScore(query, document) {
6262
6538
  palsuToolCall,
6263
6539
  parseFastModeRejection,
6264
6540
  pendingClaudeCodeVersionRefresh,
6541
+ preconnectOrigin,
6542
+ preconnectProvider,
6265
6543
  prewarmAnthropicCache,
6266
6544
  providerRegistry,
6267
6545
  redactText,