@kenkaiiii/gg-ai 5.94.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,10 +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
- CODEX_CLIENT_VERSION: () => CODEX_CLIENT_VERSION,
34
33
  EventStream: () => EventStream,
35
34
  FAST_MODE_SUFFIX: () => FAST_MODE_SUFFIX,
36
35
  GGAIError: () => GGAIError,
36
+ GG_CODER_USER_AGENT: () => GG_CODER_USER_AGENT,
37
37
  LOCAL_NO_API_KEY: () => LOCAL_NO_API_KEY,
38
38
  PRECONNECT_INTERVAL_MS: () => PRECONNECT_INTERVAL_MS,
39
39
  ProviderError: () => ProviderError,
@@ -333,6 +333,18 @@ function formatError(err) {
333
333
  }
334
334
  const miniMax = err.provider === "minimax" ? formatMiniMaxError(err, cleanMessage) : void 0;
335
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
+ }
336
348
  if (isUsageLimitError(err)) {
337
349
  const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
338
350
  return {
@@ -1233,7 +1245,7 @@ function toolCallNameRuleFor(provider, options) {
1233
1245
  return TOOL_CALL_NAME_RULES.anthropic;
1234
1246
  case "openai":
1235
1247
  if (options?.accountId) return TOOL_CALL_NAME_RULES["openai-responses"];
1236
- 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;
1237
1249
  case "gemini":
1238
1250
  return TOOL_CALL_NAME_RULES.gemini;
1239
1251
  default:
@@ -1431,8 +1443,8 @@ function usesDeferredToolLoading(options) {
1431
1443
  const firstParty = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
1432
1444
  return firstParty && supportsAnthropicDeferredTools(options.model);
1433
1445
  }
1434
- if (options.provider === "openai" && options.accountId) {
1435
- return supportsResponsesToolSearch(options.model);
1446
+ if (options.provider === "openai" && !options.accountId) {
1447
+ return isDefaultOrHost(options.baseUrl, "api.openai.com") && supportsResponsesToolSearch(options.model);
1436
1448
  }
1437
1449
  return false;
1438
1450
  }
@@ -2007,12 +2019,12 @@ function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {
2007
2019
  };
2008
2020
  const dispatcher = useEnvProxy ? new import_undici.EnvHttpProxyAgent(timeouts) : new import_undici.Agent(timeouts);
2009
2021
  const builtinFetch = globalThis.fetch;
2010
- const providerFetch2 = async (input, init2) => {
2011
- if (globalThis.fetch !== builtinFetch || input instanceof Request || init2?.body instanceof FormData) {
2012
- 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);
2013
2025
  }
2014
2026
  const response = await (0, import_undici.fetch)(input, {
2015
- ...init2,
2027
+ ...init,
2016
2028
  dispatcher
2017
2029
  });
2018
2030
  return response;
@@ -2020,8 +2032,8 @@ function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {
2020
2032
  return Object.assign(providerFetch2, { Response: globalThis.Response });
2021
2033
  }
2022
2034
  function withBodyActivity(fetchImpl, onActivity) {
2023
- return async (input, init2) => {
2024
- const response = await fetchImpl(input, init2);
2035
+ return async (input, init) => {
2036
+ const response = await fetchImpl(input, init);
2025
2037
  if (!response.body || !response.headers.get("content-type")?.includes("text/event-stream")) {
2026
2038
  return response;
2027
2039
  }
@@ -3336,10 +3348,6 @@ function toError2(err, provider = "openai") {
3336
3348
  return new ProviderError(provider, String(err));
3337
3349
  }
3338
3350
 
3339
- // src/providers/openai-codex.ts
3340
- var import_node_os = __toESM(require("os"), 1);
3341
- var zstd = __toESM(require("@bokuweb/zstd-wasm"), 1);
3342
-
3343
3351
  // src/utils/sse.ts
3344
3352
  function parseSseBuffer(buffer) {
3345
3353
  const events = [];
@@ -3391,6 +3399,9 @@ function extractRequestIdFromMessage(message) {
3391
3399
  return match?.[1];
3392
3400
  }
3393
3401
 
3402
+ // src/user-agent.ts
3403
+ var GG_CODER_USER_AGENT = "ggcoder (+https://github.com/KenKaiii/gg-coder)";
3404
+
3394
3405
  // src/providers/responses-stream.ts
3395
3406
  function isEncryptedReasoning(data) {
3396
3407
  return data.type === "reasoning" && typeof data.id === "string" && typeof data.encrypted_content === "string";
@@ -3438,9 +3449,11 @@ async function* consumeResponsesEvents(events, ctx) {
3438
3449
  });
3439
3450
  }
3440
3451
  if (type === "response.failed") {
3441
- const nested = event.error;
3452
+ const nested = event.error ?? event.response?.error;
3442
3453
  const message = nested?.message ?? `${ctx.label} response failed.`;
3443
3454
  const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
3455
+ const usageLimit = nested ? ctx.errorChunk?.(nested, requestId) : null;
3456
+ if (usageLimit) throw usageLimit;
3444
3457
  throw new ProviderError(ctx.provider, message, {
3445
3458
  ...requestId != null ? { requestId } : {}
3446
3459
  });
@@ -3790,6 +3803,7 @@ function toResponsesInput(messages, options) {
3790
3803
  ...sendItemId ? { id: mapId(itemId ?? callId, idMap) } : {},
3791
3804
  call_id: mapId(callId, idMap),
3792
3805
  name: part.name,
3806
+ ...options.toolNamespace ? { namespace: options.toolNamespace } : {},
3793
3807
  arguments: JSON.stringify(part.args)
3794
3808
  });
3795
3809
  }
@@ -3853,85 +3867,46 @@ function toResponsesInput(messages, options) {
3853
3867
  return { system, input };
3854
3868
  }
3855
3869
 
3856
- // src/providers/openai-codex.ts
3857
- var DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
3858
- var CODEX_CLIENT_VERSION = "0.166.0";
3859
- var CODEX_REQUEST_COMPRESSION_MIN_BYTES = 16 * 1024;
3860
- var zstdInitPromise;
3861
- async function encodeCodexRequest(body) {
3862
- const json = JSON.stringify(body);
3863
- const raw = new TextEncoder().encode(json);
3864
- if (raw.byteLength < CODEX_REQUEST_COMPRESSION_MIN_BYTES) {
3865
- return {
3866
- body: json,
3867
- compressed: false,
3868
- rawBytes: raw.byteLength,
3869
- encodedBytes: raw.byteLength
3870
- };
3871
- }
3872
- try {
3873
- zstdInitPromise ??= zstd.init();
3874
- await zstdInitPromise;
3875
- const compressed = Uint8Array.from(zstd.compress(raw));
3876
- if (compressed.byteLength >= raw.byteLength) {
3877
- return {
3878
- body: json,
3879
- compressed: false,
3880
- rawBytes: raw.byteLength,
3881
- encodedBytes: raw.byteLength
3882
- };
3883
- }
3884
- return {
3885
- body: compressed,
3886
- compressed: true,
3887
- rawBytes: raw.byteLength,
3888
- encodedBytes: compressed.byteLength
3889
- };
3890
- } catch (error) {
3891
- providerDiag("codex_request_compression_failed", {
3892
- error: error instanceof Error ? error.message : String(error),
3893
- rawBytes: raw.byteLength
3894
- });
3895
- return {
3896
- body: json,
3897
- compressed: false,
3898
- rawBytes: raw.byteLength,
3899
- encodedBytes: raw.byteLength
3900
- };
3901
- }
3902
- }
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";
3903
3874
  function usesResponsesLite(model) {
3904
3875
  return model.startsWith("gpt-5.6-") || model.startsWith("gpt-6-") || model.startsWith("gpt-6.");
3905
3876
  }
3906
- function toCodexToolChoice(choice, tools) {
3877
+ function isChatGPTPlanRoute(options) {
3878
+ return Boolean(options.accountId);
3879
+ }
3880
+ function toResponsesToolChoice(choice, tools) {
3907
3881
  const resolved = choice ?? "auto";
3908
3882
  if (typeof resolved === "object") {
3909
3883
  throw new GGAIError(
3910
- `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.`,
3911
3885
  { source: "capability" }
3912
3886
  );
3913
3887
  }
3914
3888
  if (resolved === "required" && !tools?.length) {
3915
- 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.", {
3916
3890
  source: "capability"
3917
3891
  });
3918
3892
  }
3919
3893
  return resolved;
3920
3894
  }
3921
- function streamOpenAICodex(options) {
3895
+ function streamOpenAIResponses(options) {
3922
3896
  return new StreamResult(runStream3(options), options.signal);
3923
3897
  }
3924
3898
  async function* runStream3(options, retriedWithoutReasoning = false) {
3899
+ const plan = isChatGPTPlanRoute(options);
3925
3900
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
3926
- const url = `${baseUrl}/codex/responses`;
3901
+ const url = `${baseUrl}/responses`;
3927
3902
  const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
3928
3903
  const downgraded = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
3929
- 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;
3930
3905
  const strictTools = options.strictTools ?? true;
3931
3906
  const toolSearchLoads = deferredPlan ? new Map(
3932
3907
  [...deferredPlan.loaded].map((name) => {
3933
3908
  const tool = deferredPlan.byName.get(name);
3934
- const [wire] = tool ? toCodexTools([tool], strictTools) : [];
3909
+ const [wire] = tool ? toResponsesTools([tool], strictTools) : [];
3935
3910
  return [name, { ...wire, defer_loading: true }];
3936
3911
  })
3937
3912
  ) : void 0;
@@ -3939,28 +3914,36 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
3939
3914
  const { system, input } = toResponsesInput(downgraded, {
3940
3915
  supportsImages: options.supportsImages,
3941
3916
  toolSearchLoads,
3942
- mapId: remapCodexId,
3917
+ mapId: remapCallId,
3918
+ ...plan ? { toolNamespace: CHATGPT_PLAN_TOOL_NAMESPACE } : {},
3943
3919
  // Stamped reasoning belongs to another transport (e.g. Grok's) and would
3944
- // be rejected here; Codex's own items are never stamped.
3920
+ // be rejected here; OpenAI's own items are never stamped.
3945
3921
  keepReasoning: (part) => part.origin === void 0
3946
3922
  });
3947
- const responsesLite = usesResponsesLite(options.model);
3948
- const liteShape = options.responsesLite ?? responsesLite;
3923
+ const gpt6 = usesResponsesLite(options.model);
3949
3924
  const body = {
3950
3925
  model: options.model,
3951
3926
  store: false,
3952
3927
  stream: true,
3953
3928
  instructions: system,
3954
3929
  input,
3955
- tool_choice: toCodexToolChoice(options.toolChoice, liveTools),
3956
- parallel_tool_calls: !liteShape,
3930
+ tool_choice: toResponsesToolChoice(options.toolChoice, liveTools),
3931
+ parallel_tool_calls: true,
3957
3932
  include: ["reasoning.encrypted_content"]
3958
3933
  };
3959
3934
  if (liveTools?.length) {
3960
- 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;
3961
3944
  }
3962
3945
  body.prompt_cache_key = normalizePromptCacheKey(options.promptCacheKey ?? "ggcoder");
3963
- if (options.temperature != null && !options.thinking) {
3946
+ if (!plan && options.temperature != null && !options.thinking) {
3964
3947
  body.temperature = options.temperature;
3965
3948
  }
3966
3949
  if (options.speed === "fast") {
@@ -3968,57 +3951,38 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
3968
3951
  }
3969
3952
  body.reasoning = {
3970
3953
  // GPT-5.6/6 require at least low; older models still support thinking off.
3971
- // Apply the floor here for every caller, including one-off prompt rewrites.
3972
- // `ultra` is a client orchestration preset, not a Codex API effort.
3973
- effort: options.thinking ? toCodexReasoningEffort(options.thinking, options.model) : responsesLite ? "low" : "none",
3974
- summary: "auto",
3975
- ...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"
3976
3957
  };
3977
- if (responsesLite) {
3958
+ if (gpt6) {
3978
3959
  body.text = { verbosity: "low" };
3979
3960
  }
3980
3961
  const headers = {
3981
3962
  "Content-Type": "application/json",
3982
3963
  Accept: "text/event-stream",
3983
3964
  Authorization: `Bearer ${options.apiKey}`,
3984
- "OpenAI-Beta": "responses=experimental",
3985
- originator: responsesLite ? "codex_cli_rs" : "ggcoder",
3986
- "User-Agent": responsesLite ? `codex_cli_rs/${CODEX_CLIENT_VERSION}` : `ggcoder (${import_node_os.default.platform()} ${import_node_os.default.release()}; ${import_node_os.default.arch()})`,
3987
- ...responsesLite ? { version: CODEX_CLIENT_VERSION } : {},
3988
- ...liteShape ? { "X-OpenAI-Internal-Codex-Responses-Lite": "true" } : {}
3965
+ "User-Agent": GG_CODER_USER_AGENT
3989
3966
  };
3990
- if (options.accountId) {
3991
- headers["chatgpt-account-id"] = options.accountId;
3992
- }
3993
- if (options.transportSessionId) {
3994
- const transportSessionId = normalizePromptCacheKey(options.transportSessionId);
3995
- headers["session_id"] = transportSessionId;
3996
- headers["x-client-request-id"] = transportSessionId;
3997
- }
3998
- const encodedRequest = await encodeCodexRequest(
3999
- mergeProviderOptions(body, options.providerOptions)
4000
- );
4001
- if (encodedRequest.compressed) headers["Content-Encoding"] = "zstd";
4002
- providerDiag("codex_request_body", {
4003
- rawBytes: encodedRequest.rawBytes,
4004
- encodedBytes: encodedRequest.encodedBytes,
4005
- compressed: encodedRequest.compressed
4006
- });
3967
+ const json = JSON.stringify(mergeProviderOptions(body, options.providerOptions));
3968
+ providerDiag("openai_responses_request_body", { rawBytes: json.length, plan });
4007
3969
  const response = await providerFetch(url, {
4008
3970
  method: "POST",
4009
3971
  headers,
4010
- body: encodedRequest.body,
3972
+ body: json,
4011
3973
  signal: options.signal
4012
3974
  });
4013
3975
  if (!response.ok) {
4014
3976
  const text = await response.text().catch(() => "");
4015
- const parsed = parseCodexErrorBody(text, response.status);
4016
- 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}.`;
4017
3979
  const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
4018
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(
4019
3981
  (msg) => msg.role === "assistant" && Array.isArray(msg.content) && msg.content.some((part) => part.type === "raw" && isEncryptedReasoning(part.data))
4020
3982
  )) {
4021
- providerDiag("codex_retry_without_encrypted_reasoning", { status: response.status });
3983
+ providerDiag("openai_responses_retry_without_encrypted_reasoning", {
3984
+ status: response.status
3985
+ });
4022
3986
  const messages = options.messages.map(
4023
3987
  (msg) => msg.role === "assistant" && Array.isArray(msg.content) ? {
4024
3988
  ...msg,
@@ -4029,13 +3993,19 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4029
3993
  );
4030
3994
  return yield* runStream3({ ...options, messages }, true);
4031
3995
  }
4032
- const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
4033
- 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
+ }
4034
4006
  let hint;
4035
- if (response.status === 400 && message === `The '${options.model}' model is not supported when using Codex with a ChatGPT account.`) {
4036
- hint = "This model is not available through your ChatGPT account. Choose another available model using the model selector.";
4037
- } else if (response.status === 404 && text.includes("does not exist")) {
4038
- 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.";
4039
4009
  }
4040
4010
  throw new ProviderError("openai", message, {
4041
4011
  statusCode: response.status,
@@ -4044,14 +4014,14 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
4044
4014
  });
4045
4015
  }
4046
4016
  if (!response.body) {
4047
- throw new ProviderError("openai", "No response body from Codex API");
4017
+ throw new ProviderError("openai", "No response body from OpenAI");
4048
4018
  }
4049
4019
  return yield* consumeResponsesEvents(parseSSE(response.body), {
4050
4020
  provider: "openai",
4051
- label: "Codex",
4052
- diagPrefix: "codex",
4021
+ label: "OpenAI",
4022
+ diagPrefix: "openai_responses",
4053
4023
  thinking: !!options.thinking,
4054
- errorChunk: (errorObj, requestId) => codexUsageLimitError(errorObj, void 0, requestId)
4024
+ errorChunk: (errorObj, requestId) => plan ? chatGPTPlanError(errorObj, void 0, requestId, void 0, options.model) : null
4055
4025
  });
4056
4026
  }
4057
4027
  async function* parseSSE(body) {
@@ -4064,7 +4034,7 @@ async function* parseSSE(body) {
4064
4034
  }
4065
4035
  }
4066
4036
  }
4067
- function remapCodexId(id, idMap) {
4037
+ function remapCallId(id, idMap) {
4068
4038
  const existing = idMap.get(id);
4069
4039
  if (existing) return existing;
4070
4040
  const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
@@ -4078,7 +4048,7 @@ function remapCodexId(id, idMap) {
4078
4048
  idMap.set(id, mapped);
4079
4049
  return mapped;
4080
4050
  }
4081
- function toCodexTools(tools, strictTools) {
4051
+ function toResponsesTools(tools, strictTools) {
4082
4052
  return tools.map((tool) => {
4083
4053
  let parameters = resolveToolSchema(tool);
4084
4054
  let strict = null;
@@ -4099,7 +4069,7 @@ function toCodexTools(tools, strictTools) {
4099
4069
  };
4100
4070
  });
4101
4071
  }
4102
- function parseCodexErrorBody(text, statusCode) {
4072
+ function parseErrorBody(text, statusCode) {
4103
4073
  if (!text) return {};
4104
4074
  try {
4105
4075
  const parsed = JSON.parse(text);
@@ -4123,23 +4093,70 @@ function parseCodexErrorBody(text, statusCode) {
4123
4093
  return bounded ? { message: bounded } : {};
4124
4094
  }
4125
4095
  }
4126
- var CODEX_USAGE_LIMIT_CODE = /usage_limit_reached|usage_not_included/i;
4127
- var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
4128
- function codexUsageLimitError(errorObj, statusCode, requestId) {
4096
+ function chatGPTPlanError(errorObj, statusCode, requestId, message, model) {
4129
4097
  const code = String(errorObj?.code ?? errorObj?.type ?? "");
4130
- const rateLimits = errorObj?.rate_limits;
4131
- const resetsAtRaw = (typeof errorObj?.resets_at === "number" ? errorObj.resets_at : void 0) ?? rateLimits?.primary?.resets_at ?? rateLimits?.secondary?.resets_at;
4132
- const resetsInSeconds = typeof errorObj?.resets_in_seconds === "number" ? errorObj.resets_in_seconds : void 0;
4133
- const resetsAt = typeof resetsAtRaw === "number" && resetsAtRaw > 0 ? resetsAtRaw : resetsInSeconds != null && resetsInSeconds > 0 ? Math.floor(Date.now() / 1e3) + resetsInSeconds : void 0;
4134
- const isHardUsage = CODEX_USAGE_LIMIT_CODE.test(code);
4135
- const isRateOr429 = CODEX_RATE_LIMIT_CODE.test(code) || statusCode === 429;
4136
- if (!isHardUsage && !(isRateOr429 && resetsAt != null)) return null;
4137
- return new ProviderError("openai", "ChatGPT usage limit reached", {
4138
- cause: errorObj,
4139
- statusCode: statusCode ?? 429,
4140
- ...requestId ? { requestId } : {},
4141
- ...resetsAt ? { resetsAt } : {}
4142
- });
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
+ }
4143
4160
  }
4144
4161
 
4145
4162
  // src/providers/xai-responses.ts
@@ -4273,92 +4290,17 @@ async function* synthesizeResponseEvents(response) {
4273
4290
  }
4274
4291
 
4275
4292
  // src/providers/gemini.ts
4276
- var DEFAULT_CODE_ASSIST_BASE_URL = "https://cloudcode-pa.googleapis.com";
4277
- var CODE_ASSIST_API_VERSION = "v1internal";
4278
- var GEMINI_CLI_USER_AGENT = "google-gemini-cli";
4279
- var GEMINI_CLI_API_CLIENT = "gemini-cli/0.0.0";
4280
- var CODE_ASSIST_NON_STREAMING_RETRIES = 3;
4281
- 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;
4282
4296
  var SYNTHETIC_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
4283
- var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
4284
- "gemini-3-pro-preview",
4285
- "gemini-3.1-pro-preview",
4286
- "gemini-3.1-pro-preview-customtools",
4287
- "gemini-3-flash-preview",
4288
- "gemini-3.5-flash",
4289
- "gemini-3-flash",
4290
- "gemini-3.1-flash-lite",
4291
- "gemini-3.7-flash",
4292
- "gemini-3.8-flash",
4293
- "gemini-3.5-flash-lite",
4294
- "gemini-2.5-pro",
4295
- "gemini-2.5-flash",
4296
- "gemma-4-31b-it",
4297
- "gemma-4-26b-a4b-it"
4298
- ]);
4299
- function isGeminiApiKeyRoute(options) {
4300
- return options.baseUrl?.includes("generativelanguage.googleapis.com") === true;
4301
- }
4302
- function getGoogleProject(options) {
4303
- const env = getEnvironment();
4304
- return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
4305
- }
4306
- function getCodeAssistEndpoint(method) {
4307
- const env = getEnvironment();
4308
- const endpoint = env?.CODE_ASSIST_ENDPOINT ?? DEFAULT_CODE_ASSIST_BASE_URL;
4309
- const version = env?.CODE_ASSIST_API_VERSION || CODE_ASSIST_API_VERSION;
4310
- return new URL(`${endpoint}/${version}:${method}`);
4311
- }
4312
- function formatUnsupportedModelMessage(model) {
4313
- return `Gemini OAuth is configured to use the Gemini Code Assist subscription endpoint only. That endpoint does not currently expose model "${model}".`;
4314
- }
4315
- var ACCOUNT_GATED_MODELS = /* @__PURE__ */ new Set([
4316
- "gemini-3-flash",
4317
- "gemini-3.5-flash",
4318
- "gemini-3.1-pro-preview",
4319
- "gemini-3.1-pro-preview-customtools",
4320
- "gemini-3.7-flash",
4321
- "gemini-3.8-flash",
4322
- "gemini-3.5-flash-lite"
4323
- ]);
4324
- function accountGatedMessage(model) {
4325
- if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
4326
- return `${model} is not available through Code Assist for this account. Public Gemini API availability does not guarantee Code Assist OAuth access.`;
4327
- }
4328
- 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.`;
4329
- }
4330
- function accountGatedHint(model) {
4331
- if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
4332
- return "Use /model to select Gemini 3.1 Flash Lite, or retry once Google enables this model through Code Assist for your account.";
4333
- }
4334
- 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.`;
4335
- }
4336
- function isCodeAssistLicenseRequired(status, body) {
4337
- if (status !== 403) return false;
4338
- try {
4339
- const parsed = JSON.parse(body);
4340
- const details = typeof parsed === "object" && parsed !== null && "error" in parsed ? parsed.error?.details : void 0;
4341
- return Array.isArray(details) && details.some(
4342
- (detail) => typeof detail === "object" && detail !== null && detail.reason === "SUBSCRIPTION_REQUIRED"
4343
- );
4344
- } catch {
4345
- return false;
4346
- }
4347
- }
4348
4297
  function isInvalidGeminiApiKey(status, body) {
4349
4298
  if (status !== 400 && status !== 401 && status !== 403) return false;
4350
4299
  return /"reason"\s*:\s*"API_KEY_INVALID"/.test(body) || /API key not valid/i.test(body);
4351
4300
  }
4352
4301
  var INVALID_API_KEY_MESSAGE = "Google rejected the Gemini API key: it is not valid.";
4353
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`).";
4354
- 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.";
4355
- 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.";
4356
- function formatErrorMessage(status, body, model) {
4357
- if (status === 404 && !CODE_ASSIST_SUPPORTED_MODELS.has(model)) {
4358
- return `Gemini API error (404): ${body}
4359
-
4360
- ${formatUnsupportedModelMessage(model)}`;
4361
- }
4303
+ function formatErrorMessage(status, body) {
4362
4304
  return `Gemini API error (${status}): ${body}`;
4363
4305
  }
4364
4306
  function parseRetryDelaySeconds(body) {
@@ -4564,27 +4506,15 @@ function buildGenerateRequest(options) {
4564
4506
  ...systemInstruction ? { systemInstruction } : {},
4565
4507
  ...tools ? { tools } : {},
4566
4508
  ...toolConfig ? { toolConfig } : {},
4567
- ...Object.keys(generationConfig).length > 0 ? { generationConfig } : {},
4568
- ...options.promptCacheKey ? { session_id: options.promptCacheKey } : {}
4569
- };
4570
- }
4571
- function buildCodeAssistRequest(options, request, projectId) {
4572
- return {
4573
- model: options.model,
4574
- ...projectId ? { project: projectId } : {},
4575
- user_prompt_id: crypto.randomUUID(),
4576
- request
4509
+ ...Object.keys(generationConfig).length > 0 ? { generationConfig } : {}
4577
4510
  };
4578
4511
  }
4579
4512
  var API_KEY_MODEL_IDS = {
4580
4513
  "gemini-3-flash": "gemini-3.5-flash"
4581
4514
  };
4582
- function buildApiKeyRequestPlan(options, method) {
4583
- const { session_id: _sessionId, ...request } = mergeProviderOptions(
4584
- buildGenerateRequest(options),
4585
- options.providerOptions
4586
- );
4587
- 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(/\/+$/, "");
4588
4518
  return {
4589
4519
  url: new URL(
4590
4520
  `${base}/models/${encodeURIComponent(API_KEY_MODEL_IDS[options.model] ?? options.model)}:${method}`
@@ -4593,27 +4523,7 @@ function buildApiKeyRequestPlan(options, method) {
4593
4523
  "Content-Type": "application/json",
4594
4524
  "x-goog-api-key": options.apiKey ?? ""
4595
4525
  },
4596
- body: request,
4597
- route: "api-key"
4598
- };
4599
- }
4600
- function buildRequestPlan(options, method) {
4601
- if (isGeminiApiKeyRoute(options)) return buildApiKeyRequestPlan(options, method);
4602
- if (!CODE_ASSIST_SUPPORTED_MODELS.has(options.model)) {
4603
- throw new ProviderError("gemini", formatUnsupportedModelMessage(options.model));
4604
- }
4605
- const projectId = getGoogleProject(options);
4606
- const request = mergeProviderOptions(buildGenerateRequest(options), options.providerOptions);
4607
- return {
4608
- url: getCodeAssistEndpoint(method),
4609
- headers: {
4610
- Authorization: `Bearer ${options.apiKey}`,
4611
- "Content-Type": "application/json",
4612
- "User-Agent": GEMINI_CLI_USER_AGENT,
4613
- "X-Goog-Api-Client": GEMINI_CLI_API_CLIENT
4614
- },
4615
- body: buildCodeAssistRequest(options, request, projectId),
4616
- route: "code-assist"
4526
+ body: request
4617
4527
  };
4618
4528
  }
4619
4529
  function normalizeGeminiStopReason(reason) {
@@ -4640,10 +4550,10 @@ async function* streamSse(response) {
4640
4550
  }
4641
4551
  }
4642
4552
  function candidatesFromResponse(response) {
4643
- return response.response?.candidates ?? response.candidates;
4553
+ return response.candidates;
4644
4554
  }
4645
4555
  function usageFromResponse(response) {
4646
- return response.response?.usageMetadata ?? response.usageMetadata;
4556
+ return response.usageMetadata;
4647
4557
  }
4648
4558
  function partsFromResponse(response) {
4649
4559
  return candidatesFromResponse(response)?.[0]?.content?.parts ?? [];
@@ -4665,7 +4575,7 @@ function readFunctionCallPart(part) {
4665
4575
  function makeToolCallId(index, providerId) {
4666
4576
  return providerId ?? `gemini_call_${index}_${crypto.randomUUID().replace(/-/g, "")}`;
4667
4577
  }
4668
- function shouldRetryCodeAssistStatus(status) {
4578
+ function shouldRetryStatus(status) {
4669
4579
  return status === 429 || status === 499 || status >= 500 && status <= 599;
4670
4580
  }
4671
4581
  function isAbortError(err) {
@@ -4688,7 +4598,7 @@ async function sleep(ms, signal) {
4688
4598
  if (signal?.aborted) onAbort();
4689
4599
  });
4690
4600
  }
4691
- async function fetchCodeAssist(plan, options) {
4601
+ async function fetchGemini(plan, options) {
4692
4602
  try {
4693
4603
  const response = await providerFetch(plan.url, {
4694
4604
  method: "POST",
@@ -4699,11 +4609,8 @@ async function fetchCodeAssist(plan, options) {
4699
4609
  if (!response.ok) {
4700
4610
  const text = await response.text().catch(() => "");
4701
4611
  const quota = parseGeminiQuota(response.status, text);
4702
- const codeAssist = plan.route === "code-assist";
4703
- const accountGated = codeAssist && response.status === 404 && ACCOUNT_GATED_MODELS.has(options.model);
4704
- const licenseRequired = codeAssist && isCodeAssistLicenseRequired(response.status, text);
4705
- const invalidKey = !codeAssist && isInvalidGeminiApiKey(response.status, text);
4706
- 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);
4707
4614
  let resetsAt;
4708
4615
  if (quota?.exhausted) {
4709
4616
  message = `Gemini quota exhausted \u2014 usage limit reached. ${message}`;
@@ -4713,7 +4620,7 @@ async function fetchCodeAssist(plan, options) {
4713
4620
  throw new ProviderError("gemini", message, {
4714
4621
  statusCode: response.status,
4715
4622
  ...resetsAt !== void 0 ? { resetsAt } : {},
4716
- ...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" } : {}
4717
4624
  });
4718
4625
  }
4719
4626
  return response;
@@ -4721,26 +4628,26 @@ async function fetchCodeAssist(plan, options) {
4721
4628
  throw toError3(err);
4722
4629
  }
4723
4630
  }
4724
- async function fetchCodeAssistWithRetry(plan, options) {
4631
+ async function fetchGeminiWithRetry(plan, options) {
4725
4632
  let lastError;
4726
- for (let attempt = 0; attempt <= CODE_ASSIST_NON_STREAMING_RETRIES; attempt++) {
4633
+ for (let attempt = 0; attempt <= NON_STREAMING_RETRIES; attempt++) {
4727
4634
  try {
4728
- return await fetchCodeAssist(plan, options);
4635
+ return await fetchGemini(plan, options);
4729
4636
  } catch (err) {
4730
4637
  const error = toError3(err);
4731
4638
  const statusCode = error instanceof ProviderError ? error.statusCode : void 0;
4732
- 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)) {
4733
4640
  throw error;
4734
4641
  }
4735
4642
  lastError = error;
4736
4643
  }
4737
4644
  try {
4738
- await sleep(CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS, options.signal);
4645
+ await sleep(NON_STREAMING_RETRY_DELAY_MS, options.signal);
4739
4646
  } catch (err) {
4740
4647
  throw toError3(err);
4741
4648
  }
4742
4649
  }
4743
- throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
4650
+ throw lastError ?? new ProviderError("gemini", "Gemini request failed.");
4744
4651
  }
4745
4652
  function streamGemini(options) {
4746
4653
  return new StreamResult(runStream5(options), options.signal);
@@ -4750,7 +4657,7 @@ async function* runStream5(options) {
4750
4657
  const method = useStreaming ? "streamGenerateContent" : "generateContent";
4751
4658
  const plan = buildRequestPlan(options, method);
4752
4659
  if (useStreaming) plan.url.searchParams.set("alt", "sse");
4753
- const response = useStreaming ? await fetchCodeAssist(plan, options) : await fetchCodeAssistWithRetry(plan, options);
4660
+ const response = useStreaming ? await fetchGemini(plan, options) : await fetchGeminiWithRetry(plan, options);
4754
4661
  const contentParts = [];
4755
4662
  const pendingToolCalls = [];
4756
4663
  let textAccum = "";
@@ -5061,7 +4968,7 @@ async function postJson(request) {
5061
4968
  }
5062
4969
  function httpError(provider, response, text, headerRequestId, clean) {
5063
4970
  const status = response.status;
5064
- const parsed = parseErrorBody(text);
4971
+ const parsed = parseErrorBody2(text);
5065
4972
  const rawMessage = parsed.message ?? (parsed.isJson ? "" : text.trim());
5066
4973
  const message = !rawMessage ? emptyProviderErrorMessage(status) : isRawHtmlErrorEcho(rawMessage) ? providerHtmlErrorMessage(status) : clean(rawMessage);
5067
4974
  const requestId = headerRequestId ?? parsed.requestId;
@@ -5087,7 +4994,7 @@ function httpError(provider, response, text, headerRequestId, clean) {
5087
4994
  }
5088
4995
  return new ProviderError(provider, message, base);
5089
4996
  }
5090
- function parseErrorBody(text) {
4997
+ function parseErrorBody2(text) {
5091
4998
  let parsed;
5092
4999
  try {
5093
5000
  parsed = JSON.parse(text);
@@ -5519,10 +5426,7 @@ var OPENAI_EMBED_BATCH_SIZE = 2048;
5519
5426
  var OPENROUTER_EMBED_BATCH_SIZE = 64;
5520
5427
  var LOCAL_EMBED_BATCH_SIZE = 64;
5521
5428
  var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
5522
- var GLM_CODING_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
5523
- var KIMI_CODE_USER_AGENT = `kimi-code-cli/${process.env.KIMI_CODE_VERSION ?? "1.0.11"}`;
5524
- var GROK_CLI_PROXY_HOST = "cli-chat-proxy.grok.com";
5525
- var GROK_CLI_VERSION = process.env.GROK_CLI_VERSION ?? "1.0.67";
5429
+ var GLM_BASE_URL = "https://api.z.ai/api/paas/v4";
5526
5430
  providerRegistry.register("anthropic", {
5527
5431
  stream: (options) => streamAnthropic(options)
5528
5432
  });
@@ -5535,12 +5439,12 @@ providerRegistry.register("xiaomi", {
5535
5439
  });
5536
5440
  providerRegistry.register("openai", {
5537
5441
  stream: (options) => {
5538
- if (options.accountId) {
5539
- return streamOpenAICodex(options);
5442
+ if (options.accountId || isOpenAIHost(options.baseUrl)) {
5443
+ return streamOpenAIResponses(options);
5540
5444
  }
5541
5445
  return streamOpenAI(options);
5542
5446
  },
5543
- // 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.
5544
5448
  // OpenAI has no task/input-type parameter, so `inputType` is ignored.
5545
5449
  embed: (request) => embedOpenAICompatible(request, {
5546
5450
  provider: "openai",
@@ -5550,22 +5454,24 @@ providerRegistry.register("openai", {
5550
5454
  });
5551
5455
  providerRegistry.register("gemini", {
5552
5456
  stream: (options) => streamGemini(options),
5553
- // Native Gemini API with an AI Studio key — not the Code Assist OAuth endpoint above.
5457
+ // Native Gemini API with an AI Studio key.
5554
5458
  embed: (request) => embedGemini(request),
5555
5459
  embedBatchSize: GEMINI_EMBED_BATCH_SIZE
5556
5460
  });
5557
5461
  providerRegistry.register("glm", {
5558
5462
  stream: (options) => streamOpenAI({
5559
5463
  ...options,
5560
- baseUrl: options.baseUrl ?? GLM_CODING_BASE_URL
5464
+ baseUrl: options.baseUrl ?? GLM_BASE_URL
5561
5465
  })
5562
5466
  });
5563
5467
  providerRegistry.register("moonshot", {
5564
- stream: (options) => {
5565
- const baseUrl = options.baseUrl ?? "https://api.moonshot.ai/v1";
5566
- const defaultHeaders = baseUrl.includes("api.kimi.com") ? { "User-Agent": KIMI_CODE_USER_AGENT, ...options.defaultHeaders } : options.defaultHeaders;
5567
- return streamOpenAI({ ...options, baseUrl, defaultHeaders });
5568
- }
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
+ })
5569
5475
  });
5570
5476
  providerRegistry.register("deepseek", {
5571
5477
  stream: (options) => streamOpenAI({
@@ -5614,27 +5520,10 @@ providerRegistry.register("sakana", {
5614
5520
  })
5615
5521
  });
5616
5522
  providerRegistry.register("xai", {
5617
- // Both Grok routes ride xAI's Responses API (see providers/xai-responses.ts):
5618
- // Chat Completions is deprecated for Grok reasoning models and can't carry
5619
- // the encrypted reasoning that keeps long tool loops coherent.
5620
- //
5621
- // Subscription OAuth (SuperGrok / X Premium) routes to the Grok CLI chat proxy
5622
- // instead, which serves the same `/v1/responses` surface but gates on Grok-CLI
5623
- // client identity. Inject those headers centrally here — exactly as the Kimi
5624
- // endpoint above — so EVERY stream (agent loop, compaction, title-gen,
5625
- // sub-agents) is accepted rather than depending on each call site to thread
5626
- // headers. Caller-provided headers still win on collision.
5627
- stream: (options) => {
5628
- const baseUrl = options.baseUrl ?? "https://api.x.ai/v1";
5629
- const defaultHeaders = baseUrl.includes(GROK_CLI_PROXY_HOST) ? {
5630
- "X-XAI-Token-Auth": "xai-grok-cli",
5631
- "x-grok-client-version": GROK_CLI_VERSION,
5632
- "x-grok-client-identifier": "ggcoder",
5633
- "x-grok-model-override": options.model,
5634
- ...options.defaultHeaders
5635
- } : options.defaultHeaders;
5636
- return streamXaiResponses({ ...options, baseUrl, defaultHeaders });
5637
- }
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" })
5638
5527
  });
5639
5528
  providerRegistry.register("minimax", {
5640
5529
  stream: (options) => streamAnthropic({
@@ -5685,6 +5574,14 @@ providerRegistry.register("local", {
5685
5574
  }
5686
5575
  )
5687
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
+ }
5688
5585
  function requireLocalBaseUrl(baseUrl) {
5689
5586
  if (!baseUrl) {
5690
5587
  throw new GGAIError(
@@ -5875,16 +5772,16 @@ function l2Normalize(vector) {
5875
5772
 
5876
5773
  // src/utils/preconnect.ts
5877
5774
  var PRECONNECT_INTERVAL_MS = 3e4;
5878
- function defaultBaseUrl(provider, accountId) {
5775
+ function defaultBaseUrl(provider) {
5879
5776
  switch (provider) {
5880
5777
  case "anthropic":
5881
5778
  return "https://api.anthropic.com";
5882
5779
  case "openai":
5883
- return accountId ? "https://chatgpt.com/backend-api" : "https://api.openai.com/v1";
5780
+ return "https://api.openai.com/v1";
5884
5781
  case "gemini":
5885
- return "https://cloudcode-pa.googleapis.com";
5782
+ return "https://generativelanguage.googleapis.com/v1beta";
5886
5783
  case "glm":
5887
- return "https://api.z.ai/api/coding/paas/v4";
5784
+ return "https://api.z.ai/api/paas/v4";
5888
5785
  case "moonshot":
5889
5786
  return "https://api.moonshot.ai/v1";
5890
5787
  case "deepseek":
@@ -5917,7 +5814,7 @@ function isLocalHost(hostname) {
5917
5814
  return a === 127 || a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31;
5918
5815
  }
5919
5816
  function preconnectTarget(options) {
5920
- const raw = options.baseUrl ?? defaultBaseUrl(options.provider, options.accountId);
5817
+ const raw = options.baseUrl ?? defaultBaseUrl(options.provider);
5921
5818
  if (!raw || options.provider === "local" || options.provider === "palsu") return void 0;
5922
5819
  let url;
5923
5820
  try {
@@ -6432,10 +6329,10 @@ function palsuRerankScore(query, document) {
6432
6329
  }
6433
6330
  // Annotate the CommonJS export names for ESM import in node:
6434
6331
  0 && (module.exports = {
6435
- CODEX_CLIENT_VERSION,
6436
6332
  EventStream,
6437
6333
  FAST_MODE_SUFFIX,
6438
6334
  GGAIError,
6335
+ GG_CODER_USER_AGENT,
6439
6336
  LOCAL_NO_API_KEY,
6440
6337
  PRECONNECT_INTERVAL_MS,
6441
6338
  ProviderError,