@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.cjs CHANGED
@@ -30,11 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- CLAUDE_CODE_FALLBACK_VERSION: () => CLAUDE_CODE_FALLBACK_VERSION,
34
- CODEX_CLIENT_VERSION: () => CODEX_CLIENT_VERSION,
35
33
  EventStream: () => EventStream,
36
34
  FAST_MODE_SUFFIX: () => FAST_MODE_SUFFIX,
37
35
  GGAIError: () => GGAIError,
36
+ GG_CODER_USER_AGENT: () => GG_CODER_USER_AGENT,
38
37
  LOCAL_NO_API_KEY: () => LOCAL_NO_API_KEY,
39
38
  PRECONNECT_INTERVAL_MS: () => PRECONNECT_INTERVAL_MS,
40
39
  ProviderError: () => ProviderError,
@@ -52,8 +51,6 @@ __export(index_exports, {
52
51
  formatChatError: () => formatChatError,
53
52
  formatError: () => formatError,
54
53
  formatErrorForDisplay: () => formatErrorForDisplay,
55
- getClaudeCliUserAgent: () => getClaudeCliUserAgent,
56
- getClaudeCodeVersion: () => getClaudeCodeVersion,
57
54
  hasLoneSurrogate: () => hasLoneSurrogate,
58
55
  isHardBillingMessage: () => isHardBillingMessage,
59
56
  isUsageLimitError: () => isUsageLimitError,
@@ -68,7 +65,6 @@ __export(index_exports, {
68
65
  palsuThinking: () => palsuThinking,
69
66
  palsuToolCall: () => palsuToolCall,
70
67
  parseFastModeRejection: () => parseFastModeRejection,
71
- pendingClaudeCodeVersionRefresh: () => pendingClaudeCodeVersionRefresh,
72
68
  preconnectOrigin: () => preconnectOrigin,
73
69
  preconnectProvider: () => preconnectProvider,
74
70
  prewarmAnthropicCache: () => prewarmAnthropicCache,
@@ -96,7 +92,7 @@ module.exports = __toCommonJS(index_exports);
96
92
  var FAST_MODE_SUFFIX = "-fast";
97
93
  var FAST_MODE_BASE_MODELS = {
98
94
  anthropic: ["claude-opus-5-5"],
99
- openai: ["gpt-6-astra", "gpt-6.1-sol", "gpt-6-luna"]
95
+ openai: ["gpt-6-astra", "gpt-6.1-sol", "gpt-6.1-terra", "gpt-6-luna"]
100
96
  };
101
97
  function resolveFastModeModel(provider, model) {
102
98
  if (!model.endsWith(FAST_MODE_SUFFIX)) return { model, fast: false };
@@ -337,6 +333,18 @@ function formatError(err) {
337
333
  }
338
334
  const miniMax = err.provider === "minimax" ? formatMiniMaxError(err, cleanMessage) : void 0;
339
335
  if (miniMax) return miniMax;
336
+ if (isUsageLimitError(err) && err.provider === "openai" && /chatgpt/i.test(err.message)) {
337
+ return {
338
+ headline: "ChatGPT plan usage limit reached.",
339
+ source: "provider",
340
+ 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.",
341
+ provider: err.provider,
342
+ statusCode: err.statusCode,
343
+ ...err.requestId ? { requestId: err.requestId } : {},
344
+ ...err.resetsAt ? { resetsAt: err.resetsAt } : {},
345
+ guidance: err.hint ?? "Check or raise it at chatgpt.com/settings/usage, or add an OpenAI API key to keep going."
346
+ };
347
+ }
340
348
  if (isUsageLimitError(err)) {
341
349
  const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
342
350
  return {
@@ -702,145 +710,6 @@ var StreamResult = class _StreamResult {
702
710
  }
703
711
  };
704
712
 
705
- // src/claude-code-version.ts
706
- var import_promises = __toESM(require("fs/promises"), 1);
707
- var import_node_os = __toESM(require("os"), 1);
708
- var import_node_path = __toESM(require("path"), 1);
709
-
710
- // src/utils/diag.ts
711
- var _diagFn = null;
712
- function setProviderDiagnostic(fn) {
713
- _diagFn = fn;
714
- }
715
- function providerDiag(phase, data) {
716
- _diagFn?.(phase, data);
717
- }
718
-
719
- // src/claude-code-version.ts
720
- var NPM_LATEST_URL = "https://registry.npmjs.org/@anthropic-ai/claude-code/latest";
721
- var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
722
- var FETCH_TIMEOUT_MS = 3e3;
723
- var CLAUDE_CODE_FALLBACK_VERSION = "2.1.316";
724
- function isOlder(a, b) {
725
- const pa = a.split(".").map((n) => Number.parseInt(n, 10) || 0);
726
- const pb = b.split(".").map((n) => Number.parseInt(n, 10) || 0);
727
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
728
- const x = pa[i] ?? 0;
729
- const y = pb[i] ?? 0;
730
- if (x !== y) return x < y;
731
- }
732
- return false;
733
- }
734
- function atLeastFallback(version) {
735
- return isOlder(version, CLAUDE_CODE_FALLBACK_VERSION) ? CLAUDE_CODE_FALLBACK_VERSION : version;
736
- }
737
- var memoryCache = null;
738
- var inflight = null;
739
- function agentDir() {
740
- return import_node_path.default.join(import_node_os.default.homedir(), ".gg");
741
- }
742
- function cachePath() {
743
- return import_node_path.default.join(agentDir(), "claude-code-version.json");
744
- }
745
- async function readDiskCache() {
746
- try {
747
- const raw = await import_promises.default.readFile(cachePath(), "utf-8");
748
- const parsed = JSON.parse(raw);
749
- if (typeof parsed.version === "string" && typeof parsed.fetchedAt === "number") {
750
- return parsed;
751
- }
752
- return null;
753
- } catch {
754
- return null;
755
- }
756
- }
757
- async function writeDiskCache(data) {
758
- try {
759
- await import_promises.default.mkdir(agentDir(), { recursive: true, mode: 448 });
760
- await import_promises.default.writeFile(cachePath(), JSON.stringify(data), { mode: 384 });
761
- } catch (err) {
762
- providerDiag("claude_code_version_cache_write_failed", {
763
- error: err instanceof Error ? err.message : String(err)
764
- });
765
- }
766
- }
767
- async function fetchLatest() {
768
- const controller = new AbortController();
769
- const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
770
- try {
771
- const response = await fetch(NPM_LATEST_URL, { signal: controller.signal });
772
- if (!response.ok) return null;
773
- const data = await response.json();
774
- if (typeof data.version === "string" && /^\d/.test(data.version)) {
775
- return data.version;
776
- }
777
- return null;
778
- } catch {
779
- return null;
780
- } finally {
781
- clearTimeout(timer);
782
- }
783
- }
784
- var refreshing = null;
785
- function refreshInBackground() {
786
- refreshing ??= (async () => {
787
- try {
788
- const fetched = await fetchLatest();
789
- if (!fetched) return;
790
- await writeDiskCache({ version: fetched, fetchedAt: Date.now() });
791
- memoryCache = { version: fetched, expiresAt: Date.now() + CACHE_TTL_MS };
792
- } finally {
793
- refreshing = null;
794
- }
795
- })();
796
- return refreshing;
797
- }
798
- function pendingClaudeCodeVersionRefresh() {
799
- return refreshing;
800
- }
801
- async function getClaudeCodeVersion() {
802
- if (memoryCache && Date.now() < memoryCache.expiresAt) {
803
- return memoryCache.version;
804
- }
805
- if (inflight) return inflight;
806
- inflight = (async () => {
807
- const disk = await readDiskCache();
808
- const diskFresh = disk && Date.now() - disk.fetchedAt < CACHE_TTL_MS;
809
- if (disk && diskFresh) {
810
- const version = atLeastFallback(disk.version);
811
- memoryCache = { version, expiresAt: Date.now() + CACHE_TTL_MS };
812
- return version;
813
- }
814
- if (disk) {
815
- const version = atLeastFallback(disk.version);
816
- memoryCache = { version, expiresAt: Date.now() + 5 * 60 * 1e3 };
817
- void refreshInBackground();
818
- return version;
819
- }
820
- const fetched = await fetchLatest();
821
- if (fetched) {
822
- await writeDiskCache({ version: fetched, fetchedAt: Date.now() });
823
- memoryCache = { version: fetched, expiresAt: Date.now() + CACHE_TTL_MS };
824
- return fetched;
825
- }
826
- const resolved = CLAUDE_CODE_FALLBACK_VERSION;
827
- memoryCache = { version: resolved, expiresAt: Date.now() + 5 * 60 * 1e3 };
828
- providerDiag("claude_code_version_fallback", { version: resolved });
829
- return resolved;
830
- })();
831
- try {
832
- return await inflight;
833
- } finally {
834
- inflight = null;
835
- }
836
- }
837
- function claudeCliUserAgent(version) {
838
- return `claude-cli/${version} (external, cli)`;
839
- }
840
- async function getClaudeCliUserAgent() {
841
- return claudeCliUserAgent(await getClaudeCodeVersion());
842
- }
843
-
844
713
  // src/utils/provider-options.ts
845
714
  var RESERVED_BODY_FIELDS = /* @__PURE__ */ new Set([
846
715
  "contents",
@@ -1376,7 +1245,7 @@ function toolCallNameRuleFor(provider, options) {
1376
1245
  return TOOL_CALL_NAME_RULES.anthropic;
1377
1246
  case "openai":
1378
1247
  if (options?.accountId) return TOOL_CALL_NAME_RULES["openai-responses"];
1379
- return isDefaultOrHost(options?.baseUrl, "api.openai.com") ? TOOL_CALL_NAME_RULES["openai-chat"] : TOOL_CALL_NAME_RULES.generic;
1248
+ return isDefaultOrHost(options?.baseUrl, "api.openai.com") ? TOOL_CALL_NAME_RULES["openai-responses"] : TOOL_CALL_NAME_RULES.generic;
1380
1249
  case "gemini":
1381
1250
  return TOOL_CALL_NAME_RULES.gemini;
1382
1251
  default:
@@ -1574,8 +1443,8 @@ function usesDeferredToolLoading(options) {
1574
1443
  const firstParty = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
1575
1444
  return firstParty && supportsAnthropicDeferredTools(options.model);
1576
1445
  }
1577
- if (options.provider === "openai" && options.accountId) {
1578
- return supportsResponsesToolSearch(options.model);
1446
+ if (options.provider === "openai" && !options.accountId) {
1447
+ return isDefaultOrHost(options.baseUrl, "api.openai.com") && supportsResponsesToolSearch(options.model);
1579
1448
  }
1580
1449
  return false;
1581
1450
  }
@@ -2066,7 +1935,7 @@ function toGlmReasoningEffort(level) {
2066
1935
  }
2067
1936
  function toCodexReasoningEffort(level, model) {
2068
1937
  if (level !== "ultra") return level;
2069
- return model === "gpt-6-astra" || model === "gpt-6.1-sol" ? "xhigh" : "max";
1938
+ return model === "gpt-6-astra" || model === "gpt-6.1-sol" || model === "gpt-6.1-terra" ? "xhigh" : "max";
2070
1939
  }
2071
1940
  var DECLARED_EFFORT_ORDER = ["low", "medium", "high", "xhigh", "max"];
2072
1941
  function effortForRequest(level, model, declared) {
@@ -2150,12 +2019,12 @@ function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {
2150
2019
  };
2151
2020
  const dispatcher = useEnvProxy ? new import_undici.EnvHttpProxyAgent(timeouts) : new import_undici.Agent(timeouts);
2152
2021
  const builtinFetch = globalThis.fetch;
2153
- const providerFetch2 = async (input, init2) => {
2154
- if (globalThis.fetch !== builtinFetch || input instanceof Request || init2?.body instanceof FormData) {
2155
- return globalThis.fetch(input, init2);
2022
+ const providerFetch2 = async (input, init) => {
2023
+ if (globalThis.fetch !== builtinFetch || input instanceof Request || init?.body instanceof FormData) {
2024
+ return globalThis.fetch(input, init);
2156
2025
  }
2157
2026
  const response = await (0, import_undici.fetch)(input, {
2158
- ...init2,
2027
+ ...init,
2159
2028
  dispatcher
2160
2029
  });
2161
2030
  return response;
@@ -2163,8 +2032,8 @@ function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {
2163
2032
  return Object.assign(providerFetch2, { Response: globalThis.Response });
2164
2033
  }
2165
2034
  function withBodyActivity(fetchImpl, onActivity) {
2166
- return async (input, init2) => {
2167
- const response = await fetchImpl(input, init2);
2035
+ return async (input, init) => {
2036
+ const response = await fetchImpl(input, init);
2168
2037
  if (!response.body || !response.headers.get("content-type")?.includes("text/event-stream")) {
2169
2038
  return response;
2170
2039
  }
@@ -2203,8 +2072,7 @@ var anthropicServerFallback = {
2203
2072
  }
2204
2073
  };
2205
2074
  function serverFallbackKey(options) {
2206
- const auth = options.apiKey?.startsWith("sk-ant-oat") ? "oauth" : "key";
2207
- return `${options.baseUrl ?? process.env.ANTHROPIC_BASE_URL ?? ""}|${auth}`;
2075
+ return options.baseUrl ?? process.env.ANTHROPIC_BASE_URL ?? "";
2208
2076
  }
2209
2077
  function isDirectAnthropicApi(baseUrl) {
2210
2078
  const effective = baseUrl ?? process.env.ANTHROPIC_BASE_URL;
@@ -2237,30 +2105,18 @@ function sumUsageIterations(usage) {
2237
2105
  }
2238
2106
  return totals;
2239
2107
  }
2240
- async function withClaudeUserAgent(options) {
2241
- if (options.userAgent || !options.apiKey?.startsWith("sk-ant-oat")) return options;
2242
- return { ...options, userAgent: await getClaudeCliUserAgent() };
2243
- }
2244
2108
  function createClient(options) {
2245
- const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
2246
- const userAgent = isOAuth ? options.userAgent ?? claudeCliUserAgent(CLAUDE_CODE_FALLBACK_VERSION) : "";
2247
- const cacheKey = `${options.apiKey ?? ""}|${options.baseUrl ?? ""}|${userAgent}`;
2109
+ const cacheKey = `${options.apiKey ?? ""}|${options.baseUrl ?? ""}`;
2248
2110
  if (!options.fetch) {
2249
2111
  const cached = anthropicClientCache.get(cacheKey);
2250
2112
  if (cached) return cached;
2251
2113
  }
2252
2114
  const client = new import_sdk.default({
2253
- ...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
2115
+ apiKey: options.apiKey,
2254
2116
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
2255
2117
  // Default transport lifts undici's 300s header/body caps (see provider-fetch.ts).
2256
2118
  fetch: options.fetch ?? providerFetch,
2257
- maxRetries: 0,
2258
- ...isOAuth ? {
2259
- defaultHeaders: {
2260
- "user-agent": userAgent,
2261
- "x-app": "cli"
2262
- }
2263
- } : {}
2119
+ maxRetries: 0
2264
2120
  });
2265
2121
  if (!options.fetch) {
2266
2122
  if (anthropicClientCache.size >= 8) {
@@ -2273,13 +2129,10 @@ function createClient(options) {
2273
2129
  }
2274
2130
  async function prewarmAnthropicCache(options) {
2275
2131
  try {
2276
- const client = createClient(
2277
- await withClaudeUserAgent({
2278
- apiKey: options.apiKey,
2279
- baseUrl: options.baseUrl,
2280
- userAgent: options.userAgent
2281
- })
2282
- );
2132
+ const client = createClient({
2133
+ apiKey: options.apiKey,
2134
+ baseUrl: options.baseUrl
2135
+ });
2283
2136
  const cacheControl = toAnthropicCacheControl(options.cacheRetention ?? "long", options.baseUrl);
2284
2137
  const { system, messages } = toAnthropicMessages(
2285
2138
  [
@@ -2288,21 +2141,13 @@ async function prewarmAnthropicCache(options) {
2288
2141
  ],
2289
2142
  cacheControl
2290
2143
  );
2291
- const isOAuth = options.apiKey.startsWith("sk-ant-oat");
2292
- const fullSystem = isOAuth ? [
2293
- {
2294
- type: "text",
2295
- text: "You are Claude Code, Anthropic's official CLI for Claude."
2296
- },
2297
- ...system ?? []
2298
- ] : system;
2299
2144
  const tools = options.tools?.length ? toAnthropicTools(options.tools, {
2300
2145
  cacheControl,
2301
2146
  // Keep the serialized tool bytes identical to runStream so the
2302
2147
  // prewarmed prompt cache actually hits — both are gated by the flag.
2303
2148
  enableFineGrainedToolStreaming: fineGrainedToolStreamingEnabled()
2304
2149
  }) : void 0;
2305
- if (estimatePrefixTokens(fullSystem, tools) < anthropicMinCacheableTokens(options.model)) {
2150
+ if (estimatePrefixTokens(system, tools) < anthropicMinCacheableTokens(options.model)) {
2306
2151
  return;
2307
2152
  }
2308
2153
  await client.messages.create(
@@ -2310,7 +2155,7 @@ async function prewarmAnthropicCache(options) {
2310
2155
  model: options.model,
2311
2156
  max_tokens: 1,
2312
2157
  messages,
2313
- ...fullSystem ? { system: fullSystem } : {},
2158
+ ...system ? { system } : {},
2314
2159
  ...tools ? {
2315
2160
  tools: [
2316
2161
  ...tools,
@@ -2321,10 +2166,7 @@ async function prewarmAnthropicCache(options) {
2321
2166
  {
2322
2167
  signal: options.signal ?? void 0,
2323
2168
  ...(() => {
2324
- const betas = [
2325
- ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
2326
- ...cacheControl?.ttl === "1h" ? ["extended-cache-ttl-2025-04-11"] : []
2327
- ];
2169
+ const betas = cacheControl?.ttl === "1h" ? ["extended-cache-ttl-2025-04-11"] : [];
2328
2170
  return betas.length ? { headers: { "anthropic-beta": betas.join(",") } } : {};
2329
2171
  })()
2330
2172
  }
@@ -2336,8 +2178,7 @@ function streamAnthropic(options) {
2336
2178
  return new StreamResult(runStream(options), options.signal);
2337
2179
  }
2338
2180
  async function* runStream(options) {
2339
- const client = createClient(await withClaudeUserAgent(options));
2340
- const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
2181
+ const client = createClient(options);
2341
2182
  const useStreaming = options.streaming !== false && !options.prewarm;
2342
2183
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
2343
2184
  const supportsFirstPartyToolExtras = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
@@ -2347,18 +2188,11 @@ async function* runStream(options) {
2347
2188
  const useServerFallback = options.provider !== "minimax" && isDirectAnthropicApi(options.baseUrl) && !anthropicServerFallback.disabled.has(fallbackKey);
2348
2189
  const deferredPlan = options.provider === "anthropic" && isDirectAnthropicApi(options.baseUrl) && supportsAnthropicDeferredTools(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgradedMessages) : void 0;
2349
2190
  const toolReferences = deferredPlan?.loaded;
2350
- const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl, {
2191
+ const { system, messages } = toAnthropicMessages(downgradedMessages, cacheControl, {
2351
2192
  fallbackBlocks: useServerFallback,
2352
2193
  toolReferences
2353
2194
  });
2354
2195
  const useFastMode = options.speed === "fast" && options.provider === "anthropic" && isDirectAnthropicApi(options.baseUrl);
2355
- const system = isOAuth ? [
2356
- {
2357
- type: "text",
2358
- text: "You are Claude Code, Anthropic's official CLI for Claude."
2359
- },
2360
- ...rawSystem ?? []
2361
- ] : rawSystem;
2362
2196
  let maxTokens = options.maxTokens ?? 4096;
2363
2197
  let thinking;
2364
2198
  let outputConfig;
@@ -2434,7 +2268,6 @@ async function* runStream(options) {
2434
2268
  const params = mergeProviderOptions(baseParams, options.providerOptions);
2435
2269
  const hasAdaptiveThinking = isAdaptiveThinkingModel(options.model);
2436
2270
  const betaHeaders = [
2437
- ...isOAuth ? ["claude-code-20250219", "oauth-2025-04-20"] : [],
2438
2271
  ...options.compaction ? ["compact-2026-01-12"] : [],
2439
2272
  ...options.clearToolUses ? ["context-management-2025-06-27"] : [],
2440
2273
  // Eager tool-input streaming beta — opt-in only (see
@@ -2930,6 +2763,15 @@ function fnv1aHash(value) {
2930
2763
  return (hash >>> 0).toString(16).padStart(8, "0");
2931
2764
  }
2932
2765
 
2766
+ // src/utils/diag.ts
2767
+ var _diagFn = null;
2768
+ function setProviderDiagnostic(fn) {
2769
+ _diagFn = fn;
2770
+ }
2771
+ function providerDiag(phase, data) {
2772
+ _diagFn?.(phase, data);
2773
+ }
2774
+
2933
2775
  // src/providers/moonshot-video.ts
2934
2776
  async function uploadMoonshotVideos(client, messages, signal) {
2935
2777
  for (const msg of messages) {
@@ -3173,11 +3015,11 @@ async function* runStream2(options) {
3173
3015
  }
3174
3016
  const requestParams = mergeProviderOptions(params, options.providerOptions);
3175
3017
  if (getEnvironment()?.GGAI_DUMP_REQUEST) {
3176
- const fs2 = await import("fs");
3018
+ const fs = await import("fs");
3177
3019
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3178
3020
  const dumpPath = `/tmp/ggai-request-${ts}.json`;
3179
- fs2.writeFileSync(dumpPath, JSON.stringify(requestParams, null, 2));
3180
- fs2.appendFileSync(
3021
+ fs.writeFileSync(dumpPath, JSON.stringify(requestParams, null, 2));
3022
+ fs.appendFileSync(
3181
3023
  "/tmp/ggai-requests.log",
3182
3024
  `[${ts}] ${dumpPath} messages=${requestParams.messages.length}
3183
3025
  `
@@ -3506,10 +3348,6 @@ function toError2(err, provider = "openai") {
3506
3348
  return new ProviderError(provider, String(err));
3507
3349
  }
3508
3350
 
3509
- // src/providers/openai-codex.ts
3510
- var import_node_os2 = __toESM(require("os"), 1);
3511
- var zstd = __toESM(require("@bokuweb/zstd-wasm"), 1);
3512
-
3513
3351
  // src/utils/sse.ts
3514
3352
  function parseSseBuffer(buffer) {
3515
3353
  const events = [];
@@ -3561,6 +3399,9 @@ function extractRequestIdFromMessage(message) {
3561
3399
  return match?.[1];
3562
3400
  }
3563
3401
 
3402
+ // src/user-agent.ts
3403
+ var GG_CODER_USER_AGENT = "ggcoder (+https://github.com/KenKaiii/gg-coder)";
3404
+
3564
3405
  // src/providers/responses-stream.ts
3565
3406
  function isEncryptedReasoning(data) {
3566
3407
  return data.type === "reasoning" && typeof data.id === "string" && typeof data.encrypted_content === "string";
@@ -3608,9 +3449,11 @@ async function* consumeResponsesEvents(events, ctx) {
3608
3449
  });
3609
3450
  }
3610
3451
  if (type === "response.failed") {
3611
- const nested = event.error;
3452
+ const nested = event.error ?? event.response?.error;
3612
3453
  const message = nested?.message ?? `${ctx.label} response failed.`;
3613
3454
  const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
3455
+ const usageLimit = nested ? ctx.errorChunk?.(nested, requestId) : null;
3456
+ if (usageLimit) throw usageLimit;
3614
3457
  throw new ProviderError(ctx.provider, message, {
3615
3458
  ...requestId != null ? { requestId } : {}
3616
3459
  });
@@ -3960,6 +3803,7 @@ function toResponsesInput(messages, options) {
3960
3803
  ...sendItemId ? { id: mapId(itemId ?? callId, idMap) } : {},
3961
3804
  call_id: mapId(callId, idMap),
3962
3805
  name: part.name,
3806
+ ...options.toolNamespace ? { namespace: options.toolNamespace } : {},
3963
3807
  arguments: JSON.stringify(part.args)
3964
3808
  });
3965
3809
  }
@@ -4023,85 +3867,46 @@ function toResponsesInput(messages, options) {
4023
3867
  return { system, input };
4024
3868
  }
4025
3869
 
4026
- // src/providers/openai-codex.ts
4027
- var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
4028
- var CODEX_CLIENT_VERSION = "0.166.0";
4029
- var CODEX_REQUEST_COMPRESSION_MIN_BYTES = 16 * 1024;
4030
- var zstdInitPromise;
4031
- async function encodeCodexRequest(body) {
4032
- const json = JSON.stringify(body);
4033
- const raw = new TextEncoder().encode(json);
4034
- if (raw.byteLength < CODEX_REQUEST_COMPRESSION_MIN_BYTES) {
4035
- return {
4036
- body: json,
4037
- compressed: false,
4038
- rawBytes: raw.byteLength,
4039
- encodedBytes: raw.byteLength
4040
- };
4041
- }
4042
- try {
4043
- zstdInitPromise ??= zstd.init();
4044
- await zstdInitPromise;
4045
- const compressed = Uint8Array.from(zstd.compress(raw));
4046
- if (compressed.byteLength >= raw.byteLength) {
4047
- return {
4048
- body: json,
4049
- compressed: false,
4050
- rawBytes: raw.byteLength,
4051
- encodedBytes: raw.byteLength
4052
- };
4053
- }
4054
- return {
4055
- body: compressed,
4056
- compressed: true,
4057
- rawBytes: raw.byteLength,
4058
- encodedBytes: compressed.byteLength
4059
- };
4060
- } catch (error) {
4061
- providerDiag("codex_request_compression_failed", {
4062
- error: error instanceof Error ? error.message : String(error),
4063
- rawBytes: raw.byteLength
4064
- });
4065
- return {
4066
- body: json,
4067
- compressed: false,
4068
- rawBytes: raw.byteLength,
4069
- encodedBytes: raw.byteLength
4070
- };
4071
- }
4072
- }
3870
+ // src/providers/openai-responses.ts
3871
+ var DEFAULT_BASE_URL = "https://api.openai.com/v1";
3872
+ var CHATGPT_PLAN_TOOL_NAMESPACE = "gg";
3873
+ var CHATGPT_USAGE_URL = "https://chatgpt.com/settings/usage";
4073
3874
  function usesResponsesLite(model) {
4074
3875
  return model.startsWith("gpt-5.6-") || model.startsWith("gpt-6-") || model.startsWith("gpt-6.");
4075
3876
  }
4076
- function toCodexToolChoice(choice, tools) {
3877
+ function isChatGPTPlanRoute(options) {
3878
+ return Boolean(options.accountId);
3879
+ }
3880
+ function toResponsesToolChoice(choice, tools) {
4077
3881
  const resolved = choice ?? "auto";
4078
3882
  if (typeof resolved === "object") {
4079
3883
  throw new GGAIError(
4080
- `OpenAI Codex does not support selecting the named tool \`${resolved.name}\`; use auto, none, or required.`,
3884
+ `OpenAI Responses does not support selecting the named tool \`${resolved.name}\`; use auto, none, or required.`,
4081
3885
  { source: "capability" }
4082
3886
  );
4083
3887
  }
4084
3888
  if (resolved === "required" && !tools?.length) {
4085
- throw new GGAIError("OpenAI Codex cannot require a tool call when no tools are configured.", {
3889
+ throw new GGAIError("OpenAI cannot require a tool call when no tools are configured.", {
4086
3890
  source: "capability"
4087
3891
  });
4088
3892
  }
4089
3893
  return resolved;
4090
3894
  }
4091
- function streamOpenAICodex(options) {
3895
+ function streamOpenAIResponses(options) {
4092
3896
  return new StreamResult(runStream3(options), options.signal);
4093
3897
  }
4094
3898
  async function* runStream3(options, retriedWithoutReasoning = false) {
3899
+ const plan = isChatGPTPlanRoute(options);
4095
3900
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
4096
- const url = `${baseUrl}/codex/responses`;
3901
+ const url = `${baseUrl}/responses`;
4097
3902
  const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
4098
3903
  const downgraded = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
4099
- const deferredPlan = supportsResponsesToolSearch(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgraded) : void 0;
3904
+ const deferredPlan = !plan && supportsResponsesToolSearch(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgraded) : void 0;
4100
3905
  const strictTools = options.strictTools ?? true;
4101
3906
  const toolSearchLoads = deferredPlan ? new Map(
4102
3907
  [...deferredPlan.loaded].map((name) => {
4103
3908
  const tool = deferredPlan.byName.get(name);
4104
- const [wire] = tool ? toCodexTools([tool], strictTools) : [];
3909
+ const [wire] = tool ? toResponsesTools([tool], strictTools) : [];
4105
3910
  return [name, { ...wire, defer_loading: true }];
4106
3911
  })
4107
3912
  ) : void 0;
@@ -4109,28 +3914,36 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4109
3914
  const { system, input } = toResponsesInput(downgraded, {
4110
3915
  supportsImages: options.supportsImages,
4111
3916
  toolSearchLoads,
4112
- mapId: remapCodexId,
3917
+ mapId: remapCallId,
3918
+ ...plan ? { toolNamespace: CHATGPT_PLAN_TOOL_NAMESPACE } : {},
4113
3919
  // Stamped reasoning belongs to another transport (e.g. Grok's) and would
4114
- // be rejected here; Codex's own items are never stamped.
3920
+ // be rejected here; OpenAI's own items are never stamped.
4115
3921
  keepReasoning: (part) => part.origin === void 0
4116
3922
  });
4117
- const responsesLite = usesResponsesLite(options.model);
4118
- const liteShape = options.responsesLite ?? responsesLite;
3923
+ const gpt6 = usesResponsesLite(options.model);
4119
3924
  const body = {
4120
3925
  model: options.model,
4121
3926
  store: false,
4122
3927
  stream: true,
4123
3928
  instructions: system,
4124
3929
  input,
4125
- tool_choice: toCodexToolChoice(options.toolChoice, liveTools),
4126
- parallel_tool_calls: !liteShape,
3930
+ tool_choice: toResponsesToolChoice(options.toolChoice, liveTools),
3931
+ parallel_tool_calls: true,
4127
3932
  include: ["reasoning.encrypted_content"]
4128
3933
  };
4129
3934
  if (liveTools?.length) {
4130
- body.tools = toCodexTools(liveTools, strictTools);
3935
+ const wire = toResponsesTools(liveTools, strictTools);
3936
+ body.tools = plan ? [
3937
+ {
3938
+ type: "namespace",
3939
+ name: CHATGPT_PLAN_TOOL_NAMESPACE,
3940
+ description: "GG Coder's tools for working in the user's project.",
3941
+ tools: wire
3942
+ }
3943
+ ] : wire;
4131
3944
  }
4132
3945
  body.prompt_cache_key = normalizePromptCacheKey(options.promptCacheKey ?? "ggcoder");
4133
- if (options.temperature != null && !options.thinking) {
3946
+ if (!plan && options.temperature != null && !options.thinking) {
4134
3947
  body.temperature = options.temperature;
4135
3948
  }
4136
3949
  if (options.speed === "fast") {
@@ -4138,57 +3951,38 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4138
3951
  }
4139
3952
  body.reasoning = {
4140
3953
  // GPT-5.6/6 require at least low; older models still support thinking off.
4141
- // Apply the floor here for every caller, including one-off prompt rewrites.
4142
- // `ultra` is a client orchestration preset, not a Codex API effort.
4143
- effort: options.thinking ? toCodexReasoningEffort(options.thinking, options.model) : responsesLite ? "low" : "none",
4144
- summary: "auto",
4145
- ...liteShape ? { context: "all_turns" } : {}
3954
+ // `ultra` is a client orchestration preset, not an API effort.
3955
+ effort: options.thinking ? toCodexReasoningEffort(options.thinking, options.model) : gpt6 ? "low" : "none",
3956
+ summary: "auto"
4146
3957
  };
4147
- if (responsesLite) {
3958
+ if (gpt6) {
4148
3959
  body.text = { verbosity: "low" };
4149
3960
  }
4150
3961
  const headers = {
4151
3962
  "Content-Type": "application/json",
4152
3963
  Accept: "text/event-stream",
4153
3964
  Authorization: `Bearer ${options.apiKey}`,
4154
- "OpenAI-Beta": "responses=experimental",
4155
- originator: responsesLite ? "codex_cli_rs" : "ggcoder",
4156
- "User-Agent": responsesLite ? `codex_cli_rs/${CODEX_CLIENT_VERSION}` : `ggcoder (${import_node_os2.default.platform()} ${import_node_os2.default.release()}; ${import_node_os2.default.arch()})`,
4157
- ...responsesLite ? { version: CODEX_CLIENT_VERSION } : {},
4158
- ...liteShape ? { "X-OpenAI-Internal-Codex-Responses-Lite": "true" } : {}
3965
+ "User-Agent": GG_CODER_USER_AGENT
4159
3966
  };
4160
- if (options.accountId) {
4161
- headers["chatgpt-account-id"] = options.accountId;
4162
- }
4163
- if (options.transportSessionId) {
4164
- const transportSessionId = normalizePromptCacheKey(options.transportSessionId);
4165
- headers["session_id"] = transportSessionId;
4166
- headers["x-client-request-id"] = transportSessionId;
4167
- }
4168
- const encodedRequest = await encodeCodexRequest(
4169
- mergeProviderOptions(body, options.providerOptions)
4170
- );
4171
- if (encodedRequest.compressed) headers["Content-Encoding"] = "zstd";
4172
- providerDiag("codex_request_body", {
4173
- rawBytes: encodedRequest.rawBytes,
4174
- encodedBytes: encodedRequest.encodedBytes,
4175
- compressed: encodedRequest.compressed
4176
- });
3967
+ const json = JSON.stringify(mergeProviderOptions(body, options.providerOptions));
3968
+ providerDiag("openai_responses_request_body", { rawBytes: json.length, plan });
4177
3969
  const response = await providerFetch(url, {
4178
3970
  method: "POST",
4179
3971
  headers,
4180
- body: encodedRequest.body,
3972
+ body: json,
4181
3973
  signal: options.signal
4182
3974
  });
4183
3975
  if (!response.ok) {
4184
3976
  const text = await response.text().catch(() => "");
4185
- const parsed = parseCodexErrorBody(text, response.status);
4186
- const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
3977
+ const parsed = parseErrorBody(text, response.status);
3978
+ const message = parsed.message ?? `OpenAI returned HTTP ${response.status}.`;
4187
3979
  const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
4188
3980
  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(
4189
3981
  (msg) => msg.role === "assistant" && Array.isArray(msg.content) && msg.content.some((part) => part.type === "raw" && isEncryptedReasoning(part.data))
4190
3982
  )) {
4191
- providerDiag("codex_retry_without_encrypted_reasoning", { status: response.status });
3983
+ providerDiag("openai_responses_retry_without_encrypted_reasoning", {
3984
+ status: response.status
3985
+ });
4192
3986
  const messages = options.messages.map(
4193
3987
  (msg) => msg.role === "assistant" && Array.isArray(msg.content) ? {
4194
3988
  ...msg,
@@ -4199,13 +3993,19 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4199
3993
  );
4200
3994
  return yield* runStream3({ ...options, messages }, true);
4201
3995
  }
4202
- const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
4203
- if (usageLimit) throw usageLimit;
3996
+ if (plan) {
3997
+ const planError = chatGPTPlanError(
3998
+ parsed.errorObj,
3999
+ response.status,
4000
+ requestId,
4001
+ message,
4002
+ options.model
4003
+ );
4004
+ if (planError) throw planError;
4005
+ }
4204
4006
  let hint;
4205
- if (response.status === 400 && message === `The '${options.model}' model is not supported when using Codex with a ChatGPT account.`) {
4206
- hint = "This model is not available through your ChatGPT account. Choose another available model using the model selector.";
4207
- } else if (response.status === 404 && text.includes("does not exist")) {
4208
- hint = "This model is not in OpenAI's current catalog for your ChatGPT account. Choose another available model using the model selector.";
4007
+ if (response.status === 404 && text.includes("does not exist")) {
4008
+ 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.";
4209
4009
  }
4210
4010
  throw new ProviderError("openai", message, {
4211
4011
  statusCode: response.status,
@@ -4214,14 +4014,14 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4214
4014
  });
4215
4015
  }
4216
4016
  if (!response.body) {
4217
- throw new ProviderError("openai", "No response body from Codex API");
4017
+ throw new ProviderError("openai", "No response body from OpenAI");
4218
4018
  }
4219
4019
  return yield* consumeResponsesEvents(parseSSE(response.body), {
4220
4020
  provider: "openai",
4221
- label: "Codex",
4222
- diagPrefix: "codex",
4021
+ label: "OpenAI",
4022
+ diagPrefix: "openai_responses",
4223
4023
  thinking: !!options.thinking,
4224
- errorChunk: (errorObj, requestId) => codexUsageLimitError(errorObj, void 0, requestId)
4024
+ errorChunk: (errorObj, requestId) => plan ? chatGPTPlanError(errorObj, void 0, requestId, void 0, options.model) : null
4225
4025
  });
4226
4026
  }
4227
4027
  async function* parseSSE(body) {
@@ -4234,7 +4034,7 @@ async function* parseSSE(body) {
4234
4034
  }
4235
4035
  }
4236
4036
  }
4237
- function remapCodexId(id, idMap) {
4037
+ function remapCallId(id, idMap) {
4238
4038
  const existing = idMap.get(id);
4239
4039
  if (existing) return existing;
4240
4040
  const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
@@ -4248,7 +4048,7 @@ function remapCodexId(id, idMap) {
4248
4048
  idMap.set(id, mapped);
4249
4049
  return mapped;
4250
4050
  }
4251
- function toCodexTools(tools, strictTools) {
4051
+ function toResponsesTools(tools, strictTools) {
4252
4052
  return tools.map((tool) => {
4253
4053
  let parameters = resolveToolSchema(tool);
4254
4054
  let strict = null;
@@ -4269,7 +4069,7 @@ function toCodexTools(tools, strictTools) {
4269
4069
  };
4270
4070
  });
4271
4071
  }
4272
- function parseCodexErrorBody(text, statusCode) {
4072
+ function parseErrorBody(text, statusCode) {
4273
4073
  if (!text) return {};
4274
4074
  try {
4275
4075
  const parsed = JSON.parse(text);
@@ -4293,23 +4093,70 @@ function parseCodexErrorBody(text, statusCode) {
4293
4093
  return bounded ? { message: bounded } : {};
4294
4094
  }
4295
4095
  }
4296
- var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
4297
- var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
4298
- function codexUsageLimitError(errorObj, statusCode, requestId) {
4096
+ function chatGPTPlanError(errorObj, statusCode, requestId, message, model) {
4299
4097
  const code = String(errorObj?.code ?? errorObj?.type ?? "");
4300
- const rateLimits = errorObj?.rate_limits;
4301
- const resetsAtRaw = (typeof errorObj?.resets_at === "number" ? errorObj.resets_at : void 0) ?? rateLimits?.primary?.resets_at ?? rateLimits?.secondary?.resets_at;
4302
- const resetsInSeconds = typeof errorObj?.resets_in_seconds === "number" ? errorObj.resets_in_seconds : void 0;
4303
- const resetsAt = typeof resetsAtRaw === "number" && resetsAtRaw > 0 ? resetsAtRaw : resetsInSeconds != null && resetsInSeconds > 0 ? Math.floor(Date.now() / 1e3) + resetsInSeconds : void 0;
4304
- const isHardUsage = CODEX_USAGE_LIMIT_CODE.test(code);
4305
- const isRateOr429 = CODEX_RATE_LIMIT_CODE.test(code) || statusCode === 429;
4306
- if (!isHardUsage && !(isRateOr429 && resetsAt != null)) return null;
4307
- return new ProviderError("openai", "ChatGPT usage limit reached", {
4308
- cause: errorObj,
4309
- statusCode: statusCode ?? 429,
4310
- ...requestId ? { requestId } : {},
4311
- ...resetsAt ? { resetsAt } : {}
4312
- });
4098
+ const base = { ...requestId ? { requestId } : {}, cause: errorObj };
4099
+ switch (code) {
4100
+ // The plan's usage — or the limit the user set for GG Coder — is spent, or
4101
+ // can't be checked right now. Both stop plan requests; the canonical
4102
+ // "usage limit reached" message lets the session fall back to an API key.
4103
+ case "subscription_sharing_usage_limit_exceeded":
4104
+ case "subscription_sharing_usage_unavailable":
4105
+ return new ProviderError("openai", "ChatGPT usage limit reached", {
4106
+ ...base,
4107
+ statusCode: statusCode ?? (code.endsWith("unavailable") ? 503 : 429),
4108
+ 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.`
4109
+ });
4110
+ case "subscription_sharing_user_not_eligible":
4111
+ return new ProviderError(
4112
+ "openai",
4113
+ "ChatGPT plan use isn't available for this account or workspace.",
4114
+ {
4115
+ ...base,
4116
+ statusCode: statusCode ?? 403,
4117
+ hint: "Add an OpenAI API key, or ask your workspace admin to allow ChatGPT plan use in other apps."
4118
+ }
4119
+ );
4120
+ // The account's plan doesn't include this model (live: 400 model_not_found,
4121
+ // "…not supported when using Codex with a ChatGPT account").
4122
+ case "model_not_found":
4123
+ return new ProviderError(
4124
+ "openai",
4125
+ `${model ?? "This model"} isn't included in your ChatGPT plan.`,
4126
+ {
4127
+ ...base,
4128
+ statusCode: statusCode ?? 400,
4129
+ hint: "Pick a model your plan includes (the model picker marks the others), or use an OpenAI API key for this one."
4130
+ }
4131
+ );
4132
+ case "subscription_sharing_unsupported_capability": {
4133
+ const param = typeof errorObj?.param === "string" ? errorObj.param : void 0;
4134
+ if (param?.includes("service_tier")) {
4135
+ return new ProviderError("openai", "Fast mode isn't included in your ChatGPT plan.", {
4136
+ ...base,
4137
+ statusCode: statusCode ?? 400,
4138
+ hint: "Switch to the standard model, or use an OpenAI API key for Fast mode."
4139
+ });
4140
+ }
4141
+ return new ProviderError(
4142
+ "openai",
4143
+ message ?? `The ChatGPT plan route doesn't support ${param ?? "part of this request"}.`,
4144
+ {
4145
+ ...base,
4146
+ statusCode: statusCode ?? 400,
4147
+ 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.`
4148
+ }
4149
+ );
4150
+ }
4151
+ case "subscription_sharing_invalid_user":
4152
+ return new ProviderError("openai", message ?? "ChatGPT couldn't validate your sign-in.", {
4153
+ ...base,
4154
+ statusCode: statusCode ?? 401,
4155
+ hint: "Continue with ChatGPT again to reconnect."
4156
+ });
4157
+ default:
4158
+ return null;
4159
+ }
4313
4160
  }
4314
4161
 
4315
4162
  // src/providers/xai-responses.ts
@@ -4443,92 +4290,17 @@ async function* synthesizeResponseEvents(response) {
4443
4290
  }
4444
4291
 
4445
4292
  // src/providers/gemini.ts
4446
- var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
4447
- var CODE_ASSIST_API_VERSION = "v1internal";
4448
- var GEMINI_CLI_USER_AGENT = "google-gemini-cli";
4449
- var GEMINI_CLI_API_CLIENT = "gemini-cli/0.0.0";
4450
- var CODE_ASSIST_NON_STREAMING_RETRIES = 3;
4451
- var CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS = 1e3;
4293
+ var DEFAULT_BASE_URL2 = "https://generativelanguage.googleapis.com/v1beta";
4294
+ var NON_STREAMING_RETRIES = 3;
4295
+ var NON_STREAMING_RETRY_DELAY_MS = 1e3;
4452
4296
  var SYNTHETIC_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
4453
- var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
4454
- "gemini-3-pro-preview",
4455
- "gemini-3.1-pro-preview",
4456
- "gemini-3.1-pro-preview-customtools",
4457
- "gemini-3-flash-preview",
4458
- "gemini-3.5-flash",
4459
- "gemini-3-flash",
4460
- "gemini-3.1-flash-lite",
4461
- "gemini-3.7-flash",
4462
- "gemini-3.8-flash",
4463
- "gemini-3.5-flash-lite",
4464
- "gemini-2.5-pro",
4465
- "gemini-2.5-flash",
4466
- "gemma-4-31b-it",
4467
- "gemma-4-26b-a4b-it"
4468
- ]);
4469
- function isGeminiApiKeyRoute(options) {
4470
- return options.baseUrl?.includes("generativelanguage.googleapis.com") === true;
4471
- }
4472
- function getGoogleProject(options) {
4473
- const env = getEnvironment();
4474
- return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
4475
- }
4476
- function getCodeAssistEndpoint(method) {
4477
- const env = getEnvironment();
4478
- const endpoint = env?.CODE_ASSIST_ENDPOINT ?? DEFAULT_CODE_ASSIST_BASE_URL;
4479
- const version = env?.CODE_ASSIST_API_VERSION || CODE_ASSIST_API_VERSION;
4480
- return new URL(`${endpoint}/${version}:${method}`);
4481
- }
4482
- function formatUnsupportedModelMessage(model) {
4483
- return `Gemini OAuth is configured to use the Gemini Code Assist subscription endpoint only. That endpoint does not currently expose model "${model}".`;
4484
- }
4485
- var ACCOUNT_GATED_MODELS = /* @__PURE__ */ new Set([
4486
- "gemini-3-flash",
4487
- "gemini-3.5-flash",
4488
- "gemini-3.1-pro-preview",
4489
- "gemini-3.1-pro-preview-customtools",
4490
- "gemini-3.7-flash",
4491
- "gemini-3.8-flash",
4492
- "gemini-3.5-flash-lite"
4493
- ]);
4494
- function accountGatedMessage(model) {
4495
- if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
4496
- return `${model} is not available through Code Assist for this account. Public Gemini API availability does not guarantee Code Assist OAuth access.`;
4497
- }
4498
- 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.`;
4499
- }
4500
- function accountGatedHint(model) {
4501
- if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
4502
- return "Use /model to select Gemini 3.1 Flash Lite, or retry once Google enables this model through Code Assist for your account.";
4503
- }
4504
- 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.`;
4505
- }
4506
- function isCodeAssistLicenseRequired(status, body) {
4507
- if (status !== 403) return false;
4508
- try {
4509
- const parsed = JSON.parse(body);
4510
- const details = typeof parsed === "object" && parsed !== null && "error" in parsed ? parsed.error?.details : void 0;
4511
- return Array.isArray(details) && details.some(
4512
- (detail) => typeof detail === "object" && detail !== null && detail.reason === "SUBSCRIPTION_REQUIRED"
4513
- );
4514
- } catch {
4515
- return false;
4516
- }
4517
- }
4518
4297
  function isInvalidGeminiApiKey(status, body) {
4519
4298
  if (status !== 400 && status !== 401 && status !== 403) return false;
4520
4299
  return /"reason"\s*:\s*"API_KEY_INVALID"/.test(body) || /API key not valid/i.test(body);
4521
4300
  }
4522
4301
  var INVALID_API_KEY_MESSAGE = "Google rejected the Gemini API key: it is not valid.";
4523
4302
  var INVALID_API_KEY_HINT = "Create a key at aistudio.google.com/apikey and add it again under Gemini in Providers (or `ggcoder login`).";
4524
- 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.";
4525
- 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.";
4526
- function formatErrorMessage(status, body, model) {
4527
- if (status === 404 && !CODE_ASSIST_SUPPORTED_MODELS.has(model)) {
4528
- return `Gemini API error (404): ${body}
4529
-
4530
- ${formatUnsupportedModelMessage(model)}`;
4531
- }
4303
+ function formatErrorMessage(status, body) {
4532
4304
  return `Gemini API error (${status}): ${body}`;
4533
4305
  }
4534
4306
  function parseRetryDelaySeconds(body) {
@@ -4734,27 +4506,15 @@ function buildGenerateRequest(options) {
4734
4506
  ...systemInstruction ? { systemInstruction } : {},
4735
4507
  ...tools ? { tools } : {},
4736
4508
  ...toolConfig ? { toolConfig } : {},
4737
- ...Object.keys(generationConfig).length > 0 ? { generationConfig } : {},
4738
- ...options.promptCacheKey ? { session_id: options.promptCacheKey } : {}
4739
- };
4740
- }
4741
- function buildCodeAssistRequest(options, request, projectId) {
4742
- return {
4743
- model: options.model,
4744
- ...projectId ? { project: projectId } : {},
4745
- user_prompt_id: crypto.randomUUID(),
4746
- request
4509
+ ...Object.keys(generationConfig).length > 0 ? { generationConfig } : {}
4747
4510
  };
4748
4511
  }
4749
4512
  var API_KEY_MODEL_IDS = {
4750
4513
  "gemini-3-flash": "gemini-3.5-flash"
4751
4514
  };
4752
- function buildApiKeyRequestPlan(options, method) {
4753
- const { session_id: _sessionId, ...request } = mergeProviderOptions(
4754
- buildGenerateRequest(options),
4755
- options.providerOptions
4756
- );
4757
- const base = (options.baseUrl ?? "").replace(/\/+$/, "");
4515
+ function buildRequestPlan(options, method) {
4516
+ const request = mergeProviderOptions(buildGenerateRequest(options), options.providerOptions);
4517
+ const base = (options.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
4758
4518
  return {
4759
4519
  url: new URL(
4760
4520
  `${base}/models/${encodeURIComponent(API_KEY_MODEL_IDS[options.model] ?? options.model)}:${method}`
@@ -4763,27 +4523,7 @@ function buildApiKeyRequestPlan(options, method) {
4763
4523
  "Content-Type": "application/json",
4764
4524
  "x-goog-api-key": options.apiKey ?? ""
4765
4525
  },
4766
- body: request,
4767
- route: "api-key"
4768
- };
4769
- }
4770
- function buildRequestPlan(options, method) {
4771
- if (isGeminiApiKeyRoute(options)) return buildApiKeyRequestPlan(options, method);
4772
- if (!CODE_ASSIST_SUPPORTED_MODELS.has(options.model)) {
4773
- throw new ProviderError("gemini", formatUnsupportedModelMessage(options.model));
4774
- }
4775
- const projectId = getGoogleProject(options);
4776
- const request = mergeProviderOptions(buildGenerateRequest(options), options.providerOptions);
4777
- return {
4778
- url: getCodeAssistEndpoint(method),
4779
- headers: {
4780
- Authorization: `Bearer ${options.apiKey}`,
4781
- "Content-Type": "application/json",
4782
- "User-Agent": GEMINI_CLI_USER_AGENT,
4783
- "X-Goog-Api-Client": GEMINI_CLI_API_CLIENT
4784
- },
4785
- body: buildCodeAssistRequest(options, request, projectId),
4786
- route: "code-assist"
4526
+ body: request
4787
4527
  };
4788
4528
  }
4789
4529
  function normalizeGeminiStopReason(reason) {
@@ -4810,10 +4550,10 @@ async function* streamSse(response) {
4810
4550
  }
4811
4551
  }
4812
4552
  function candidatesFromResponse(response) {
4813
- return response.response?.candidates ?? response.candidates;
4553
+ return response.candidates;
4814
4554
  }
4815
4555
  function usageFromResponse(response) {
4816
- return response.response?.usageMetadata ?? response.usageMetadata;
4556
+ return response.usageMetadata;
4817
4557
  }
4818
4558
  function partsFromResponse(response) {
4819
4559
  return candidatesFromResponse(response)?.[0]?.content?.parts ?? [];
@@ -4835,7 +4575,7 @@ function readFunctionCallPart(part) {
4835
4575
  function makeToolCallId(index, providerId) {
4836
4576
  return providerId ?? `gemini_call_${index}_${crypto.randomUUID().replace(/-/g, "")}`;
4837
4577
  }
4838
- function shouldRetryCodeAssistStatus(status) {
4578
+ function shouldRetryStatus(status) {
4839
4579
  return status === 429 || status === 499 || status >= 500 && status <= 599;
4840
4580
  }
4841
4581
  function isAbortError(err) {
@@ -4858,7 +4598,7 @@ async function sleep(ms, signal) {
4858
4598
  if (signal?.aborted) onAbort();
4859
4599
  });
4860
4600
  }
4861
- async function fetchCodeAssist(plan, options) {
4601
+ async function fetchGemini(plan, options) {
4862
4602
  try {
4863
4603
  const response = await providerFetch(plan.url, {
4864
4604
  method: "POST",
@@ -4869,11 +4609,8 @@ async function fetchCodeAssist(plan, options) {
4869
4609
  if (!response.ok) {
4870
4610
  const text = await response.text().catch(() => "");
4871
4611
  const quota = parseGeminiQuota(response.status, text);
4872
- const codeAssist = plan.route === "code-assist";
4873
- const accountGated = codeAssist && response.status === 404 && ACCOUNT_GATED_MODELS.has(options.model);
4874
- const licenseRequired = codeAssist && isCodeAssistLicenseRequired(response.status, text);
4875
- const invalidKey = !codeAssist && isInvalidGeminiApiKey(response.status, text);
4876
- let message = invalidKey ? INVALID_API_KEY_MESSAGE : licenseRequired ? LICENSE_REQUIRED_MESSAGE : accountGated ? accountGatedMessage(options.model) : formatErrorMessage(response.status, text, options.model);
4612
+ const invalidKey = isInvalidGeminiApiKey(response.status, text);
4613
+ let message = invalidKey ? INVALID_API_KEY_MESSAGE : formatErrorMessage(response.status, text);
4877
4614
  let resetsAt;
4878
4615
  if (quota?.exhausted) {
4879
4616
  message = `Gemini quota exhausted \u2014 usage limit reached. ${message}`;
@@ -4883,7 +4620,7 @@ async function fetchCodeAssist(plan, options) {
4883
4620
  throw new ProviderError("gemini", message, {
4884
4621
  statusCode: response.status,
4885
4622
  ...resetsAt !== void 0 ? { resetsAt } : {},
4886
- ...invalidKey ? { hint: INVALID_API_KEY_HINT, source: "auth" } : licenseRequired ? { hint: LICENSE_REQUIRED_HINT, source: "auth" } : accountGated ? { hint: accountGatedHint(options.model) } : {}
4623
+ ...invalidKey ? { hint: INVALID_API_KEY_HINT, source: "auth" } : {}
4887
4624
  });
4888
4625
  }
4889
4626
  return response;
@@ -4891,26 +4628,26 @@ async function fetchCodeAssist(plan, options) {
4891
4628
  throw toError3(err);
4892
4629
  }
4893
4630
  }
4894
- async function fetchCodeAssistWithRetry(plan, options) {
4631
+ async function fetchGeminiWithRetry(plan, options) {
4895
4632
  let lastError;
4896
- for (let attempt = 0; attempt <= CODE_ASSIST_NON_STREAMING_RETRIES; attempt++) {
4633
+ for (let attempt = 0; attempt <= NON_STREAMING_RETRIES; attempt++) {
4897
4634
  try {
4898
- return await fetchCodeAssist(plan, options);
4635
+ return await fetchGemini(plan, options);
4899
4636
  } catch (err) {
4900
4637
  const error = toError3(err);
4901
4638
  const statusCode = error instanceof ProviderError ? error.statusCode : void 0;
4902
- if (options.signal?.aborted || isAbortError(error) || attempt === CODE_ASSIST_NON_STREAMING_RETRIES || statusCode != null && !shouldRetryCodeAssistStatus(statusCode)) {
4639
+ if (options.signal?.aborted || isAbortError(error) || attempt === NON_STREAMING_RETRIES || statusCode != null && !shouldRetryStatus(statusCode)) {
4903
4640
  throw error;
4904
4641
  }
4905
4642
  lastError = error;
4906
4643
  }
4907
4644
  try {
4908
- await sleep(CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS, options.signal);
4645
+ await sleep(NON_STREAMING_RETRY_DELAY_MS, options.signal);
4909
4646
  } catch (err) {
4910
4647
  throw toError3(err);
4911
4648
  }
4912
4649
  }
4913
- throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
4650
+ throw lastError ?? new ProviderError("gemini", "Gemini request failed.");
4914
4651
  }
4915
4652
  function streamGemini(options) {
4916
4653
  return new StreamResult(runStream5(options), options.signal);
@@ -4920,7 +4657,7 @@ async function* runStream5(options) {
4920
4657
  const method = useStreaming ? "streamGenerateContent" : "generateContent";
4921
4658
  const plan = buildRequestPlan(options, method);
4922
4659
  if (useStreaming) plan.url.searchParams.set("alt", "sse");
4923
- const response = useStreaming ? await fetchCodeAssist(plan, options) : await fetchCodeAssistWithRetry(plan, options);
4660
+ const response = useStreaming ? await fetchGemini(plan, options) : await fetchGeminiWithRetry(plan, options);
4924
4661
  const contentParts = [];
4925
4662
  const pendingToolCalls = [];
4926
4663
  let textAccum = "";
@@ -5231,7 +4968,7 @@ async function postJson(request) {
5231
4968
  }
5232
4969
  function httpError(provider, response, text, headerRequestId, clean) {
5233
4970
  const status = response.status;
5234
- const parsed = parseErrorBody(text);
4971
+ const parsed = parseErrorBody2(text);
5235
4972
  const rawMessage = parsed.message ?? (parsed.isJson ? "" : text.trim());
5236
4973
  const message = !rawMessage ? emptyProviderErrorMessage(status) : isRawHtmlErrorEcho(rawMessage) ? providerHtmlErrorMessage(status) : clean(rawMessage);
5237
4974
  const requestId = headerRequestId ?? parsed.requestId;
@@ -5257,7 +4994,7 @@ function httpError(provider, response, text, headerRequestId, clean) {
5257
4994
  }
5258
4995
  return new ProviderError(provider, message, base);
5259
4996
  }
5260
- function parseErrorBody(text) {
4997
+ function parseErrorBody2(text) {
5261
4998
  let parsed;
5262
4999
  try {
5263
5000
  parsed = JSON.parse(text);
@@ -5293,8 +5030,8 @@ function bound(text) {
5293
5030
  const trimmed = text.trim();
5294
5031
  return trimmed.length > MAX_ERROR_MESSAGE_CHARS ? `${trimmed.slice(0, MAX_ERROR_MESSAGE_CHARS)}\u2026` : trimmed;
5295
5032
  }
5296
- function joinUrl(baseUrl, path2) {
5297
- return `${baseUrl.replace(/\/+$/, "")}/${path2.replace(/^\/+/, "")}`;
5033
+ function joinUrl(baseUrl, path) {
5034
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
5298
5035
  }
5299
5036
 
5300
5037
  // src/providers/gemini-embeddings.ts
@@ -5689,10 +5426,7 @@ var OPENAI_EMBED_BATCH_SIZE = 2048;
5689
5426
  var OPENROUTER_EMBED_BATCH_SIZE = 64;
5690
5427
  var LOCAL_EMBED_BATCH_SIZE = 64;
5691
5428
  var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
5692
- var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
5693
- var KIMI_CODE_USER_AGENT = `kimi-code-cli/${process.env.KIMI_CODE_VERSION ?? "1.0.11"}`;
5694
- var GROK_CLI_PROXY_HOST = "cli-chat-proxy.grok.com";
5695
- var GROK_CLI_VERSION = process.env.GROK_CLI_VERSION ?? "1.0.67";
5429
+ var GLM_BASE_URL = "https://api.z.ai/api/paas/v4";
5696
5430
  providerRegistry.register("anthropic", {
5697
5431
  stream: (options) => streamAnthropic(options)
5698
5432
  });
@@ -5705,12 +5439,12 @@ providerRegistry.register("xiaomi", {
5705
5439
  });
5706
5440
  providerRegistry.register("openai", {
5707
5441
  stream: (options) => {
5708
- if (options.accountId) {
5709
- return streamOpenAICodex(options);
5442
+ if (options.accountId || isOpenAIHost(options.baseUrl)) {
5443
+ return streamOpenAIResponses(options);
5710
5444
  }
5711
5445
  return streamOpenAI(options);
5712
5446
  },
5713
- // OpenAI API-key auth only; ChatGPT-subscription (Codex) tokens have no embeddings API.
5447
+ // OpenAI API-key auth only; the ChatGPT plan route has no embeddings API.
5714
5448
  // OpenAI has no task/input-type parameter, so `inputType` is ignored.
5715
5449
  embed: (request) => embedOpenAICompatible(request, {
5716
5450
  provider: "openai",
@@ -5720,22 +5454,24 @@ providerRegistry.register("openai", {
5720
5454
  });
5721
5455
  providerRegistry.register("gemini", {
5722
5456
  stream: (options) => streamGemini(options),
5723
- // Native Gemini API with an AI Studio key — not the Code Assist OAuth endpoint above.
5457
+ // Native Gemini API with an AI Studio key.
5724
5458
  embed: (request) => embedGemini(request),
5725
5459
  embedBatchSize: GEMINI_EMBED_BATCH_SIZE
5726
5460
  });
5727
5461
  providerRegistry.register("glm", {
5728
5462
  stream: (options) => streamOpenAI({
5729
5463
  ...options,
5730
- baseUrl: options.baseUrl ?? GLM_CODING_BASE_URL
5464
+ baseUrl: options.baseUrl ?? GLM_BASE_URL
5731
5465
  })
5732
5466
  });
5733
5467
  providerRegistry.register("moonshot", {
5734
- stream: (options) => {
5735
- const baseUrl = options.baseUrl ?? "https://api.moonshot.ai/v1";
5736
- const defaultHeaders = baseUrl.includes("api.kimi.com") ? { "User-Agent": KIMI_CODE_USER_AGENT, ...options.defaultHeaders } : options.defaultHeaders;
5737
- return streamOpenAI({ ...options, baseUrl, defaultHeaders });
5738
- }
5468
+ // A Moonshot key goes to the public API; a Kimi Code plan key carries its
5469
+ // own baseUrl (api.kimi.com / api.kimi.ai). Both get GG Coder's own identity.
5470
+ stream: (options) => streamOpenAI({
5471
+ ...options,
5472
+ baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1",
5473
+ defaultHeaders: { "User-Agent": GG_CODER_USER_AGENT, ...options.defaultHeaders }
5474
+ })
5739
5475
  });
5740
5476
  providerRegistry.register("deepseek", {
5741
5477
  stream: (options) => streamOpenAI({
@@ -5784,27 +5520,10 @@ providerRegistry.register("sakana", {
5784
5520
  })
5785
5521
  });
5786
5522
  providerRegistry.register("xai", {
5787
- // Both Grok routes ride xAI's Responses API (see providers/xai-responses.ts):
5788
- // Chat Completions is deprecated for Grok reasoning models and can't carry
5789
- // the encrypted reasoning that keeps long tool loops coherent.
5790
- //
5791
- // Subscription OAuth (SuperGrok / X Premium) routes to the Grok CLI chat proxy
5792
- // instead, which serves the same `/v1/responses` surface but gates on Grok-CLI
5793
- // client identity. Inject those headers centrally here — exactly as the Kimi
5794
- // endpoint above — so EVERY stream (agent loop, compaction, title-gen,
5795
- // sub-agents) is accepted rather than depending on each call site to thread
5796
- // headers. Caller-provided headers still win on collision.
5797
- stream: (options) => {
5798
- const baseUrl = options.baseUrl ?? "https://api.x.ai/v1";
5799
- const defaultHeaders = baseUrl.includes(GROK_CLI_PROXY_HOST) ? {
5800
- "X-XAI-Token-Auth": "xai-grok-cli",
5801
- "x-grok-client-version": GROK_CLI_VERSION,
5802
- "x-grok-client-identifier": "ggcoder",
5803
- "x-grok-model-override": options.model,
5804
- ...options.defaultHeaders
5805
- } : options.defaultHeaders;
5806
- return streamXaiResponses({ ...options, baseUrl, defaultHeaders });
5807
- }
5523
+ // xAI API key on xAI's Responses API (see providers/xai-responses.ts): Chat
5524
+ // Completions is deprecated for Grok reasoning models and can't carry the
5525
+ // encrypted reasoning that keeps long tool loops coherent.
5526
+ stream: (options) => streamXaiResponses({ ...options, baseUrl: options.baseUrl ?? "https://api.x.ai/v1" })
5808
5527
  });
5809
5528
  providerRegistry.register("minimax", {
5810
5529
  stream: (options) => streamAnthropic({
@@ -5855,6 +5574,14 @@ providerRegistry.register("local", {
5855
5574
  }
5856
5575
  )
5857
5576
  });
5577
+ function isOpenAIHost(baseUrl) {
5578
+ if (!baseUrl) return true;
5579
+ try {
5580
+ return new URL(baseUrl).hostname === "api.openai.com";
5581
+ } catch {
5582
+ return false;
5583
+ }
5584
+ }
5858
5585
  function requireLocalBaseUrl(baseUrl) {
5859
5586
  if (!baseUrl) {
5860
5587
  throw new GGAIError(
@@ -6045,16 +5772,16 @@ function l2Normalize(vector) {
6045
5772
 
6046
5773
  // src/utils/preconnect.ts
6047
5774
  var PRECONNECT_INTERVAL_MS = 3e4;
6048
- function defaultBaseUrl(provider, accountId) {
5775
+ function defaultBaseUrl(provider) {
6049
5776
  switch (provider) {
6050
5777
  case "anthropic":
6051
5778
  return "https://api.anthropic.com";
6052
5779
  case "openai":
6053
- return accountId ? "https://chatgpt.com/backend-api" : "https://api.openai.com/v1";
5780
+ return "https://api.openai.com/v1";
6054
5781
  case "gemini":
6055
- return "https://cloudcode-pa.googleapis.com";
5782
+ return "https://generativelanguage.googleapis.com/v1beta";
6056
5783
  case "glm":
6057
- return "https://api.z.ai/api/coding/paas/v4";
5784
+ return "https://api.z.ai/api/paas/v4";
6058
5785
  case "moonshot":
6059
5786
  return "https://api.moonshot.ai/v1";
6060
5787
  case "deepseek":
@@ -6087,7 +5814,7 @@ function isLocalHost(hostname) {
6087
5814
  return a === 127 || a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31;
6088
5815
  }
6089
5816
  function preconnectTarget(options) {
6090
- const raw = options.baseUrl ?? defaultBaseUrl(options.provider, options.accountId);
5817
+ const raw = options.baseUrl ?? defaultBaseUrl(options.provider);
6091
5818
  if (!raw || options.provider === "local" || options.provider === "palsu") return void 0;
6092
5819
  let url;
6093
5820
  try {
@@ -6602,11 +6329,10 @@ function palsuRerankScore(query, document) {
6602
6329
  }
6603
6330
  // Annotate the CommonJS export names for ESM import in node:
6604
6331
  0 && (module.exports = {
6605
- CLAUDE_CODE_FALLBACK_VERSION,
6606
- CODEX_CLIENT_VERSION,
6607
6332
  EventStream,
6608
6333
  FAST_MODE_SUFFIX,
6609
6334
  GGAIError,
6335
+ GG_CODER_USER_AGENT,
6610
6336
  LOCAL_NO_API_KEY,
6611
6337
  PRECONNECT_INTERVAL_MS,
6612
6338
  ProviderError,
@@ -6624,8 +6350,6 @@ function palsuRerankScore(query, document) {
6624
6350
  formatChatError,
6625
6351
  formatError,
6626
6352
  formatErrorForDisplay,
6627
- getClaudeCliUserAgent,
6628
- getClaudeCodeVersion,
6629
6353
  hasLoneSurrogate,
6630
6354
  isHardBillingMessage,
6631
6355
  isUsageLimitError,
@@ -6640,7 +6364,6 @@ function palsuRerankScore(query, document) {
6640
6364
  palsuThinking,
6641
6365
  palsuToolCall,
6642
6366
  parseFastModeRejection,
6643
- pendingClaudeCodeVersionRefresh,
6644
6367
  preconnectOrigin,
6645
6368
  preconnectProvider,
6646
6369
  prewarmAnthropicCache,