@kenkaiiii/gg-ai 5.87.2 → 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.js CHANGED
@@ -399,6 +399,26 @@ function providerGuidance(provider, message, statusCode) {
399
399
  // src/providers/anthropic.ts
400
400
  import Anthropic from "@anthropic-ai/sdk";
401
401
 
402
+ // src/providers/cache-minimums.ts
403
+ var ANTHROPIC_MIN_CACHEABLE_TOKENS = [
404
+ [/haiku-4-5|opus-4-[56]/, 4096],
405
+ [/haiku-3-5|haiku-3\b/, 2048],
406
+ [/[a-z]-(?:[5-9]|\d{2})(?:-|$)/, 512],
407
+ // 5.x and later families (family name, then major)
408
+ [/sonnet-4|opus-4|sonnet-3-7/, 1024]
409
+ ];
410
+ function anthropicMinCacheableTokens(model) {
411
+ for (const [pattern, min] of ANTHROPIC_MIN_CACHEABLE_TOKENS) {
412
+ if (pattern.test(model)) return min;
413
+ }
414
+ return 1024;
415
+ }
416
+ function estimatePrefixTokens(...parts) {
417
+ let chars = 0;
418
+ for (const part of parts) if (part !== void 0) chars += JSON.stringify(part).length;
419
+ return Math.ceil(chars / 3.5);
420
+ }
421
+
402
422
  // src/utils/event-stream.ts
403
423
  var EventStream = class {
404
424
  queue = [];
@@ -554,6 +574,145 @@ var StreamResult = class _StreamResult {
554
574
  }
555
575
  };
556
576
 
577
+ // src/claude-code-version.ts
578
+ import fs from "fs/promises";
579
+ import os from "os";
580
+ import path from "path";
581
+
582
+ // src/utils/diag.ts
583
+ var _diagFn = null;
584
+ function setProviderDiagnostic(fn) {
585
+ _diagFn = fn;
586
+ }
587
+ function providerDiag(phase, data) {
588
+ _diagFn?.(phase, data);
589
+ }
590
+
591
+ // src/claude-code-version.ts
592
+ var NPM_LATEST_URL = "https://registry.npmjs.org/@anthropic-ai/claude-code/latest";
593
+ var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
594
+ var FETCH_TIMEOUT_MS = 3e3;
595
+ var CLAUDE_CODE_FALLBACK_VERSION = "2.1.316";
596
+ function isOlder(a, b) {
597
+ const pa = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
598
+ const pb = b.split(".").map((n) => Number.parseInt(n, 10) || 0);
599
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
600
+ const x = pa[i] ?? 0;
601
+ const y = pb[i] ?? 0;
602
+ if (x !== y) return x < y;
603
+ }
604
+ return false;
605
+ }
606
+ function atLeastFallback(version) {
607
+ return isOlder(version, CLAUDE_CODE_FALLBACK_VERSION) ? CLAUDE_CODE_FALLBACK_VERSION : version;
608
+ }
609
+ var memoryCache = null;
610
+ var inflight = null;
611
+ function agentDir() {
612
+ return path.join(os.homedir(), ".gg");
613
+ }
614
+ function cachePath() {
615
+ return path.join(agentDir(), "claude-code-version.json");
616
+ }
617
+ async function readDiskCache() {
618
+ try {
619
+ const raw = await fs.readFile(cachePath(), "utf-8");
620
+ const parsed = JSON.parse(raw);
621
+ if (typeof parsed.version === "string" && typeof parsed.fetchedAt === "number") {
622
+ return parsed;
623
+ }
624
+ return null;
625
+ } catch {
626
+ return null;
627
+ }
628
+ }
629
+ async function writeDiskCache(data) {
630
+ try {
631
+ await fs.mkdir(agentDir(), { recursive: true, mode: 448 });
632
+ await fs.writeFile(cachePath(), JSON.stringify(data), { mode: 384 });
633
+ } catch (err) {
634
+ providerDiag("claude_code_version_cache_write_failed", {
635
+ error: err instanceof Error ? err.message : String(err)
636
+ });
637
+ }
638
+ }
639
+ async function fetchLatest() {
640
+ const controller = new AbortController();
641
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
642
+ try {
643
+ const response = await fetch(NPM_LATEST_URL, { signal: controller.signal });
644
+ if (!response.ok) return null;
645
+ const data = await response.json();
646
+ if (typeof data.version === "string" && /^\d/.test(data.version)) {
647
+ return data.version;
648
+ }
649
+ return null;
650
+ } catch {
651
+ return null;
652
+ } finally {
653
+ clearTimeout(timer);
654
+ }
655
+ }
656
+ var refreshing = null;
657
+ function refreshInBackground() {
658
+ refreshing ??= (async () => {
659
+ try {
660
+ const fetched = await fetchLatest();
661
+ if (!fetched) return;
662
+ await writeDiskCache({ version: fetched, fetchedAt: Date.now() });
663
+ memoryCache = { version: fetched, expiresAt: Date.now() + CACHE_TTL_MS };
664
+ } finally {
665
+ refreshing = null;
666
+ }
667
+ })();
668
+ return refreshing;
669
+ }
670
+ function pendingClaudeCodeVersionRefresh() {
671
+ return refreshing;
672
+ }
673
+ async function getClaudeCodeVersion() {
674
+ if (memoryCache && Date.now() < memoryCache.expiresAt) {
675
+ return memoryCache.version;
676
+ }
677
+ if (inflight) return inflight;
678
+ inflight = (async () => {
679
+ const disk = await readDiskCache();
680
+ const diskFresh = disk && Date.now() - disk.fetchedAt < CACHE_TTL_MS;
681
+ if (disk && diskFresh) {
682
+ const version = atLeastFallback(disk.version);
683
+ memoryCache = { version, expiresAt: Date.now() + CACHE_TTL_MS };
684
+ return version;
685
+ }
686
+ if (disk) {
687
+ const version = atLeastFallback(disk.version);
688
+ memoryCache = { version, expiresAt: Date.now() + 5 * 60 * 1e3 };
689
+ void refreshInBackground();
690
+ return version;
691
+ }
692
+ const fetched = await fetchLatest();
693
+ if (fetched) {
694
+ await writeDiskCache({ version: fetched, fetchedAt: Date.now() });
695
+ memoryCache = { version: fetched, expiresAt: Date.now() + CACHE_TTL_MS };
696
+ return fetched;
697
+ }
698
+ const resolved = CLAUDE_CODE_FALLBACK_VERSION;
699
+ memoryCache = { version: resolved, expiresAt: Date.now() + 5 * 60 * 1e3 };
700
+ providerDiag("claude_code_version_fallback", { version: resolved });
701
+ return resolved;
702
+ })();
703
+ try {
704
+ return await inflight;
705
+ } finally {
706
+ inflight = null;
707
+ }
708
+ }
709
+ function claudeCliUserAgent(version) {
710
+ return `claude-cli/${version} (external, cli)`;
711
+ }
712
+ async function getClaudeCliUserAgent() {
713
+ return claudeCliUserAgent(await getClaudeCodeVersion());
714
+ }
715
+
557
716
  // src/utils/provider-options.ts
