@kenkaiiii/gg-ai 5.93.0 → 5.95.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
@@ -2,7 +2,7 @@
2
2
  var FAST_MODE_SUFFIX = "-fast";
3
3
  var FAST_MODE_BASE_MODELS = {
4
4
  anthropic: ["claude-opus-5-5"],
5
- openai: ["gpt-6-astra", "gpt-6.1-sol", "gpt-6-luna"]
5
+ openai: ["gpt-6-astra", "gpt-6.1-sol", "gpt-6.1-terra", "gpt-6-luna"]
6
6
  };
7
7
  function resolveFastModeModel(provider, model) {
8
8
  if (!model.endsWith(FAST_MODE_SUFFIX)) return { model, fast: false };
@@ -243,6 +243,18 @@ function formatError(err) {
243
243
  }
244
244
  const miniMax = err.provider === "minimax" ? formatMiniMaxError(err, cleanMessage) : void 0;
245
245
  if (miniMax) return miniMax;
246
+ if (isUsageLimitError(err) && err.provider === "openai" && /chatgpt/i.test(err.message)) {
247
+ return {
248
+ headline: "ChatGPT plan usage limit reached.",
249
+ source: "provider",
250
+ message: "GG Coder has used what your ChatGPT plan allows for now. That can be your plan's limit or one you set for GG Coder.",
251
+ provider: err.provider,
252
+ statusCode: err.statusCode,
253
+ ...err.requestId ? { requestId: err.requestId } : {},
254
+ ...err.resetsAt ? { resetsAt: err.resetsAt } : {},
255
+ guidance: err.hint ?? "Check or raise it at chatgpt.com/settings/usage, or add an OpenAI API key to keep going."
256
+ };
257
+ }
246
258
  if (isUsageLimitError(err)) {
247
259
  const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
248
260
  return {
@@ -608,145 +620,6 @@ var StreamResult = class _StreamResult {
608
620
  }
609
621
  };
610
622
 
611
- // src/claude-code-version.ts
612
- import fs from "fs/promises";
613
- import os from "os";
614
- import path from "path";
615
-
616
- // src/utils/diag.ts
617
- var _diagFn = null;
618
- function setProviderDiagnostic(fn) {
619
- _diagFn = fn;
620
- }
621
- function providerDiag(phase, data) {
622
- _diagFn?.(phase, data);
623
- }
624
-
625
- // src/claude-code-version.ts
626
- var NPM_LATEST_URL = "https://registry.npmjs.org/@anthropic-ai/claude-code/latest";
627
- var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
628
- var FETCH_TIMEOUT_MS = 3e3;
629
- var CLAUDE_CODE_FALLBACK_VERSION = "2.1.316";
630
- function isOlder(a, b) {
631
- const pa = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
632
- const pb = b.split(".").map((n) => Number.parseInt(n, 10) || 0);
633
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
634
- const x = pa[i] ?? 0;
635
- const y = pb[i] ?? 0;
636
- if (x !== y) return x < y;
637
- }
638
- return false;
639
- }
640
- function atLeastFallback(version) {
641
- return isOlder(version, CLAUDE_CODE_FALLBACK_VERSION) ? CLAUDE_CODE_FALLBACK_VERSION : version;
642
- }
643
- var memoryCache = null;
644
- var inflight = null;
645
- function agentDir() {
646
- return path.join(os.homedir(), ".gg");
647
- }
648
- function cachePath() {
649
- return path.join(agentDir(), "claude-code-version.json");
650
- }
651
- async function readDiskCache() {
652
- try {
653
- const raw = await fs.readFile(cachePath(), "utf-8");
654
- const parsed = JSON.parse(raw);
655
- if (typeof parsed.version === "string" && typeof parsed.fetchedAt === "number") {
656
- return parsed;
657
- }
658
- return null;
659
- } catch {
660
- return null;
661
- }
662
- }
663
- async function writeDiskCache(data) {
664
- try {
665
- await fs.mkdir(agentDir(), { recursive: true, mode: 448 });
666
- await fs.writeFile(cachePath(), JSON.stringify(data), { mode: 384 });
667
- } catch (err) {
668
- providerDiag("claude_code_version_cache_write_failed", {
669
- error: err instanceof Error ? err.message : String(err)
670
- });
671
- }
672
- }
673
- async function fetchLatest() {
674
- const controller = new AbortController();
675
- const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
676
- try {
677
- const response = await fetch(NPM_LATEST_URL, { signal: controller.signal });
678
- if (!response.ok) return null;
679
- const data = await response.json();
680
- if (typeof data.version === "string" && /^\d/.test(data.version)) {
681
- return data.version;
682
- }
683
- return null;
684
- } catch {
685
- return null;
686
- } finally {
687
- clearTimeout(timer);
688
- }
689
- }
690
- var refreshing = null;
691
- function refreshInBackground() {
692
- refreshing ??= (async () => {
693
- try {
694
- const fetched = await fetchLatest();
695
- if (!fetched) return;
696
- await writeDiskCache({ version: fetched, fetchedAt: Date.now() });
697
- memoryCache = { version: fetched, expiresAt: Date.now() + CACHE_TTL_MS };
698
- } finally {
699
- refreshing = null;
700
- }
701
- })();
702
- return refreshing;
703
- }
704
- function pendingClaudeCodeVersionRefresh() {
705
- return refreshing;
706
- }
707
- async function getClaudeCodeVersion() {
708
- if (memoryCache && Date.now() < memoryCache.expiresAt) {
709
- return memoryCache.version;
710
- }
711
- if (inflight) return inflight;
712
- inflight = (async () => {
713
- const disk = await readDiskCache();
714
- const diskFresh = disk && Date.now() - disk.fetchedAt < CACHE_TTL_MS;
715
- if (disk && diskFresh) {
716
- const version = atLeastFallback(disk.version);
717
- memoryCache = { version, expiresAt: Date.now() + CACHE_TTL_MS };
718
- return version;
719
- }
720
- if (disk) {
721
- const version = atLeastFallback(disk.version);
722
- memoryCache = { version, expiresAt: Date.now() + 5 * 60 * 1e3 };
723
- void refreshInBackground();
724
- return version;
725
- }
726
- const fetched = await fetchLatest();
727
- if (fetched) {
728
- await writeDiskCache({ version: fetched, fetchedAt: Date.now() });
729
- memoryCache = { version: fetched, expiresAt: Date.now() + CACHE_TTL_MS };
730
- return fetched;
731
- }
732
- const resolved = CLAUDE_CODE_FALLBACK_VERSION;
733
- memoryCache = { version: resolved, expiresAt: Date.now() + 5 * 60 * 1e3 };
734
- providerDiag("claude_code_version_fallback", { version: resolved });
735
- return resolved;
736
- })();
737
- try {
738
- return await inflight;
739
- } finally {
740
- inflight = null;
741
- }
742
- }
743
- function claudeCliUserAgent(version) {
744
- return `claude-cli/${version} (external, cli)`;
745
- }
746
- async function getClaudeCliUserAgent() {
747
- return claudeCliUserAgent(await getClaudeCodeVersion());
748
- }
749
-
750
623
  // src/utils/provider-options.ts
751
624
  var RESERVED_BODY_FIELDS = /* @__PURE__ */ new Set([
752
625
  "contents",
@@ -1282,7 +1155,7 @@ function toolCallNameRuleFor(provider, options) {
1282
1155
  return TOOL_CALL_NAME_RULES.anthropic;
1283
1156
  case "openai":
1284
1157
  if (options?.accountId) return TOOL_CALL_NAME_RULES["openai-responses"];
1285
- return isDefaultOrHost(options?.baseUrl, "api.openai.com") ? TOOL_CALL_NAME_RULES["openai-chat"] : TOOL_CALL_NAME_RULES.generic;
1158
+ return isDefaultOrHost(options?.baseUrl, "api.openai.com") ? TOOL_CALL_NAME_RULES["openai-responses"] : TOOL_CALL_NAME_RULES.generic;
1286
1159
  case "gemini":
1287
1160
  return TOOL_CALL_NAME_RULES.gemini;
1288
1161
  default:
@@ -1480,8 +1353,8 @@ function usesDeferredToolLoading(options) {
1480
1353
  const firstParty = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
1481
1354
  return firstParty && supportsAnthropicDeferredTools(options.model);
1482
1355
  }
1483
- if (options.provider === "openai" && options.accountId) {
1484
- return supportsResponsesToolSearch(options.model);
1356
+ if (options.provider === "openai" && !options.accountId) {
1357
+ return isDefaultOrHost(options.baseUrl, "api.openai.com") && supportsResponsesToolSearch(options.model);
1485
1358
  }
1486
1359
  return false;
1487
1360
  }
@@ -1972,7 +1845,7 @@ function toGlmReasoningEffort(level) {
1972
1845
  }
1973
1846
  function toCodexReasoningEffort(level, model) {
1974
1847
  if (level !== "ultra") return level;
1975
- return model === "gpt-6-astra" || model === "gpt-6.1-sol" ? "xhigh" : "max";
1848
+ return model === "gpt-6-astra" || model === "gpt-6.1-sol" || model === "gpt-6.1-terra" ? "xhigh" : "max";
1976
1849
  }
1977
1850
  var DECLARED_EFFORT_ORDER = ["low", "medium", "high", "xhigh", "max"];
1978
1851
  function effortForRequest(level, model, declared) {
@@ -2056,12 +1929,12 @@ function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {
2056
1929
  };
2057
1930
  const dispatcher = useEnvProxy ? new EnvHttpProxyAgent(timeouts) : new Agent(timeouts);
2058
1931
  const builtinFetch = globalThis.fetch;
2059
- const providerFetch2 = async (input, init2) => {
2060
- if (globalThis.fetch !== builtinFetch || input instanceof Request || init2?.body instanceof FormData) {
2061
- return globalThis.fetch(input, init2);
1932
+ const providerFetch2 = async (input, init) => {
1933
+ if (globalThis.fetch !== builtinFetch || input instanceof Request || init?.body instanceof FormData) {
1934
+ return globalThis.fetch(input, init);
2062
1935
  }
2063
1936
  const response = await undiciFetch(input, {
2064
- ...init2,
1937
+ ...init,
2065
1938
  dispatcher
2066
1939
  });
2067
1940
  return response;
@@ -2069,8 +1942,8 @@ function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {
2069
1942
  return Object.assign(providerFetch2, { Response: globalThis.Response });
2070
1943
  }
2071
1944
  function withBodyActivity(fetchImpl, onActivity) {
2072
- return async (input, init2) => {
2073
- const response = await fetchImpl(input, init2);
1945
+ return async (input, init) => {
1946
+ const response = await fetchImpl(input, init);
2074
1947
  if (!response.body || !response.headers.get("content-type")?.includes("text/event-stream")) {
2075
1948
  return response;
2076
1949
  }
@@ -2109,8 +1982,7 @@ var anthropicServerFallback = {
2109
1982
  }
2110
1983
  };
2111
1984
  function serverFallbackKey(options) {
2112
- const auth = options.apiKey?.startsWith("sk-ant-oat") ? "oauth" : "key";
2113
- return `${options.baseUrl ?? process.env.ANTHROPIC_BASE_URL ?? ""}|${auth}`;
1985
+ return options.baseUrl ?? process.env.ANTHROPIC_BASE_URL ?? "";
2114
1986
  }
2115
1987
  function isDirectAnthropicApi(baseUrl) {
2116
1988
  const effective = baseUrl ?? process.env.ANTHROPIC_BASE_URL;
@@ -2143,30 +2015,18 @@ function sumUsageIterations(usage) {
2143
2015
  }
2144
2016
  return totals;
2145
2017
  }
2146
- async function withClaudeUserAgent(options) {
2147
- if (options.userAgent || !options.apiKey?.startsWith("sk-ant-oat")) return options;
2148
- return { ...options, userAgent: await getClaudeCliUserAgent() };
2149
- }
2150
2018
  function createClient(options) {
2151
- const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
2152
- const userAgent = isOAuth ? options.userAgent ?? claudeCliUserAgent(CLAUDE_CODE_FALLBACK_VERSION) : "";
2153
- const cacheKey = `${options.apiKey ?? ""}|${options.baseUrl ?? ""}|${userAgent}`;
2019
+ const cacheKey = `${options.apiKey ?? ""}|${options.baseUrl ?? ""}`;
2154
2020
  if (!options.fetch) {
2155
2021
  const cached = anthropicClientCache.get(cacheKey);
2156
2022
  if (cached) return cached;
2157
2023
  }
2158
2024
  const client = new Anthropic({
2159
- ...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
2025
+ apiKey: options.apiKey,
2160
2026
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
2161
2027
  // Default transport lifts undici's 300s header/body caps (see provider-fetch.ts).
2162
2028
  fetch: options.fetch ?? providerFetch,
2163
- maxRetries: 0,
2164
- ...isOAuth ? {
2165
- defaultHeaders: {
2166
- "user-agent": userAgent,
2167
- "x-app": "cli"
2168
- }
2169
- } : {}
2029
+ maxRetries: 0
2170
2030
  });
2171
2031
  if (!options.fetch) {
2172
2032
  if (anthropicClientCache.size >= 8) {
@@ -2179,13 +2039,10 @@ function createClient(options) {
2179
2039
  }
2180
2040
  async function prewarmAnthropicCache(options) {
2181
2041
  try {
2182
- const client = createClient(
2183
- await withClaudeUserAgent({
2184
- apiKey: options.apiKey,
2185
- baseUrl: options.baseUrl,
2186
- userAgent: options.userAgent
2187
- })
2188
- );
2042
+ const client = createClient({
2043
+ apiKey: options.apiKey,
2044
+ baseUrl: options.baseUrl
2045
+ });
2189
2046
  const cacheControl = toAnthropicCacheControl(options.cacheRetention ?? "long", options.baseUrl);
2190
2047
  const { system, messages } = toAnthropicMessages(
2191
2048
  [
@@ -2194,21 +2051,13 @@ async function prewarmAnthropicCache(options) {
2194
2051
  ],
2195
2052
  cacheControl
2196
2053
  );
2197
- const isOAuth = options.apiKey.startsWith("sk-ant-oat");
2198
- const fullSystem = isOAuth ? [
2199
- {
2200
- type: "text",
2201
- text: "You are Claude Code, Anthropic's official CLI for Claude."
2202
- },
2203
- ...system ?? []
2204
- ] : system;
2205
2054
  const tools = options.tools?.length ? toAnthropicTools(options.tools, {
2206
2055
  cacheControl,
2207
2056
  // Keep the serialized tool bytes identical to runStream so the
2208
2057
  // prewarmed prompt cache actually hits — both are gated by the flag.
2209
2058
  enableFineGrainedToolStreaming: fineGrainedToolStreamingEnabled()
2210
2059
  }) : void 0;
2211
- if (estimatePrefixTokens(fullSystem, tools) < anthropicMinCacheableTokens(options.model)) {
2060
+ if (estimatePrefixTokens(system, tools) < anthropicMinCacheableTokens(options.model)) {
2212
2061
  return;
2213
2062
  }
2214
2063
  await client.messages.create(
@@ -2216,7 +2065,7 @@ async function prewarmAnthropicCache(options) {
2216
2065
  model: options.model,
2217
2066
  max_tokens: 1,
2218
2067
  messages,
2219
- ...fullSystem ? { system: fullSystem } : {},
2068
+ ...system ? { system } : {},
2220
2069
  ...tools ? {
2221
2070
  tools: [
2222
2071
  ...tools,
@@ -2227,10 +2076,7 @@ async function prewarmAnthropicCache(options) {
2227
2076
  {
2228
2077
  signal: options.signal ?? void 0,
2229
2078
  ...(() => {
2230
- const betas = [
2231
- ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
2232
- ...cacheControl?.ttl === "1h" ? ["extended-cache-ttl-2025-04-11"] : []
2233
- ];
2079
+ const betas = cacheControl?.ttl === "1h" ? ["extended-cache-ttl-2025-04-11"] : [];
2234
2080
  return betas.length ? { headers: { "anthropic-beta": betas.join(",") } } : {};
2235
2081
  })()
2236
2082
  }
@@ -2242,8 +2088,7 @@ function streamAnthropic(options) {
2242
2088
  return new StreamResult(runStream(options), options.signal);
2243
2089
  }
2244
2090
  async function* runStream(options) {
2245
- const client = createClient(await withClaudeUserAgent(options));
2246
- const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
2091
+ const client = createClient(options);
2247
2092
  const useStreaming = options.streaming !== false && !options.prewarm;
2248
2093
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
2249
2094
  const supportsFirstPartyToolExtras = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
@@ -2253,18 +2098,11 @@ async function* runStream(options) {
2253
2098
  const useServerFallback = options.provider !== "minimax" && isDirectAnthropicApi(options.baseUrl) && !anthropicServerFallback.disabled.has(fallbackKey);
2254
2099
  const deferredPlan = options.provider === "anthropic" && isDirectAnthropicApi(options.baseUrl) && supportsAnthropicDeferredTools(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgradedMessages) : void 0;
2255
2100
  const toolReferences = deferredPlan?.loaded;
2256
- const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl, {
2101
+ const { system, messages } = toAnthropicMessages(downgradedMessages, cacheControl, {
2257
2102
  fallbackBlocks: useServerFallback,
2258
2103
  toolReferences
2259
2104
  });
2260
2105
  const useFastMode = options.speed === "fast" && options.provider === "anthropic" && isDirectAnthropicApi(options.baseUrl);
2261
- const system = isOAuth ? [
2262
- {
2263
- type: "text",
2264
- text: "You are Claude Code, Anthropic's official CLI for Claude."
2265
- },
2266
- ...rawSystem ?? []
2267
- ] : rawSystem;
2268
2106
  let maxTokens = options.maxTokens ?? 4096;
2269
2107
  let thinking;
2270
2108
  let outputConfig;
@@ -2340,7 +2178,6 @@ async function* runStream(options) {
2340
2178
  const params = mergeProviderOptions(baseParams, options.providerOptions);
2341
2179
  const hasAdaptiveThinking = isAdaptiveThinkingModel(options.model);
2342
2180
  const betaHeaders = [
2343
- ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
2344
2181
  ...options.compaction ? ["compact-2026-01-12"] : [],
2345
2182
  ...options.clearToolUses ? ["context-management-2025-06-27"] : [],
2346
2183
  // Eager tool-input streaming beta — opt-in only (see
@@ -2836,6 +2673,15 @@ function fnv1aHash(value) {
2836
2673
  return (hash >>> 0).toString(16).padStart(8, "0");
2837
2674
  }
2838
2675
 
2676
+ // src/utils/diag.ts
2677
+ var _diagFn = null;
2678
+ function setProviderDiagnostic(fn) {
2679
+ _diagFn = fn;
2680
+ }
2681
+ function providerDiag(phase, data) {
2682
+ _diagFn?.(phase, data);
2683
+ }
2684
+
2839
2685
  // src/providers/moonshot-video.ts
2840
2686
  async function uploadMoonshotVideos(client, messages, signal) {
2841
2687
  for (const msg of messages) {
@@ -3079,11 +2925,11 @@ async function* runStream2(options) {
3079
2925
  }
3080
2926
  const requestParams = mergeProviderOptions(params, options.providerOptions);
3081
2927
  if (getEnvironment()?.GGAI_DUMP_REQUEST) {
3082
- const fs2 = await import("fs");
2928
+ const fs = await import("fs");
3083
2929
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3084
2930
  const dumpPath = `/tmp/ggai-request-${ts}.json`;
3085
- fs2.writeFileSync(dumpPath, JSON.stringify(requestParams, null, 2));
3086
- fs2.appendFileSync(
2931
+ fs.writeFileSync(dumpPath, JSON.stringify(requestParams, null, 2));
2932
+ fs.appendFileSync(
3087
2933
  "/tmp/ggai-requests.log",
3088
2934
  `[${ts}] ${dumpPath} messages=${requestParams.messages.length}
3089
2935
  `
@@ -3412,10 +3258,6 @@ function toError2(err, provider = "openai") {
3412
3258
  return new ProviderError(provider, String(err));
3413
3259
  }
3414
3260
 
3415
- // src/providers/openai-codex.ts
3416
- import os2 from "os";
3417
- import * as zstd from "@bokuweb/zstd-wasm";
3418
-
3419
3261
  // src/utils/sse.ts
3420
3262
  function parseSseBuffer(buffer) {
3421
3263
  const events = [];
@@ -3467,6 +3309,9 @@ function extractRequestIdFromMessage(message) {
3467
3309
  return match?.[1];
3468
3310
  }
3469
3311
 
3312
+ // src/user-agent.ts
3313
+ var GG_CODER_USER_AGENT = "ggcoder (+https://github.com/KenKaiii/gg-coder)";
3314
+
3470
3315
  // src/providers/responses-stream.ts
3471
3316
  function isEncryptedReasoning(data) {
3472
3317
  return data.type === "reasoning" && typeof data.id === "string" && typeof data.encrypted_content === "string";
@@ -3514,9 +3359,11 @@ async function* consumeResponsesEvents(events, ctx) {
3514
3359
  });
3515
3360
  }
3516
3361
  if (type === "response.failed") {
3517
- const nested = event.error;
3362
+ const nested = event.error ?? event.response?.error;
3518
3363
  const message = nested?.message ?? `${ctx.label} response failed.`;
3519
3364
  const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
3365
+ const usageLimit = nested ? ctx.errorChunk?.(nested, requestId) : null;
3366
+ if (usageLimit) throw usageLimit;
3520
3367
  throw new ProviderError(ctx.provider, message, {
3521
3368
  ...requestId != null ? { requestId } : {}
3522
3369
  });
@@ -3866,6 +3713,7 @@ function toResponsesInput(messages, options) {
3866
3713
  ...sendItemId ? { id: mapId(itemId ?? callId, idMap) } : {},
3867
3714
  call_id: mapId(callId, idMap),
3868
3715
  name: part.name,
3716
+ ...options.toolNamespace ? { namespace: options.toolNamespace } : {},
3869
3717
  arguments: JSON.stringify(part.args)
3870
3718
  });
3871
3719
  }
@@ -3929,85 +3777,46 @@ function toResponsesInput(messages, options) {
3929
3777
  return { system, input };
3930
3778
  }
3931
3779
 
3932
- // src/providers/openai-codex.ts
3933
- var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
3934
- var CODEX_CLIENT_VERSION = "0.166.0";
3935
- var CODEX_REQUEST_COMPRESSION_MIN_BYTES = 16 * 1024;
3936
- var zstdInitPromise;
3937
- async function encodeCodexRequest(body) {
3938
- const json = JSON.stringify(body);
3939
- const raw = new TextEncoder().encode(json);
3940
- if (raw.byteLength < CODEX_REQUEST_COMPRESSION_MIN_BYTES) {
3941
- return {
3942
- body: json,
3943
- compressed: false,
3944
- rawBytes: raw.byteLength,
3945
- encodedBytes: raw.byteLength
3946
- };
3947
- }
3948
- try {
3949
- zstdInitPromise ??= zstd.init();
3950
- await zstdInitPromise;
3951
- const compressed = Uint8Array.from(zstd.compress(raw));
3952
- if (compressed.byteLength >= raw.byteLength) {
3953
- return {
3954
- body: json,
3955
- compressed: false,
3956
- rawBytes: raw.byteLength,
3957
- encodedBytes: raw.byteLength
3958
- };
3959
- }
3960
- return {
3961
- body: compressed,
3962
- compressed: true,
3963
- rawBytes: raw.byteLength,
3964
- encodedBytes: compressed.byteLength
3965
- };
3966
- } catch (error) {
3967
- providerDiag("codex_request_compression_failed", {
3968
- error: error instanceof Error ? error.message : String(error),
3969
- rawBytes: raw.byteLength
3970
- });
3971
- return {
3972
- body: json,
3973
- compressed: false,
3974
- rawBytes: raw.byteLength,
3975
- encodedBytes: raw.byteLength
3976
- };
3977
- }
3978
- }
3780
+ // src/providers/openai-responses.ts
3781
+ var DEFAULT_BASE_URL = "https://api.openai.com/v1";
3782
+ var CHATGPT_PLAN_TOOL_NAMESPACE = "gg";
3783
+ var CHATGPT_USAGE_URL = "https://chatgpt.com/settings/usage";
3979
3784
  function usesResponsesLite(model) {
3980
3785
  return model.startsWith("gpt-5.6-") || model.startsWith("gpt-6-") || model.startsWith("gpt-6.");
3981
3786
  }
3982
- function toCodexToolChoice(choice, tools) {
3787
+ function isChatGPTPlanRoute(options) {
3788
+ return Boolean(options.accountId);
3789
+ }
3790
+ function toResponsesToolChoice(choice, tools) {
3983
3791
  const resolved = choice ?? "auto";
3984
3792
  if (typeof resolved === "object") {
3985
3793
  throw new GGAIError(
3986
- `OpenAI Codex does not support selecting the named tool \`${resolved.name}\`; use auto, none, or required.`,
3794
+ `OpenAI Responses does not support selecting the named tool \`${resolved.name}\`; use auto, none, or required.`,
3987
3795
  { source: "capability" }
3988
3796
  );
3989
3797
  }
3990
3798
  if (resolved === "required" && !tools?.length) {
3991
- throw new GGAIError("OpenAI Codex cannot require a tool call when no tools are configured.", {
3799
+ throw new GGAIError("OpenAI cannot require a tool call when no tools are configured.", {
3992
3800
  source: "capability"
3993
3801
  });
3994
3802
  }
3995
3803
  return resolved;
3996
3804
  }
3997
- function streamOpenAICodex(options) {
3805
+ function streamOpenAIResponses(options) {
3998
3806
  return new StreamResult(runStream3(options), options.signal);
3999
3807
  }
4000
3808
  async function* runStream3(options, retriedWithoutReasoning = false) {
3809
+ const plan = isChatGPTPlanRoute(options);
4001
3810
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
4002
- const url = `${baseUrl}/codex/responses`;
3811
+ const url = `${baseUrl}/responses`;
4003
3812
  const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
4004
3813
  const downgraded = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
4005
- const deferredPlan = supportsResponsesToolSearch(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgraded) : void 0;
3814
+ const deferredPlan = !plan && supportsResponsesToolSearch(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgraded) : void 0;
4006
3815
  const strictTools = options.strictTools ?? true;
4007
3816
  const toolSearchLoads = deferredPlan ? new Map(
4008
3817
  [...deferredPlan.loaded].map((name) => {
4009
3818
  const tool = deferredPlan.byName.get(name);
4010
- const [wire] = tool ? toCodexTools([tool], strictTools) : [];
3819
+ const [wire] = tool ? toResponsesTools([tool], strictTools) : [];
4011
3820
  return [name, { ...wire, defer_loading: true }];
4012
3821
  })
4013
3822
  ) : void 0;
@@ -4015,28 +3824,36 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4015
3824
  const { system, input } = toResponsesInput(downgraded, {
4016
3825
  supportsImages: options.supportsImages,
4017
3826
  toolSearchLoads,
4018
- mapId: remapCodexId,
3827
+ mapId: remapCallId,
3828
+ ...plan ? { toolNamespace: CHATGPT_PLAN_TOOL_NAMESPACE } : {},
4019
3829
  // Stamped reasoning belongs to another transport (e.g. Grok's) and would
4020
- // be rejected here; Codex's own items are never stamped.
3830
+ // be rejected here; OpenAI's own items are never stamped.
4021
3831
  keepReasoning: (part) => part.origin === void 0
4022
3832
  });
4023
- const responsesLite = usesResponsesLite(options.model);
4024
- const liteShape = options.responsesLite ?? responsesLite;
3833
+ const gpt6 = usesResponsesLite(options.model);
4025
3834
  const body = {
4026
3835
  model: options.model,
4027
3836
  store: false,
4028
3837
  stream: true,
4029
3838
  instructions: system,
4030
3839
  input,
4031
- tool_choice: toCodexToolChoice(options.toolChoice, liveTools),
4032
- parallel_tool_calls: !liteShape,
3840
+ tool_choice: toResponsesToolChoice(options.toolChoice, liveTools),
3841
+ parallel_tool_calls: true,
4033
3842
  include: ["reasoning.encrypted_content"]
4034
3843
  };
4035
3844
  if (liveTools?.length) {
4036
- body.tools = toCodexTools(liveTools, strictTools);
3845
+ const wire = toResponsesTools(liveTools, strictTools);
3846
+ body.tools = plan ? [
3847
+ {
3848
+ type: "namespace",
3849
+ name: CHATGPT_PLAN_TOOL_NAMESPACE,
3850
+ description: "GG Coder's tools for working in the user's project.",
3851
+ tools: wire
3852
+ }
3853
+ ] : wire;
4037
3854
  }
4038
3855
  body.prompt_cache_key = normalizePromptCacheKey(options.promptCacheKey ?? "ggcoder");
4039
- if (options.temperature != null && !options.thinking) {
3856
+ if (!plan && options.temperature != null && !options.thinking) {
4040
3857
  body.temperature = options.temperature;
4041
3858
  }
4042
3859
  if (options.speed === "fast") {
@@ -4044,57 +3861,38 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4044
3861
  }
4045
3862
  body.reasoning = {
4046
3863
  // GPT-5.6/6 require at least low; older models still support thinking off.
4047
- // Apply the floor here for every caller, including one-off prompt rewrites.
4048
- // `ultra` is a client orchestration preset, not a Codex API effort.
4049
- effort: options.thinking ? toCodexReasoningEffort(options.thinking, options.model) : responsesLite ? "low" : "none",
4050
- summary: "auto",
4051
- ...liteShape ? { context: "all_turns" } : {}
3864
+ // `ultra` is a client orchestration preset, not an API effort.
3865
+ effort: options.thinking ? toCodexReasoningEffort(options.thinking, options.model) : gpt6 ? "low" : "none",
3866
+ summary: "auto"
4052
3867
  };
4053
- if (responsesLite) {
3868
+ if (gpt6) {
4054
3869
  body.text = { verbosity: "low" };
4055
3870
  }
4056
3871
  const headers = {
4057
3872
  "Content-Type": "application/json",
4058
3873
  Accept: "text/event-stream",
4059
3874
  Authorization: `Bearer ${options.apiKey}`,
4060
- "OpenAI-Beta": "responses=experimental",
4061
- originator: responsesLite ? "codex_cli_rs" : "ggcoder",
4062
- "User-Agent": responsesLite ? `codex_cli_rs/${CODEX_CLIENT_VERSION}` : `ggcoder (${os2.platform()} ${os2.release()}; ${os2.arch()})`,
4063
- ...responsesLite ? { version: CODEX_CLIENT_VERSION } : {},
4064
- ...liteShape ? { "X-OpenAI-Internal-Codex-Responses-Lite": "true" } : {}
3875
+ "User-Agent": GG_CODER_USER_AGENT
4065
3876
  };
4066
- if (options.accountId) {
4067
- headers["chatgpt-account-id"] = options.accountId;
4068
- }
4069
- if (options.transportSessionId) {
4070
- const transportSessionId = normalizePromptCacheKey(options.transportSessionId);
4071
- headers["session_id"] = transportSessionId;
4072
- headers["x-client-request-id"] = transportSessionId;
4073
- }
4074
- const encodedRequest = await encodeCodexRequest(
4075
- mergeProviderOptions(body, options.providerOptions)
4076
- );
4077
- if (encodedRequest.compressed) headers["Content-Encoding"] = "zstd";
4078
- providerDiag("codex_request_body", {
4079
- rawBytes: encodedRequest.rawBytes,
4080
- encodedBytes: encodedRequest.encodedBytes,
4081
- compressed: encodedRequest.compressed
4082
- });
3877
+ const json = JSON.stringify(mergeProviderOptions(body, options.providerOptions));
3878
+ providerDiag("openai_responses_request_body", { rawBytes: json.length, plan });
4083
3879
  const response = await providerFetch(url, {
4084
3880
  method: "POST",
4085
3881
  headers,
4086
- body: encodedRequest.body,
3882
+ body: json,
4087
3883
  signal: options.signal
4088
3884
  });
4089
3885
  if (!response.ok) {
4090
3886
  const text = await response.text().catch(() => "");
4091
- const parsed = parseCodexErrorBody(text, response.status);
4092
- const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
3887
+ const parsed = parseErrorBody(text, response.status);
3888
+ const message = parsed.message ?? `OpenAI returned HTTP ${response.status}.`;
4093
3889
  const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
4094
3890
  if (!retriedWithoutReasoning && !options.signal?.aborted && response.status === 400 && (parsed.errorObj?.code === "invalid_encrypted_content" || /encrypted content.*could not be (?:verified|decrypted|parsed)/i.test(message)) && options.messages.some(
4095
3891
  (msg) => msg.role === "assistant" && Array.isArray(msg.content) && msg.content.some((part) => part.type === "raw" && isEncryptedReasoning(part.data))
4096
3892
  )) {
4097
- providerDiag("codex_retry_without_encrypted_reasoning", { status: response.status });
3893
+ providerDiag("openai_responses_retry_without_encrypted_reasoning", {
3894
+ status: response.status
3895
+ });
4098
3896
  const messages = options.messages.map(
4099
3897
  (msg) => msg.role === "assistant" && Array.isArray(msg.content) ? {
4100
3898
  ...msg,
@@ -4105,13 +3903,19 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4105
3903
  );
4106
3904
  return yield* runStream3({ ...options, messages }, true);
4107
3905
  }
4108
- const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
4109
- if (usageLimit) throw usageLimit;
3906
+ if (plan) {
3907
+ const planError = chatGPTPlanError(
3908
+ parsed.errorObj,
3909
+ response.status,
3910
+ requestId,
3911
+ message,
3912
+ options.model
3913
+ );
3914
+ if (planError) throw planError;
3915
+ }
4110
3916
  let hint;
4111
- if (response.status === 400 && message === `The '${options.model}' model is not supported when using Codex with a ChatGPT account.`) {
4112
- hint = "This model is not available through your ChatGPT account. Choose another available model using the model selector.";
4113
- } else if (response.status === 404 && text.includes("does not exist")) {
4114
- hint = "This model is not in OpenAI's current catalog for your ChatGPT account. Choose another available model using the model selector.";
3917
+ if (response.status === 404 && text.includes("does not exist")) {
3918
+ hint = plan ? "This model isn't available on your ChatGPT plan. Choose another model using the model selector." : "This model isn't available to your OpenAI API key. Choose another model using the model selector.";
4115
3919
  }
4116
3920
  throw new ProviderError("openai", message, {
4117
3921
  statusCode: response.status,
@@ -4120,14 +3924,14 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4120
3924
  });
4121
3925
  }
4122
3926
  if (!response.body) {
4123
- throw new ProviderError("openai", "No response body from Codex API");
3927
+ throw new ProviderError("openai", "No response body from OpenAI");
4124
3928
  }
4125
3929
  return yield* consumeResponsesEvents(parseSSE(response.body), {
4126
3930
  provider: "openai",
4127
- label: "Codex",
4128
- diagPrefix: "codex",
3931
+ label: "OpenAI",
3932
+ diagPrefix: "openai_responses",
4129
3933
  thinking: !!options.thinking,
4130
- errorChunk: (errorObj, requestId) => codexUsageLimitError(errorObj, void 0, requestId)
3934
+ errorChunk: (errorObj, requestId) => plan ? chatGPTPlanError(errorObj, void 0, requestId, void 0, options.model) : null
4131
3935
  });
4132
3936
  }
4133
3937
  async function* parseSSE(body) {
@@ -4140,7 +3944,7 @@ async function* parseSSE(body) {
4140
3944
  }
4141
3945
  }
4142
3946
  }
4143
- function remapCodexId(id, idMap) {
3947
+ function remapCallId(id, idMap) {
4144
3948
  const existing = idMap.get(id);
4145
3949
  if (existing) return existing;
4146
3950
  const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
@@ -4154,7 +3958,7 @@ function remapCodexId(id, idMap) {
4154
3958
  idMap.set(id, mapped);
4155
3959
  return mapped;
4156
3960
  }
4157
- function toCodexTools(tools, strictTools) {
3961
+ function toResponsesTools(tools, strictTools) {
4158
3962
  return tools.map((tool) => {
4159
3963
  let parameters = resolveToolSchema(tool);
4160
3964
  let strict = null;
@@ -4175,7 +3979,7 @@ function toCodexTools(tools, strictTools) {
4175
3979
  };
4176
3980
  });
4177
3981
  }
4178
- function parseCodexErrorBody(text, statusCode) {
3982
+ function parseErrorBody(text, statusCode) {
4179
3983
  if (!text) return {};
4180
3984
  try {
4181
3985
  const parsed = JSON.parse(text);
@@ -4199,23 +4003,70 @@ function parseCodexErrorBody(text, statusCode) {
4199
4003
  return bounded ? { message: bounded } : {};
4200
4004
  }
4201
4005
  }
4202
- var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
4203
- var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
4204
- function codexUsageLimitError(errorObj, statusCode, requestId) {
4006
+ function chatGPTPlanError(errorObj, statusCode, requestId, message, model) {
4205
4007
  const code = String(errorObj?.code ?? errorObj?.type ?? "");
4206
- const rateLimits = errorObj?.rate_limits;
4207
- const resetsAtRaw = (typeof errorObj?.resets_at === "number" ? errorObj.resets_at : void 0) ?? rateLimits?.primary?.resets_at ?? rateLimits?.secondary?.resets_at;
4208
- const resetsInSeconds = typeof errorObj?.resets_in_seconds === "number" ? errorObj.resets_in_seconds : void 0;
4209
- const resetsAt = typeof resetsAtRaw === "number" && resetsAtRaw > 0 ? resetsAtRaw : resetsInSeconds != null && resetsInSeconds > 0 ? Math.floor(Date.now() / 1e3) + resetsInSeconds : void 0;
4210
- const isHardUsage = CODEX_USAGE_LIMIT_CODE.test(code);
4211
- const isRateOr429 = CODEX_RATE_LIMIT_CODE.test(code) || statusCode === 429;
4212
- if (!isHardUsage && !(isRateOr429 && resetsAt != null)) return null;
4213
- return new ProviderError("openai", "ChatGPT usage limit reached", {
4214
- cause: errorObj,
4215
- statusCode: statusCode ?? 429,
4216
- ...requestId ? { requestId } : {},
4217
- ...resetsAt ? { resetsAt } : {}
4218
- });
4008
+ const base = { ...requestId ? { requestId } : {}, cause: errorObj };
4009
+ switch (code) {
4010
+ // The plan's usage — or the limit the user set for GG Coder — is spent, or
4011
+ // can't be checked right now. Both stop plan requests; the canonical
4012
+ // "usage limit reached" message lets the session fall back to an API key.
4013
+ case "subscription_sharing_usage_limit_exceeded":
4014
+ case "subscription_sharing_usage_unavailable":
4015
+ return new ProviderError("openai", "ChatGPT usage limit reached", {
4016
+ ...base,
4017
+ statusCode: statusCode ?? (code.endsWith("unavailable") ? 503 : 429),
4018
+ hint: code === "subscription_sharing_usage_unavailable" ? "OpenAI couldn't check your ChatGPT plan usage just now. Try again shortly, or add an OpenAI API key to keep going." : `Check or raise what GG Coder may use at ${CHATGPT_USAGE_URL}, or add an OpenAI API key to keep going.`
4019
+ });
4020
+ case "subscription_sharing_user_not_eligible":
4021
+ return new ProviderError(
4022
+ "openai",
4023
+ "ChatGPT plan use isn't available for this account or workspace.",
4024
+ {
4025
+ ...base,
4026
+ statusCode: statusCode ?? 403,
4027
+ hint: "Add an OpenAI API key, or ask your workspace admin to allow ChatGPT plan use in other apps."
4028
+ }
4029
+ );
4030
+ // The account's plan doesn't include this model (live: 400 model_not_found,
4031
+ // "…not supported when using Codex with a ChatGPT account").
4032
+ case "model_not_found":
4033
+ return new ProviderError(
4034
+ "openai",
4035
+ `${model ?? "This model"} isn't included in your ChatGPT plan.`,
4036
+ {
4037
+ ...base,
4038
+ statusCode: statusCode ?? 400,
4039
+ hint: "Pick a model your plan includes (the model picker marks the others), or use an OpenAI API key for this one."
4040
+ }
4041
+ );
4042
+ case "subscription_sharing_unsupported_capability": {
4043
+ const param = typeof errorObj?.param === "string" ? errorObj.param : void 0;
4044
+ if (param?.includes("service_tier")) {
4045
+ return new ProviderError("openai", "Fast mode isn't included in your ChatGPT plan.", {
4046
+ ...base,
4047
+ statusCode: statusCode ?? 400,
4048
+ hint: "Switch to the standard model, or use an OpenAI API key for Fast mode."
4049
+ });
4050
+ }
4051
+ return new ProviderError(
4052
+ "openai",
4053
+ message ?? `The ChatGPT plan route doesn't support ${param ?? "part of this request"}.`,
4054
+ {
4055
+ ...base,
4056
+ statusCode: statusCode ?? 400,
4057
+ hint: `${param ? `\`${param}\` isn't` : "Part of this request isn't"} available on the ChatGPT plan. Choose another model, or add an OpenAI API key.`
4058
+ }
4059
+ );
4060
+ }
4061
+ case "subscription_sharing_invalid_user":
4062
+ return new ProviderError("openai", message ?? "ChatGPT couldn't validate your sign-in.", {
4063
+ ...base,
4064
+ statusCode: statusCode ?? 401,
4065
+ hint: "Continue with ChatGPT again to reconnect."
4066
+ });
4067
+ default:
4068
+ return null;
4069
+ }
4219
4070
  }
4220
4071
 
4221
4072
  // src/providers/xai-responses.ts
@@ -4349,92 +4200,17 @@ async function* synthesizeResponseEvents(response) {
4349
4200
  }
4350
4201
 
4351
4202
  // src/providers/gemini.ts
4352
- var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
4353
- var CODE_ASSIST_API_VERSION = "v1internal";
4354
- var GEMINI_CLI_USER_AGENT = "google-gemini-cli";
4355
- var GEMINI_CLI_API_CLIENT = "gemini-cli/0.0.0";
4356
- var CODE_ASSIST_NON_STREAMING_RETRIES = 3;
4357
- var CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS = 1e3;
4203
+ var DEFAULT_BASE_URL2 = "https://generativelanguage.googleapis.com/v1beta";
4204
+ var NON_STREAMING_RETRIES = 3;
4205
+ var NON_STREAMING_RETRY_DELAY_MS = 1e3;
4358
4206
  var SYNTHETIC_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
4359
- var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
4360
- "gemini-3-pro-preview",
4361
- "gemini-3.1-pro-preview",
4362
- "gemini-3.1-pro-preview-customtools",
4363
- "gemini-3-flash-preview",
4364
- "gemini-3.5-flash",
4365
- "gemini-3-flash",
4366
- "gemini-3.1-flash-lite",
4367
- "gemini-3.7-flash",
4368
- "gemini-3.8-flash",
4369
- "gemini-3.5-flash-lite",
4370
- "gemini-2.5-pro",
4371
- "gemini-2.5-flash",
4372
- "gemma-4-31b-it",
4373
- "gemma-4-26b-a4b-it"
4374
- ]);
4375
- function isGeminiApiKeyRoute(options) {
4376
- return options.baseUrl?.includes("generativelanguage.googleapis.com") === true;
4377
- }
4378
- function getGoogleProject(options) {
4379
- const env = getEnvironment();
4380
- return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
4381
- }
4382
- function getCodeAssistEndpoint(method) {
4383
- const env = getEnvironment();
4384
- const endpoint = env?.CODE_ASSIST_ENDPOINT ?? DEFAULT_CODE_ASSIST_BASE_URL;
4385
- const version = env?.CODE_ASSIST_API_VERSION || CODE_ASSIST_API_VERSION;
4386
- return new URL(`${endpoint}/${version}:${method}`);
4387
- }
4388
- function formatUnsupportedModelMessage(model) {
4389
- return `Gemini OAuth is configured to use the Gemini Code Assist subscription endpoint only. That endpoint does not currently expose model "${model}".`;
4390
- }
4391
- var ACCOUNT_GATED_MODELS = /* @__PURE__ */ new Set([
4392
- "gemini-3-flash",
4393
- "gemini-3.5-flash",
4394
- "gemini-3.1-pro-preview",
4395
- "gemini-3.1-pro-preview-customtools",
4396
- "gemini-3.7-flash",
4397
- "gemini-3.8-flash",
4398
- "gemini-3.5-flash-lite"
4399
- ]);
4400
- function accountGatedMessage(model) {
4401
- if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
4402
- return `${model} is not available through Code Assist for this account. Public Gemini API availability does not guarantee Code Assist OAuth access.`;
4403
- }
4404
- return `Your Google account isn't entitled to "${model}" over Gemini Code Assist OAuth, so the API reports it as not found. This is an account-access limit, not a ggcoder bug.`;
4405
- }
4406
- function accountGatedHint(model) {
4407
- if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
4408
- return "Use /model to select Gemini 3.1 Flash Lite, or retry once Google enables this model through Code Assist for your account.";
4409
- }
4410
- return `Newer Gemini models (3.5 Flash, 3.1 Pro Preview) are available only to Code Assist Standard/Enterprise accounts with preview/GA access enabled by a cloud admin. Try Gemini 3.1 Flash Lite with /model, or sign in with a Code Assist Standard/Enterprise account that has preview access.`;
4411
- }
4412
- function isCodeAssistLicenseRequired(status, body) {
4413
- if (status !== 403) return false;
4414
- try {
4415
- const parsed = JSON.parse(body);
4416
- const details = typeof parsed === "object" && parsed !== null && "error" in parsed ? parsed.error?.details : void 0;
4417
- return Array.isArray(details) && details.some(
4418
- (detail) => typeof detail === "object" && detail !== null && detail.reason === "SUBSCRIPTION_REQUIRED"
4419
- );
4420
- } catch {
4421
- return false;
4422
- }
4423
- }
4424
4207
  function isInvalidGeminiApiKey(status, body) {
4425
4208
  if (status !== 400 && status !== 401 && status !== 403) return false;
4426
4209
  return /"reason"\s*:\s*"API_KEY_INVALID"/.test(body) || /API key not valid/i.test(body);
4427
4210
  }
4428
4211
  var INVALID_API_KEY_MESSAGE = "Google rejected the Gemini API key: it is not valid.";
4429
4212
  var INVALID_API_KEY_HINT = "Create a key at aistudio.google.com/apikey and add it again under Gemini in Providers (or `ggcoder login`).";
4430
- var LICENSE_REQUIRED_MESSAGE = "This Google account has no Gemini Code Assist licence. Google stopped serving Gemini through Code Assist to personal accounts (Google AI Pro/Ultra and the free individual tier) on June 18, 2026, so every Gemini model is refused, not just this one.";
4431
- var LICENSE_REQUIRED_HINT = "Sign in to Gemini with a Google account that has a Gemini Code Assist Standard or Enterprise licence, or pick a model from another provider with /model.";
4432
- function formatErrorMessage(status, body, model) {
4433
- if (status === 404 && !CODE_ASSIST_SUPPORTED_MODELS.has(model)) {
4434
- return `Gemini API error (404): ${body}
4435
-
4436
- ${formatUnsupportedModelMessage(model)}`;
4437
- }
4213
+ function formatErrorMessage(status, body) {
4438
4214
  return `Gemini API error (${status}): ${body}`;
4439
4215
  }
4440
4216
  function parseRetryDelaySeconds(body) {
@@ -4640,27 +4416,15 @@ function buildGenerateRequest(options) {
4640
4416
  ...systemInstruction ? { systemInstruction } : {},
4641
4417
  ...tools ? { tools } : {},
4642
4418
  ...toolConfig ? { toolConfig } : {},
4643
- ...Object.keys(generationConfig).length > 0 ? { generationConfig } : {},
4644
- ...options.promptCacheKey ? { session_id: options.promptCacheKey } : {}
4645
- };
4646
- }
4647
- function buildCodeAssistRequest(options, request, projectId) {
4648
- return {
4649
- model: options.model,
4650
- ...projectId ? { project: projectId } : {},
4651
- user_prompt_id: crypto.randomUUID(),
4652
- request
4419
+ ...Object.keys(generationConfig).length > 0 ? { generationConfig } : {}
4653
4420
  };
4654
4421
  }
4655
4422
  var API_KEY_MODEL_IDS = {
4656
4423
  "gemini-3-flash": "gemini-3.5-flash"
4657
4424
  };
4658
- function buildApiKeyRequestPlan(options, method) {
4659
- const { session_id: _sessionId, ...request } = mergeProviderOptions(
4660
- buildGenerateRequest(options),
4661
- options.providerOptions
4662
- );
4663
- const base = (options.baseUrl ?? "").replace(/\/+$/, "");
4425
+ function buildRequestPlan(options, method) {
4426
+ const request = mergeProviderOptions(buildGenerateRequest(options), options.providerOptions);
4427
+ const base = (options.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
4664
4428
  return {
4665
4429
  url: new URL(
4666
4430
  `${base}/models/${encodeURIComponent(API_KEY_MODEL_IDS[options.model] ?? options.model)}:${method}`
@@ -4669,27 +4433,7 @@ function buildApiKeyRequestPlan(options, method) {
4669
4433
  "Content-Type": "application/json",
4670
4434
  "x-goog-api-key": options.apiKey ?? ""
4671
4435
  },
4672
- body: request,
4673
- route: "api-key"
4674
- };
4675
- }
4676
- function buildRequestPlan(options, method) {
4677
- if (isGeminiApiKeyRoute(options)) return buildApiKeyRequestPlan(options, method);
4678
- if (!CODE_ASSIST_SUPPORTED_MODELS.has(options.model)) {
4679
- throw new ProviderError("gemini", formatUnsupportedModelMessage(options.model));
4680
- }
4681
- const projectId = getGoogleProject(options);
4682
- const request = mergeProviderOptions(buildGenerateRequest(options), options.providerOptions);
4683
- return {
4684
- url: getCodeAssistEndpoint(method),
4685
- headers: {
4686
- Authorization: `Bearer ${options.apiKey}`,
4687
- "Content-Type": "application/json",
4688
- "User-Agent": GEMINI_CLI_USER_AGENT,
4689
- "X-Goog-Api-Client": GEMINI_CLI_API_CLIENT
4690
- },
4691
- body: buildCodeAssistRequest(options, request, projectId),
4692
- route: "code-assist"
4436
+ body: request
4693
4437
  };
4694
4438
  }
4695
4439
  function normalizeGeminiStopReason(reason) {
@@ -4716,10 +4460,10 @@ async function* streamSse(response) {
4716
4460
  }
4717
4461
  }
4718
4462
  function candidatesFromResponse(response) {
4719
- return response.response?.candidates ?? response.candidates;
4463
+ return response.candidates;
4720
4464
  }
4721
4465
  function usageFromResponse(response) {
4722
- return response.response?.usageMetadata ?? response.usageMetadata;
4466
+ return response.usageMetadata;
4723
4467
  }
4724
4468
  function partsFromResponse(response) {
4725
4469
  return candidatesFromResponse(response)?.[0]?.content?.parts ?? [];
@@ -4741,7 +4485,7 @@ function readFunctionCallPart(part) {
4741
4485
  function makeToolCallId(index, providerId) {
4742
4486
  return providerId ?? `gemini_call_${index}_${crypto.randomUUID().replace(/-/g, "")}`;
4743
4487
  }
4744
- function shouldRetryCodeAssistStatus(status) {
4488
+ function shouldRetryStatus(status) {
4745
4489
  return status === 429 || status === 499 || status >= 500 && status <= 599;
4746
4490
  }
4747
4491
  function isAbortError(err) {
@@ -4764,7 +4508,7 @@ async function sleep(ms, signal) {
4764
4508
  if (signal?.aborted) onAbort();
4765
4509
  });
4766
4510
  }
4767
- async function fetchCodeAssist(plan, options) {
4511
+ async function fetchGemini(plan, options) {
4768
4512
  try {
4769
4513
  const response = await providerFetch(plan.url, {
4770
4514
  method: "POST",
@@ -4775,11 +4519,8 @@ async function fetchCodeAssist(plan, options) {
4775
4519
  if (!response.ok) {
4776
4520
  const text = await response.text().catch(() => "");
4777
4521
  const quota = parseGeminiQuota(response.status, text);
4778
- const codeAssist = plan.route === "code-assist";
4779
- const accountGated = codeAssist && response.status === 404 && ACCOUNT_GATED_MODELS.has(options.model);
4780
- const licenseRequired = codeAssist && isCodeAssistLicenseRequired(response.status, text);
4781
- const invalidKey = !codeAssist && isInvalidGeminiApiKey(response.status, text);
4782
- let message = invalidKey ? INVALID_API_KEY_MESSAGE : licenseRequired ? LICENSE_REQUIRED_MESSAGE : accountGated ? accountGatedMessage(options.model) : formatErrorMessage(response.status, text, options.model);
4522
+ const invalidKey = isInvalidGeminiApiKey(response.status, text);
4523
+ let message = invalidKey ? INVALID_API_KEY_MESSAGE : formatErrorMessage(response.status, text);
4783
4524
  let resetsAt;
4784
4525
  if (quota?.exhausted) {
4785
4526
  message = `Gemini quota exhausted \u2014 usage limit reached. ${message}`;
@@ -4789,7 +4530,7 @@ async function fetchCodeAssist(plan, options) {
4789
4530
  throw new ProviderError("gemini", message, {
4790
4531
  statusCode: response.status,
4791
4532
  ...resetsAt !== void 0 ? { resetsAt } : {},
4792
- ...invalidKey ? { hint: INVALID_API_KEY_HINT, source: "auth" } : licenseRequired ? { hint: LICENSE_REQUIRED_HINT, source: "auth" } : accountGated ? { hint: accountGatedHint(options.model) } : {}
4533
+ ...invalidKey ? { hint: INVALID_API_KEY_HINT, source: "auth" } : {}
4793
4534
  });
4794
4535
  }
4795
4536
  return response;
@@ -4797,26 +4538,26 @@ async function fetchCodeAssist(plan, options) {
4797
4538
  throw toError3(err);
4798
4539
  }
4799
4540
  }
4800
- async function fetchCodeAssistWithRetry(plan, options) {
4541
+ async function fetchGeminiWithRetry(plan, options) {
4801
4542
  let lastError;
4802
- for (let attempt = 0; attempt <= CODE_ASSIST_NON_STREAMING_RETRIES; attempt++) {
4543
+ for (let attempt = 0; attempt <= NON_STREAMING_RETRIES; attempt++) {
4803
4544
  try {
4804
- return await fetchCodeAssist(plan, options);
4545
+ return await fetchGemini(plan, options);
4805
4546
  } catch (err) {
4806
4547
  const error = toError3(err);
4807
4548
  const statusCode = error instanceof ProviderError ? error.statusCode : void 0;
4808
- if (options.signal?.aborted || isAbortError(error) || attempt === CODE_ASSIST_NON_STREAMING_RETRIES || statusCode != null && !shouldRetryCodeAssistStatus(statusCode)) {
4549
+ if (options.signal?.aborted || isAbortError(error) || attempt === NON_STREAMING_RETRIES || statusCode != null && !shouldRetryStatus(statusCode)) {
4809
4550
  throw error;
4810
4551
  }
4811
4552
  lastError = error;
4812
4553
  }
4813
4554
  try {
4814
- await sleep(CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS, options.signal);
4555
+ await sleep(NON_STREAMING_RETRY_DELAY_MS, options.signal);
4815
4556
  } catch (err) {
4816
4557
  throw toError3(err);
4817
4558
  }
4818
4559
  }
4819
- throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
4560
+ throw lastError ?? new ProviderError("gemini", "Gemini request failed.");
4820
4561
  }
4821
4562
  function streamGemini(options) {
4822
4563
  return new StreamResult(runStream5(options), options.signal);
@@ -4826,7 +4567,7 @@ async function* runStream5(options) {
4826
4567
  const method = useStreaming ? "streamGenerateContent" : "generateContent";
4827
4568
  const plan = buildRequestPlan(options, method);
4828
4569
  if (useStreaming) plan.url.searchParams.set("alt", "sse");
4829
- const response = useStreaming ? await fetchCodeAssist(plan, options) : await fetchCodeAssistWithRetry(plan, options);
4570
+ const response = useStreaming ? await fetchGemini(plan, options) : await fetchGeminiWithRetry(plan, options);
4830
4571
  const contentParts = [];
4831
4572
  const pendingToolCalls = [];
4832
4573
  let textAccum = "";
@@ -5137,7 +4878,7 @@ async function postJson(request) {
5137
4878
  }
5138
4879
  function httpError(provider, response, text, headerRequestId, clean) {
5139
4880
  const status = response.status;
5140
- const parsed = parseErrorBody(text);
4881
+ const parsed = parseErrorBody2(text);
5141
4882
  const rawMessage = parsed.message ?? (parsed.isJson ? "" : text.trim());
5142
4883
  const message = !rawMessage ? emptyProviderErrorMessage(status) : isRawHtmlErrorEcho(rawMessage) ? providerHtmlErrorMessage(status) : clean(rawMessage);
5143
4884
  const requestId = headerRequestId ?? parsed.requestId;
@@ -5163,7 +4904,7 @@ function httpError(provider, response, text, headerRequestId, clean) {
5163
4904
  }
5164
4905
  return new ProviderError(provider, message, base);
5165
4906
  }
5166
- function parseErrorBody(text) {
4907
+ function parseErrorBody2(text) {
5167
4908
  let parsed;
5168
4909
  try {
5169
4910
  parsed = JSON.parse(text);
@@ -5199,8 +4940,8 @@ function bound(text) {
5199
4940
  const trimmed = text.trim();
5200
4941
  return trimmed.length > MAX_ERROR_MESSAGE_CHARS ? `${trimmed.slice(0, MAX_ERROR_MESSAGE_CHARS)}\u2026` : trimmed;
5201
4942
  }
5202
- function joinUrl(baseUrl, path2) {
5203
- return `${baseUrl.replace(/\/+$/, "")}/${path2.replace(/^\/+/, "")}`;
4943
+ function joinUrl(baseUrl, path) {
4944
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
5204
4945
  }
5205
4946
 
5206
4947
  // src/providers/gemini-embeddings.ts
@@ -5595,10 +5336,7 @@ var OPENAI_EMBED_BATCH_SIZE = 2048;
5595
5336
  var OPENROUTER_EMBED_BATCH_SIZE = 64;
5596
5337
  var LOCAL_EMBED_BATCH_SIZE = 64;
5597
5338
  var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
5598
- var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
5599
- var KIMI_CODE_USER_AGENT = `kimi-code-cli/${process.env.KIMI_CODE_VERSION ?? "1.0.11"}`;
5600
- var GROK_CLI_PROXY_HOST = "cli-chat-proxy.grok.com";
5601
- var GROK_CLI_VERSION = process.env.GROK_CLI_VERSION ?? "1.0.67";
5339
+ var GLM_BASE_URL = "https://api.z.ai/api/paas/v4";
5602
5340
  providerRegistry.register("anthropic", {
5603
5341
  stream: (options) => streamAnthropic(options)
5604
5342
  });
@@ -5611,12 +5349,12 @@ providerRegistry.register("xiaomi", {
5611
5349
  });
5612
5350
  providerRegistry.register("openai", {
5613
5351
  stream: (options) => {
5614
- if (options.accountId) {
5615
- return streamOpenAICodex(options);
5352
+ if (options.accountId || isOpenAIHost(options.baseUrl)) {
5353
+ return streamOpenAIResponses(options);
5616
5354
  }
5617
5355
  return streamOpenAI(options);
5618
5356
  },
5619
- // OpenAI API-key auth only; ChatGPT-subscription (Codex) tokens have no embeddings API.
5357
+ // OpenAI API-key auth only; the ChatGPT plan route has no embeddings API.
5620
5358
  // OpenAI has no task/input-type parameter, so `inputType` is ignored.
5621
5359
  embed: (request) => embedOpenAICompatible(request, {
5622
5360
  provider: "openai",
@@ -5626,22 +5364,24 @@ providerRegistry.register("openai", {
5626
5364
  });
5627
5365
  providerRegistry.register("gemini", {
5628
5366
  stream: (options) => streamGemini(options),
5629
- // Native Gemini API with an AI Studio key — not the Code Assist OAuth endpoint above.
5367
+ // Native Gemini API with an AI Studio key.
5630
5368
  embed: (request) => embedGemini(request),
5631
5369
  embedBatchSize: GEMINI_EMBED_BATCH_SIZE
5632
5370
  });
5633
5371
  providerRegistry.register("glm", {
5634
5372
  stream: (options) => streamOpenAI({
5635
5373
  ...options,
5636
- baseUrl: options.baseUrl ?? GLM_CODING_BASE_URL
5374
+ baseUrl: options.baseUrl ?? GLM_BASE_URL
5637
5375
  })
5638
5376
  });
5639
5377
  providerRegistry.register("moonshot", {
5640
- stream: (options) => {
5641
- const baseUrl = options.baseUrl ?? "https://api.moonshot.ai/v1";
5642
- const defaultHeaders = baseUrl.includes("api.kimi.com") ? { "User-Agent": KIMI_CODE_USER_AGENT, ...options.defaultHeaders } : options.defaultHeaders;
5643
- return streamOpenAI({ ...options, baseUrl, defaultHeaders });
5644
- }
5378
+ // A Moonshot key goes to the public API; a Kimi Code plan key carries its
5379
+ // own baseUrl (api.kimi.com / api.kimi.ai). Both get GG Coder's own identity.
5380
+ stream: (options) => streamOpenAI({
5381
+ ...options,
5382
+ baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1",
5383
+ defaultHeaders: { "User-Agent": GG_CODER_USER_AGENT, ...options.defaultHeaders }
5384
+ })
5645
5385
  });
5646
5386
  providerRegistry.register("deepseek", {
5647
5387
  stream: (options) => streamOpenAI({
@@ -5690,27 +5430,10 @@ providerRegistry.register("sakana", {
5690
5430
  })
5691
5431
  });
5692
5432
  providerRegistry.register("xai", {
5693
- // Both Grok routes ride xAI's Responses API (see providers/xai-responses.ts):
5694
- // Chat Completions is deprecated for Grok reasoning models and can't carry
5695
- // the encrypted reasoning that keeps long tool loops coherent.
5696
- //
5697
- // Subscription OAuth (SuperGrok / X Premium) routes to the Grok CLI chat proxy
5698
- // instead, which serves the same `/v1/responses` surface but gates on Grok-CLI
5699
- // client identity. Inject those headers centrally here — exactly as the Kimi
5700
- // endpoint above — so EVERY stream (agent loop, compaction, title-gen,
5701
- // sub-agents) is accepted rather than depending on each call site to thread
5702
- // headers. Caller-provided headers still win on collision.
5703
- stream: (options) => {
5704
- const baseUrl = options.baseUrl ?? "https://api.x.ai/v1";
5705
- const defaultHeaders = baseUrl.includes(GROK_CLI_PROXY_HOST) ? {
5706
- "X-XAI-Token-Auth": "xai-grok-cli",
5707
- "x-grok-client-version": GROK_CLI_VERSION,
5708
- "x-grok-client-identifier": "ggcoder",
5709
- "x-grok-model-override": options.model,
5710
- ...options.defaultHeaders
5711
- } : options.defaultHeaders;
5712
- return streamXaiResponses({ ...options, baseUrl, defaultHeaders });
5713
- }
5433
+ // xAI API key on xAI's Responses API (see providers/xai-responses.ts): Chat
5434
+ // Completions is deprecated for Grok reasoning models and can't carry the
5435
+ // encrypted reasoning that keeps long tool loops coherent.
5436
+ stream: (options) => streamXaiResponses({ ...options, baseUrl: options.baseUrl ?? "https://api.x.ai/v1" })
5714
5437
  });
5715
5438
  providerRegistry.register("minimax", {
5716
5439
  stream: (options) => streamAnthropic({
@@ -5761,6 +5484,14 @@ providerRegistry.register("local", {
5761
5484
  }
5762
5485
  )
5763
5486
  });
5487
+ function isOpenAIHost(baseUrl) {
5488
+ if (!baseUrl) return true;
5489
+ try {
5490
+ return new URL(baseUrl).hostname === "api.openai.com";
5491
+ } catch {
5492
+ return false;
5493
+ }
5494
+ }
5764
5495
  function requireLocalBaseUrl(baseUrl) {
5765
5496
  if (!baseUrl) {
5766
5497
  throw new GGAIError(
@@ -5951,16 +5682,16 @@ function l2Normalize(vector) {
5951
5682
 
5952
5683
  // src/utils/preconnect.ts
5953
5684
  var PRECONNECT_INTERVAL_MS = 3e4;
5954
- function defaultBaseUrl(provider, accountId) {
5685
+ function defaultBaseUrl(provider) {
5955
5686
  switch (provider) {
5956
5687
  case "anthropic":
5957
5688
  return "https://api.anthropic.com";
5958
5689
  case "openai":
5959
- return accountId ? "https://chatgpt.com/backend-api" : "https://api.openai.com/v1";
5690
+ return "https://api.openai.com/v1";
5960
5691
  case "gemini":
5961
- return "https://cloudcode-pa.googleapis.com";
5692
+ return "https://generativelanguage.googleapis.com/v1beta";
5962
5693
  case "glm":
5963
- return "https://api.z.ai/api/coding/paas/v4";
5694
+ return "https://api.z.ai/api/paas/v4";
5964
5695
  case "moonshot":
5965
5696
  return "https://api.moonshot.ai/v1";
5966
5697
  case "deepseek":
@@ -5993,7 +5724,7 @@ function isLocalHost(hostname) {
5993
5724
  return a === 127 || a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31;
5994
5725
  }
5995
5726
  function preconnectTarget(options) {
5996
- const raw = options.baseUrl ?? defaultBaseUrl(options.provider, options.accountId);
5727
+ const raw = options.baseUrl ?? defaultBaseUrl(options.provider);
5997
5728
  if (!raw || options.provider === "local" || options.provider === "palsu") return void 0;
5998
5729
  let url;
5999
5730
  try {
@@ -6507,11 +6238,10 @@ function palsuRerankScore(query, document) {
6507
6238
  return hits / queryTokens.size;
6508
6239
  }
6509
6240
  export {
6510
- CLAUDE_CODE_FALLBACK_VERSION,
6511
- CODEX_CLIENT_VERSION,
6512
6241
  EventStream,
6513
6242
  FAST_MODE_SUFFIX,
6514
6243
  GGAIError,
6244
+ GG_CODER_USER_AGENT,
6515
6245
  LOCAL_NO_API_KEY,
6516
6246
  PRECONNECT_INTERVAL_MS,
6517
6247
  ProviderError,
@@ -6529,8 +6259,6 @@ export {
6529
6259
  formatChatError,
6530
6260
  formatError,
6531
6261
  formatErrorForDisplay,
6532
- getClaudeCliUserAgent,
6533
- getClaudeCodeVersion,
6534
6262
  hasLoneSurrogate,
6535
6263
  isHardBillingMessage,
6536
6264
  isUsageLimitError,
@@ -6545,7 +6273,6 @@ export {
6545
6273
  palsuThinking,
6546
6274
  palsuToolCall,
6547
6275
  parseFastModeRejection,
6548
- pendingClaudeCodeVersionRefresh,
6549
6276
  preconnectOrigin,
6550
6277
  preconnectProvider,
6551
6278
  prewarmAnthropicCache,