558
717
  var RESERVED_BODY_FIELDS = /* @__PURE__ */ new Set([
559
718
  "contents",
@@ -1257,6 +1416,41 @@ function toolResultVideos(content) {
1257
1416
  if (typeof content === "string") return [];
1258
1417
  return content.filter((b) => b.type === "video");
1259
1418
  }
1419
+ function planDeferredTools(tools, catalog, messages) {
1420
+ if (!catalog?.length) return void 0;
1421
+ const byName = /* @__PURE__ */ new Map();
1422
+ for (const tool of catalog) if (!byName.has(tool.name)) byName.set(tool.name, tool);
1423
+ const loaded = /* @__PURE__ */ new Set();
1424
+ for (const message of messages) {
1425
+ if (message.role !== "tool") continue;
1426
+ for (const result of message.content) {
1427
+ for (const name of result.loadedTools ?? []) if (byName.has(name)) loaded.add(name);
1428
+ }
1429
+ }
1430
+ const live = (tools ?? []).filter((tool) => !(byName.has(tool.name) && loaded.has(tool.name)));
1431
+ if (live.length === 0) return void 0;
1432
+ const liveNames = new Set(live.map((tool) => tool.name));
1433
+ const deferred = [...byName.values()].filter((tool) => !liveNames.has(tool.name));
1434
+ return { live, deferred, loaded, byName };
1435
+ }
1436
+ function supportsAnthropicDeferredTools(model) {
1437
+ return /opus-4[-.][5-9]|sonnet-4[-.][5-9]|haiku-4[-.]5|opus-5|sonnet-5|haiku-5|fable-5|mythos-5/.test(
1438
+ model
1439
+ );
1440
+ }
1441
+ function supportsResponsesToolSearch(model) {
1442
+ return /^gpt-(5\.[4-9]|[6-9])/.test(model);
1443
+ }
1444
+ function usesDeferredToolLoading(options) {
1445
+ if (options.provider === "anthropic") {
1446
+ const firstParty = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
1447
+ return firstParty && supportsAnthropicDeferredTools(options.model);
1448
+ }
1449
+ if (options.provider === "openai" && options.accountId) {
1450
+ return supportsResponsesToolSearch(options.model);
1451
+ }
1452
+ return false;
1453
+ }
1260
1454
  function toAnthropicCacheControl(retention, baseUrl) {
1261
1455
  const resolved = retention ?? "short";
1262
1456
  if (resolved === "none") return void 0;
@@ -1299,6 +1493,8 @@ function toAnthropicMessages(messages, cacheControl, options) {
1299
1493
  const keepFallbackBlocks = options?.fallbackBlocks === true;
1300
1494
  const droppedToolCallIds = /* @__PURE__ */ new Set();
1301
1495
  const unresolvedServerToolIds = unresolvedServerToolUseIds(messages);
1496
+ const toolReferences = options?.toolReferences;
1497
+ const referencedTools = /* @__PURE__ */ new Set();
1302
1498
  const trajectoryStartIdx = messages.reduce(
1303
1499
  (last, m, i) => opensTurn(m) ? i : last,
1304
1500
  -1
@@ -1361,16 +1557,43 @@ function toAnthropicMessages(messages, cacheControl, options) {
1361
1557
  if (msg.role === "tool") {
1362
1558
  const results = droppedToolCallIds.size ? msg.content.filter((r) => !droppedToolCallIds.has(r.toolCallId)) : msg.content;
1363
1559
  if (results.length === 0) continue;
1364
- out.push({
1365
- role: "user",
1366
- // Cast covers the video block (used by the Anthropic-compatible MiniMax
1367
- // API), which isn't in the first-party Anthropic tool_result types.
1368
- content: results.map((result) => ({
1560
+ const trailing = [];
1561
+ const blocks = results.map((result) => {
1562
+ const toolUseId = remapAnthropicToolCallId(result.toolCallId, idMap);
1563
+ const refs = toolReferences ? (result.loadedTools ?? []).filter(
1564
+ (name) => toolReferences.has(name) && !referencedTools.has(name)
1565
+ ) : [];
1566
+ if (refs.length === 0) {
1567
+ return {
1568
+ type: "tool_result",
1569
+ tool_use_id: toolUseId,
1570
+ content: toAnthropicToolResultContent(result.content),
1571
+ is_error: result.isError
1572
+ };
1573
+ }
1574
+ for (const name of refs) referencedTools.add(name);
1575
+ const original = toAnthropicToolResultContent(result.content);
1576
+ const parts = typeof original === "string" ? [{ type: "text", text: original }] : original;
1577
+ const first = parts.findIndex((part) => part.type === "text" && part.text !== "");
1578
+ parts.forEach((part, index) => {
1579
+ if (part.type === "text" && part.text === "") return;
1580
+ trailing.push(
1581
+ index === first && part.type === "text" ? { type: "text", text: `Result of tool call ${toolUseId}:
1582
+ ${part.text}` } : part
1583
+ );
1584
+ });
1585
+ return {
1369
1586
  type: "tool_result",
1370
- tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
1371
- content: toAnthropicToolResultContent(result.content),
1587
+ tool_use_id: toolUseId,
1588
+ content: refs.map((name) => ({ type: "tool_reference", tool_name: name })),
1372
1589
  is_error: result.isError
1373
- }))
1590
+ };
1591
+ });
1592
+ out.push({
1593
+ role: "user",
1594
+ // Cast covers the video and tool_reference blocks, which aren't in this
1595
+ // SDK's first-party tool_result types.
1596
+ content: [...blocks, ...trailing]
1374
1597
  });
1375
1598
  }
1376
1599
  }
@@ -1386,7 +1609,8 @@ function toAnthropicMessages(messages, cacheControl, options) {
1386
1609
  markAnthropicCacheBreakpoint(out, tail, cacheControl);
1387
1610
  } else {
1388
1611
  if (anchor !== void 0) markAnthropicCacheBreakpoint(out, anchor, cacheControl);
1389
- markAnthropicCacheBreakpoint(out, tail, { type: "ephemeral" });
1612
+ const longRun = isLongToolRun(messages, trajectoryStartIdx);
1613
+ markAnthropicCacheBreakpoint(out, tail, longRun ? cacheControl : { type: "ephemeral" });
1390
1614
  }
1391
1615
  } else {
1392
1616
  markAnthropicCacheBreakpoint(out, tail, cacheControl);
@@ -1416,6 +1640,28 @@ function toAnthropicMessages(messages, cacheControl, options) {
1416
1640
  }
1417
1641
  return { system, messages: out };
1418
1642
  }
1643
+ var BLOCKING_TOOL_NAMES = /* @__PURE__ */ new Set([
1644
+ "wait_agent",
1645
+ "task_output",
1646
+ "subagent",
1647
+ "ask_user"
1648
+ ]);
1649
+ var LONG_RUN_ROUNDS = 6;
1650
+ function isLongToolRun(messages, trajectoryStartIdx) {
1651
+ let rounds = 0;
1652
+ for (let i = Math.max(0, trajectoryStartIdx + 1); i < messages.length; i++) {
1653
+ const m = messages[i];
1654
+ if (m?.role !== "assistant" || typeof m.content === "string") continue;
1655
+ let called = false;
1656
+ for (const part of m.content) {
1657
+ if (part.type !== "tool_call") continue;
1658
+ called = true;
1659
+ if (BLOCKING_TOOL_NAMES.has(part.name)) return true;
1660
+ }
1661
+ if (called && ++rounds >= LONG_RUN_ROUNDS) return true;
1662
+ }
1663
+ return false;
1664
+ }
1419
1665
  function markAnthropicCacheBreakpoint(out, index, cacheControl) {
1420
1666
  const message = out[index];
1421
1667
  if (!message) return;
@@ -1436,6 +1682,18 @@ function markAnthropicCacheBreakpoint(out, index, cacheControl) {
1436
1682
  }
1437
1683
  }
1438
1684
  function toAnthropicTools(tools, options) {
1685
+ const deferred = (options?.deferred ?? []).map(
1686
+ (tool) => ({
1687
+ name: tool.name,
1688
+ description: tool.description,
1689
+ input_schema: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters),
1690
+ ...options?.enableFineGrainedToolStreaming ? { eager_input_streaming: true } : {},
1691
+ defer_loading: true
1692
+ })
1693
+ );
1694
+ return [...toAnthropicLiveTools(tools, options), ...deferred];
1695
+ }
1696
+ function toAnthropicLiveTools(tools, options) {
1439
1697
  return tools.map((tool, index) => {
1440
1698
  const anthropicTool = {
1441
1699
  name: tool.name,
@@ -1722,6 +1980,7 @@ function parseToolArguments(argsJson) {
1722
1980
  // src/utils/provider-fetch.ts
1723
1981
  import { Agent, EnvHttpProxyAgent, fetch as undiciFetch } from "undici";
1724
1982
  var PROVIDER_TRANSPORT_TIMEOUT_MS = 45 * 6e4;
1983
+ var PROVIDER_KEEP_ALIVE_TIMEOUT_MS = 3e4;
1725
1984
  function nodeUsesEnvProxy({
1726
1985
  env = process.env,
1727
1986
  execArgv = process.execArgv,
@@ -1734,7 +1993,11 @@ function nodeUsesEnvProxy({
1734
1993
  return major >= 24 || major === 22 && minor >= 21;
1735
1994
  }
1736
1995
  function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {}) {
1737
- const timeouts = { headersTimeout: timeoutMs, bodyTimeout: timeoutMs };
1996
+ const timeouts = {
1997
+ headersTimeout: timeoutMs,
1998
+ bodyTimeout: timeoutMs,
1999
+ keepAliveTimeout: PROVIDER_KEEP_ALIVE_TIMEOUT_MS
2000
+ };
1738
2001
  const dispatcher = useEnvProxy ? new EnvHttpProxyAgent(timeouts) : new Agent(timeouts);
1739
2002
  const builtinFetch = globalThis.fetch;
1740
2003
  const providerFetch2 = async (input, init2) => {
@@ -1824,9 +2087,13 @@ function sumUsageIterations(usage) {
1824
2087
  }
1825
2088
  return totals;
1826
2089
  }
2090
+ async function withClaudeUserAgent(options) {
2091
+ if (options.userAgent || !options.apiKey?.startsWith("sk-ant-oat")) return options;
2092
+ return { ...options, userAgent: await getClaudeCliUserAgent() };
2093
+ }
1827
2094
  function createClient(options) {
1828
2095
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
1829
- const userAgent = isOAuth ? options.userAgent ?? "claude-cli/2.1.75 (external, cli)" : "";
2096
+ const userAgent = isOAuth ? options.userAgent ?? claudeCliUserAgent(CLAUDE_CODE_FALLBACK_VERSION) : "";
1830
2097
  const cacheKey = `${options.apiKey ?? ""}|${options.baseUrl ?? ""}|${userAgent}`;
1831
2098
  if (!options.fetch) {
1832
2099
  const cached = anthropicClientCache.get(cacheKey);
@@ -1856,11 +2123,13 @@ function createClient(options) {
1856
2123
  }
1857
2124
  async function prewarmAnthropicCache(options) {
1858
2125
  try {
1859
- const client = createClient({
1860
- apiKey: options.apiKey,
1861
- baseUrl: options.baseUrl,
1862
- userAgent: options.userAgent
1863
- });
2126
+ const client = createClient(
2127
+ await withClaudeUserAgent({
2128
+ apiKey: options.apiKey,
2129
+ baseUrl: options.baseUrl,
2130
+ userAgent: options.userAgent
2131
+ })
2132
+ );
1864
2133
  const cacheControl = toAnthropicCacheControl(options.cacheRetention ?? "long", options.baseUrl);
1865
2134
  const { system, messages } = toAnthropicMessages(
1866
2135
  [
@@ -1883,6 +2152,9 @@ async function prewarmAnthropicCache(options) {
1883
2152
  // prewarmed prompt cache actually hits — both are gated by the flag.
1884
2153
  enableFineGrainedToolStreaming: fineGrainedToolStreamingEnabled()
1885
2154
  }) : void 0;
2155
+ if (estimatePrefixTokens(fullSystem, tools) < anthropicMinCacheableTokens(options.model)) {
2156
+ return;
2157
+ }
1886
2158
  await client.messages.create(
1887
2159
  {
1888
2160
  model: options.model,
@@ -1914,7 +2186,7 @@ function streamAnthropic(options) {
1914
2186
  return new StreamResult(runStream(options), options.signal);
1915
2187
  }
1916
2188
  async function* runStream(options) {
1917
- const client = createClient(options);
2189
+ const client = createClient(await withClaudeUserAgent(options));
1918
2190
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
1919
2191
  const useStreaming = options.streaming !== false && !options.prewarm;
1920
2192
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
@@ -1923,8 +2195,11 @@ async function* runStream(options) {
1923
2195
  const downgradedMessages = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
1924
2196
  const fallbackKey = serverFallbackKey(options);
1925
2197
  const useServerFallback = options.provider !== "minimax" && isDirectAnthropicApi(options.baseUrl) && !anthropicServerFallback.disabled.has(fallbackKey);
2198
+ const deferredPlan = options.provider === "anthropic" && isDirectAnthropicApi(options.baseUrl) && supportsAnthropicDeferredTools(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgradedMessages) : void 0;
2199
+ const toolReferences = deferredPlan?.loaded;
1926
2200
  const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl, {
1927
- fallbackBlocks: useServerFallback
2201
+ fallbackBlocks: useServerFallback,
2202
+ toolReferences
1928
2203
  });
1929
2204
  const useFastMode = options.speed === "fast" && options.provider === "anthropic" && isDirectAnthropicApi(options.baseUrl);
1930
2205
  const system = isOAuth ? [
@@ -1966,20 +2241,20 @@ async function* runStream(options) {
1966
2241
  ...options.temperature != null && !thinking ? { temperature: options.temperature } : {},
1967
2242
  ...options.topP != null ? { top_p: options.topP } : {},
1968
2243
  ...options.stop ? { stop_sequences: options.stop } : {},
1969
- ...options.tools?.length || options.serverTools?.length || options.webSearch ? (() => {
2244
+ ...(deferredPlan?.live ?? options.tools)?.length || options.serverTools?.length || options.webSearch ? (() => {
1970
2245
  const reservedServerNames = /* @__PURE__ */ new Set();
1971
2246
  if (options.webSearch) reservedServerNames.add("web_search");
1972
2247
  for (const t of options.serverTools ?? []) {
1973
2248
  const name = t.name;
1974
2249
  if (name) reservedServerNames.add(name);
1975
2250
  }
1976
- const clientTools = options.tools?.length ? toAnthropicTools(
1977
- options.tools.filter((t) => !reservedServerNames.has(t.name)),
1978
- {
1979
- ...supportsFirstPartyToolExtras && cacheControl ? { cacheControl } : {},
1980
- ...supportsFirstPartyToolExtras && fineGrainedToolStreamingEnabled() ? { enableFineGrainedToolStreaming: true } : {}
1981
- }
1982
- ) : [];
2251
+ const notReserved = (t) => !reservedServerNames.has(t.name);
2252
+ const liveTools = deferredPlan?.live ?? options.tools ?? [];
2253
+ const clientTools = liveTools.length ? toAnthropicTools(liveTools.filter(notReserved), {
2254
+ ...supportsFirstPartyToolExtras && cacheControl ? { cacheControl } : {},
2255
+ ...supportsFirstPartyToolExtras && fineGrainedToolStreamingEnabled() ? { enableFineGrainedToolStreaming: true } : {},
2256
+ ...deferredPlan ? { deferred: deferredPlan.deferred.filter(notReserved) } : {}
2257
+ }) : [];
1983
2258
  return {
1984
2259
  tools: [
1985
2260
  ...clientTools,
@@ -2000,6 +2275,13 @@ async function* runStream(options) {
2000
2275
  ...useFastMode ? { speed: "fast" } : {},
2001
2276
  stream: useStreaming
2002
2277
  };
2278
+ if (options.prewarm && estimatePrefixTokens(baseParams.system, baseParams.tools) < anthropicMinCacheableTokens(options.model)) {
2279
+ return {
2280
+ message: { role: "assistant", content: [] },
2281
+ stopReason: "end_turn",
2282
+ usage: { inputTokens: 0, outputTokens: 0 }
2283
+ };
2284
+ }
2003
2285
  const params = mergeProviderOptions(baseParams, options.providerOptions);
2004
2286
  const hasAdaptiveThinking = isAdaptiveThinkingModel(options.model);
2005
2287
  const betaHeaders = [
@@ -2033,7 +2315,10 @@ async function* runStream(options) {
2033
2315
  const { fallbacks: _dropped, ...rest } = params;
2034
2316
  const retryParams = {
2035
2317
  ...rest,
2036
- messages: toAnthropicMessages(downgradedMessages, cacheControl, { fallbackBlocks: false }).messages
2318
+ messages: toAnthropicMessages(downgradedMessages, cacheControl, {
2319
+ fallbackBlocks: false,
2320
+ toolReferences
2321
+ }).messages
2037
2322
  };
2038
2323
  return create(
2039
2324
  retryParams,
@@ -2495,15 +2780,6 @@ function fnv1aHash(value) {
2495
2780
  return (hash >>> 0).toString(16).padStart(8, "0");
2496
2781
  }
2497
2782
 
2498
- // src/utils/diag.ts
2499
- var _diagFn = null;
2500
- function setProviderDiagnostic(fn) {
2501
- _diagFn = fn;
2502
- }
2503
- function providerDiag(phase, data) {
2504
- _diagFn?.(phase, data);
2505
- }
2506
-
2507
2783
  // src/providers/moonshot-video.ts
2508
2784
  async function uploadMoonshotVideos(client, messages, signal) {
2509
2785
  for (const msg of messages) {
@@ -2618,6 +2894,10 @@ function createClient2(options) {
2618
2894
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
2619
2895
  // Default transport lifts undici's 300s header/body caps (see provider-fetch.ts).
2620
2896
  fetch: options.fetch ?? providerFetch,
2897
+ // The agent loop owns retries (classified backoff, Retry-After, UI retry
2898
+ // events). The SDK default of 2 silent retries stacked under it: one 5xx
2899
+ // cost up to 3 hidden requests plus SDK sleeps before the loop even saw it.
2900
+ maxRetries: 0,
2621
2901
  ...options.defaultHeaders ? { defaultHeaders: options.defaultHeaders } : {}
2622
2902
  });
2623
2903
  if (!options.fetch) {
@@ -2729,11 +3009,11 @@ async function* runStream2(options) {
2729
3009
  }
2730
3010
  const requestParams = mergeProviderOptions(params, options.providerOptions);
2731
3011
  if (getEnvironment()?.GGAI_DUMP_REQUEST) {
2732
- const fs = await import("fs");
3012
+ const fs2 = await import("fs");
2733
3013
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2734
3014
  const dumpPath = `/tmp/ggai-request-${ts}.json`;
2735
- fs.writeFileSync(dumpPath, JSON.stringify(requestParams, null, 2));
2736
- fs.appendFileSync(
3015
+ fs2.writeFileSync(dumpPath, JSON.stringify(requestParams, null, 2));
3016
+ fs2.appendFileSync(
2737
3017
  "/tmp/ggai-requests.log",
2738
3018
  `[${ts}] ${dumpPath} messages=${requestParams.messages.length}
2739
3019
  `
@@ -3063,7 +3343,7 @@ function toError2(err, provider = "openai") {
3063
3343
  }
3064
3344
 
3065
3345
  // src/providers/openai-codex.ts
3066
- import os from "os";
3346
+ import os2 from "os";
3067
3347
  import * as zstd from "@bokuweb/zstd-wasm";
3068
3348
 
3069
3349
  // src/utils/sse.ts
@@ -3392,6 +3672,15 @@ async function* consumeResponsesEvents(events, ctx) {
3392
3672
  droppedToolCalls
3393
3673
  });
3394
3674
  }
3675
+ const addedOrder = [...toolCalls.keys()];
3676
+ const toolSlots = orderedItems.flatMap((e, i) => e.kind === "tool" ? [i] : []);
3677
+ const sortedTools = toolSlots.map((i) => orderedItems[i]).sort(
3678
+ (a, b) => a?.kind === "tool" && b?.kind === "tool" ? addedOrder.indexOf(a.id) - addedOrder.indexOf(b.id) : 0
3679
+ );
3680
+ toolSlots.forEach((slot, k) => {
3681
+ const entry = sortedTools[k];
3682
+ if (entry) orderedItems[slot] = entry;
3683
+ });
3395
3684
  const seenTool = /* @__PURE__ */ new Set();
3396
3685
  let textInserted = false;
3397
3686
  for (const entry of orderedItems) {
@@ -3468,6 +3757,8 @@ function toResponsesInput(messages, options) {
3468
3757
  let system;
3469
3758
  const input = [];
3470
3759
  const idMap = /* @__PURE__ */ new Map();
3760
+ const loadedTools = /* @__PURE__ */ new Set();
3761
+ let toolLoads = 0;
3471
3762
  for (const msg of messages) {
3472
3763
  if (msg.role === "system") {
3473
3764
  system = msg.content;
@@ -3521,6 +3812,28 @@ function toResponsesInput(messages, options) {
3521
3812
  call_id: mapId(callId, idMap),
3522
3813
  output: text.length > 0 ? text : "(see attached image)"
3523
3814
  });
3815
+ const loads = options.toolSearchLoads;
3816
+ const names = loads ? (result.loadedTools ?? []).filter((name) => loads.has(name) && !loadedTools.has(name)) : [];
3817
+ if (loads && names.length > 0) {
3818
+ for (const name of names) loadedTools.add(name);
3819
+ const loadId = `gg_tool_load_${++toolLoads}`;
3820
+ input.push(
3821
+ {
3822
+ type: "tool_search_call",
3823
+ call_id: loadId,
3824
+ execution: "client",
3825
+ status: "completed",
3826
+ arguments: { query: names.join(" "), limit: names.length }
3827
+ },
3828
+ {
3829
+ type: "tool_search_output",
3830
+ call_id: loadId,
3831
+ execution: "client",
3832
+ status: "completed",
3833
+ tools: names.map((name) => loads.get(name))
3834
+ }
3835
+ );
3836
+ }
3524
3837
  if (options.supportsImages !== false && Array.isArray(result.content)) {
3525
3838
  for (const block of result.content) {
3526
3839
  if (block.type === "image") toolImages.push(block);
@@ -3619,8 +3932,19 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
3619
3932
  const url = `${baseUrl}/codex/responses`;
3620
3933
  const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
3621
3934
  const downgraded = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
3935
+ const deferredPlan = supportsResponsesToolSearch(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgraded) : void 0;
3936
+ const strictTools = options.strictTools ?? true;
3937
+ const toolSearchLoads = deferredPlan ? new Map(
3938
+ [...deferredPlan.loaded].map((name) => {
3939
+ const tool = deferredPlan.byName.get(name);
3940
+ const [wire] = tool ? toCodexTools([tool], strictTools) : [];
3941
+ return [name, { ...wire, defer_loading: true }];
3942
+ })
3943
+ ) : void 0;
3944
+ const liveTools = deferredPlan?.live ?? options.tools;
3622
3945
  const { system, input } = toResponsesInput(downgraded, {
3623
3946
  supportsImages: options.supportsImages,
3947
+ toolSearchLoads,
3624
3948
  mapId: remapCodexId,
3625
3949
  // Stamped reasoning belongs to another transport (e.g. Grok's) and would
3626
3950
  // be rejected here; Codex's own items are never stamped.
@@ -3634,12 +3958,12 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
3634
3958
  stream: true,
3635
3959
  instructions: system,
3636
3960
  input,
3637
- tool_choice: toCodexToolChoice(options.toolChoice, options.tools),
3961
+ tool_choice: toCodexToolChoice(options.toolChoice, liveTools),
3638
3962
  parallel_tool_calls: !liteShape,
3639
3963
  include: ["reasoning.encrypted_content"]
3640
3964
  };
3641
- if (options.tools?.length) {
3642
- body.tools = toCodexTools(options.tools, options.strictTools ?? true);
3965
+ if (liveTools?.length) {
3966
+ body.tools = toCodexTools(liveTools, strictTools);
3643
3967
  }
3644
3968
  body.prompt_cache_key = normalizePromptCacheKey(options.promptCacheKey ?? "ggcoder");
3645
3969
  if (options.temperature != null && !options.thinking) {
@@ -3665,7 +3989,7 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
3665
3989
  Authorization: `Bearer ${options.apiKey}`,
3666
3990
  "OpenAI-Beta": "responses=experimental",
3667
3991
  originator: responsesLite ? "codex_cli_rs" : "ggcoder",
3668
- "User-Agent": responsesLite ? `codex_cli_rs/${CODEX_CLIENT_VERSION}` : `ggcoder (${os.platform()} ${os.release()}; ${os.arch()})`,
3992
+ "User-Agent": responsesLite ? `codex_cli_rs/${CODEX_CLIENT_VERSION}` : `ggcoder (${os2.platform()} ${os2.release()}; ${os2.arch()})`,
3669
3993
  ...responsesLite ? { version: CODEX_CLIENT_VERSION } : {},
3670
3994
  ...liteShape ? { "X-OpenAI-Internal-Codex-Responses-Lite": "true" } : {}
3671
3995
  };
@@ -4789,8 +5113,8 @@ function bound(text) {
4789
5113
  const trimmed = text.trim();
4790
5114
  return trimmed.length > MAX_ERROR_MESSAGE_CHARS ? `${trimmed.slice(0, MAX_ERROR_MESSAGE_CHARS)}\u2026` : trimmed;
4791
5115
  }
4792
- function joinUrl(baseUrl, path) {
4793
- return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
5116
+ function joinUrl(baseUrl, path2) {
5117
+ return `${baseUrl.replace(/\/+$/, "")}/${path2.replace(/^\/+/, "")}`;
4794
5118
  }
4795
5119
 
4796
5120
  // src/providers/gemini-embeddings.ts
@@ -5381,7 +5705,7 @@ function stream(requested) {
5381
5705
  observePreparedContext(
5382
5706
  replayable === sanitized ? wireMessages : replayable,
5383
5707
  messages,
5384
- options.tools ?? []
5708
+ observedTools(options, messages)
5385
5709
  )
5386
5710
  );
5387
5711
  } catch {
@@ -5389,6 +5713,11 @@ function stream(requested) {
5389
5713
  }
5390
5714
  return entry.stream(messages === options.messages ? options : { ...options, messages });
5391
5715
  }
5716
+ function observedTools(options, messages) {
5717
+ if (!usesDeferredToolLoading(options)) return options.tools ?? [];
5718
+ const plan = planDeferredTools(options.tools, options.deferredTools, messages);
5719
+ return plan ? [...plan.live, ...plan.deferred] : options.tools ?? [];
5720
+ }
5392
5721
  function stripMessageProvenance(messages) {
5393
5722
  let stripped;
5394
5723
  for (let index = 0; index < messages.length; index++) {
@@ -5528,6 +5857,85 @@ function l2Normalize(vector) {
5528
5857
  return vector.map((value) => value / norm);
5529
5858
  }
5530
5859
 
5860
+ // src/utils/preconnect.ts
5861
+ var PRECONNECT_INTERVAL_MS = 3e4;
5862
+ function defaultBaseUrl(provider, accountId) {
5863
+ switch (provider) {
5864
+ case "anthropic":
5865
+ return "https://api.anthropic.com";
5866
+ case "openai":
5867
+ return accountId ? "https://chatgpt.com/backend-api" : "https://api.openai.com/v1";
5868
+ case "gemini":
5869
+ return "https://cloudcode-pa.googleapis.com";
5870
+ case "glm":
5871
+ return "https://api.z.ai/api/coding/paas/v4";
5872
+ case "moonshot":
5873
+ return "https://api.moonshot.ai/v1";
5874
+ case "deepseek":
5875
+ return "https://api.deepseek.com/v1";
5876
+ case "openrouter":
5877
+ return "https://openrouter.ai/api/v1";
5878
+ case "xiaomi":
5879
+ return "https://token-plan-sgp.xiaomimimo.com/v1";
5880
+ case "minimax":
5881
+ return "https://api.minimax.io/anthropic";
5882
+ case "xai":
5883
+ return "https://api.x.ai/v1";
5884
+ case "huggingface":
5885
+ return "https://router.huggingface.co/v1";
5886
+ case "sakana":
5887
+ return "https://api.sakana.ai/v1";
5888
+ case "local":
5889
+ case "palsu":
5890
+ return void 0;
5891
+ }
5892
+ }
5893
+ function isLocalHost(hostname) {
5894
+ const host = hostname.replace(/^\[|\]$/g, "").toLowerCase();
5895
+ if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) return true;
5896
+ if (host === "::1" || host === "0.0.0.0") return true;
5897
+ if (host.includes(":")) return /^(f[cd]|fe[89ab])/.test(host);
5898
+ const octets = host.split(".").map(Number);
5899
+ if (octets.length !== 4 || octets.some((n) => !Number.isInteger(n))) return false;
5900
+ const [a = 0, b = 0] = octets;
5901
+ return a === 127 || a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31;
5902
+ }
5903
+ function preconnectTarget(options) {
5904
+ const raw = options.baseUrl ?? defaultBaseUrl(options.provider, options.accountId);
5905
+ if (!raw || options.provider === "local" || options.provider === "palsu") return void 0;
5906
+ let url;
5907
+ try {
5908
+ url = new URL(raw);
5909
+ } catch {
5910
+ return void 0;
5911
+ }
5912
+ if (url.protocol !== "https:" || isLocalHost(url.hostname)) return void 0;
5913
+ url.pathname = url.pathname.replace(/\/+$/, "") || "/";
5914
+ url.search = "";
5915
+ url.hash = "";
5916
+ return url;
5917
+ }
5918
+ function preconnectOrigin(options) {
5919
+ return preconnectTarget(options)?.origin;
5920
+ }
5921
+ function preconnectProvider(options) {
5922
+ const target = preconnectTarget(options);
5923
+ if (!target) return void 0;
5924
+ const origin = target.origin;
5925
+ const now = options.now ?? Date.now();
5926
+ const last = options.warmedAt.get(origin);
5927
+ if (last !== void 0 && now - last < PRECONNECT_INTERVAL_MS) return void 0;
5928
+ options.warmedAt.set(origin, now);
5929
+ const fetchImpl = options.fetch ?? providerFetch;
5930
+ void fetchImpl(target.href, {
5931
+ method: "HEAD",
5932
+ redirect: "manual",
5933
+ signal: AbortSignal.timeout(1e4)
5934
+ }).then((response) => response.body?.cancel()).catch(() => {
5935
+ });
5936
+ return origin;
5937
+ }
5938
+
5531
5939
  // src/chat-error.ts
5532
5940
  function causeCode(error) {
5533
5941
  if (!(error instanceof Error) || !error.cause || typeof error.cause !== "object") return "";
@@ -6000,10 +6408,12 @@ function palsuRerankScore(query, document) {
6000
6408
  return hits / queryTokens.size;
6001
6409
  }
6002
6410
  export {
6411
+ CLAUDE_CODE_FALLBACK_VERSION,
6003
6412
  CODEX_CLIENT_VERSION,
6004
6413
  EventStream,
6005
6414
  FAST_MODE_SUFFIX,
6006
6415
  GGAIError,
6416
+ PRECONNECT_INTERVAL_MS,
6007
6417
  ProviderError,
6008
6418
  REDACTED as REDACTION_MARKER,
6009
6419
  ResponseStalledError,
@@ -6019,6 +6429,8 @@ export {
6019
6429
  formatChatError,
6020
6430
  formatError,
6021
6431
  formatErrorForDisplay,
6432
+ getClaudeCliUserAgent,
6433
+ getClaudeCodeVersion,
6022
6434
  hasLoneSurrogate,
6023
6435
  isHardBillingMessage,
6024
6436
  isUsageLimitError,
@@ -6032,6 +6444,9 @@ export {
6032
6444
  palsuThinking,
6033
6445
  palsuToolCall,
6034
6446
  parseFastModeRejection,
6447
+ pendingClaudeCodeVersionRefresh,
6448
+ preconnectOrigin,
6449
+ preconnectProvider,
6035
6450
  prewarmAnthropicCache,
6036
6451
  providerRegistry,
6037
6452
  redactText,