@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/README.md +16 -16
- package/dist/index.cjs +211 -314
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -21
- package/dist/index.d.ts +17 -21
- package/dist/index.js +210 -313
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -243,6 +243,18 @@ function formatError(err) {
|
|
|
243
243
|
}
|
|
244
244
|
const miniMax = err.provider === "minimax" ? formatMiniMaxError(err, cleanMessage) : void 0;
|
|
245
245
|
if (miniMax) return miniMax;
|
|
246
|
+
if (isUsageLimitError(err) && err.provider === "openai" && /chatgpt/i.test(err.message)) {
|
|
247
|
+
return {
|
|
248
|
+
headline: "ChatGPT plan usage limit reached.",
|
|
249
|
+
source: "provider",
|
|
250
|
+
message: "GG Coder has used what your ChatGPT plan allows for now. That can be your plan's limit or one you set for GG Coder.",
|
|
251
|
+
provider: err.provider,
|
|
252
|
+
statusCode: err.statusCode,
|
|
253
|
+
...err.requestId ? { requestId: err.requestId } : {},
|
|
254
|
+
...err.resetsAt ? { resetsAt: err.resetsAt } : {},
|
|
255
|
+
guidance: err.hint ?? "Check or raise it at chatgpt.com/settings/usage, or add an OpenAI API key to keep going."
|
|
256
|
+
};
|
|
257
|
+
}
|
|
246
258
|
if (isUsageLimitError(err)) {
|
|
247
259
|
const resetClause = err.resetsAt ? ` It resets at ${formatResetTime(err.resetsAt)}.` : "";
|
|
248
260
|
return {
|
|
@@ -1143,7 +1155,7 @@ function toolCallNameRuleFor(provider, options) {
|
|
|
1143
1155
|
return TOOL_CALL_NAME_RULES.anthropic;
|
|
1144
1156
|
case "openai":
|
|
1145
1157
|
if (options?.accountId) return TOOL_CALL_NAME_RULES["openai-responses"];
|
|
1146
|
-
return isDefaultOrHost(options?.baseUrl, "api.openai.com") ? TOOL_CALL_NAME_RULES["openai-
|
|
1158
|
+
return isDefaultOrHost(options?.baseUrl, "api.openai.com") ? TOOL_CALL_NAME_RULES["openai-responses"] : TOOL_CALL_NAME_RULES.generic;
|
|
1147
1159
|
case "gemini":
|
|
1148
1160
|
return TOOL_CALL_NAME_RULES.gemini;
|
|
1149
1161
|
default:
|
|
@@ -1341,8 +1353,8 @@ function usesDeferredToolLoading(options) {
|
|
|
1341
1353
|
const firstParty = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
|
|
1342
1354
|
return firstParty && supportsAnthropicDeferredTools(options.model);
|
|
1343
1355
|
}
|
|
1344
|
-
if (options.provider === "openai" && options.accountId) {
|
|
1345
|
-
return supportsResponsesToolSearch(options.model);
|
|
1356
|
+
if (options.provider === "openai" && !options.accountId) {
|
|
1357
|
+
return isDefaultOrHost(options.baseUrl, "api.openai.com") && supportsResponsesToolSearch(options.model);
|
|
1346
1358
|
}
|
|
1347
1359
|
return false;
|
|
1348
1360
|
}
|
|
@@ -1917,12 +1929,12 @@ function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {
|
|
|
1917
1929
|
};
|
|
1918
1930
|
const dispatcher = useEnvProxy ? new EnvHttpProxyAgent(timeouts) : new Agent(timeouts);
|
|
1919
1931
|
const builtinFetch = globalThis.fetch;
|
|
1920
|
-
const providerFetch2 = async (input,
|
|
1921
|
-
if (globalThis.fetch !== builtinFetch || input instanceof Request ||
|
|
1922
|
-
return globalThis.fetch(input,
|
|
1932
|
+
const providerFetch2 = async (input, init) => {
|
|
1933
|
+
if (globalThis.fetch !== builtinFetch || input instanceof Request || init?.body instanceof FormData) {
|
|
1934
|
+
return globalThis.fetch(input, init);
|
|
1923
1935
|
}
|
|
1924
1936
|
const response = await undiciFetch(input, {
|
|
1925
|
-
...
|
|
1937
|
+
...init,
|
|
1926
1938
|
dispatcher
|
|
1927
1939
|
});
|
|
1928
1940
|
return response;
|
|
@@ -1930,8 +1942,8 @@ function createProviderFetch(timeoutMs, { useEnvProxy = nodeUsesEnvProxy() } = {
|
|
|
1930
1942
|
return Object.assign(providerFetch2, { Response: globalThis.Response });
|
|
1931
1943
|
}
|
|
1932
1944
|
function withBodyActivity(fetchImpl, onActivity) {
|
|
1933
|
-
return async (input,
|
|
1934
|
-
const response = await fetchImpl(input,
|
|
1945
|
+
return async (input, init) => {
|
|
1946
|
+
const response = await fetchImpl(input, init);
|
|
1935
1947
|
if (!response.body || !response.headers.get("content-type")?.includes("text/event-stream")) {
|
|
1936
1948
|
return response;
|
|
1937
1949
|
}
|
|
@@ -3246,10 +3258,6 @@ function toError2(err, provider = "openai") {
|
|
|
3246
3258
|
return new ProviderError(provider, String(err));
|
|
3247
3259
|
}
|
|
3248
3260
|
|
|
3249
|
-
// src/providers/openai-codex.ts
|
|
3250
|
-
import os from "os";
|
|
3251
|
-
import * as zstd from "@bokuweb/zstd-wasm";
|
|
3252
|
-
|
|
3253
3261
|
// src/utils/sse.ts
|
|
3254
3262
|
function parseSseBuffer(buffer) {
|
|
3255
3263
|
const events = [];
|
|
@@ -3301,6 +3309,9 @@ function extractRequestIdFromMessage(message) {
|
|
|
3301
3309
|
return match?.[1];
|
|
3302
3310
|
}
|
|
3303
3311
|
|
|
3312
|
+
// src/user-agent.ts
|
|
3313
|
+
var GG_CODER_USER_AGENT = "ggcoder (+https://github.com/KenKaiii/gg-coder)";
|
|
3314
|
+
|
|
3304
3315
|
// src/providers/responses-stream.ts
|
|
3305
3316
|
function isEncryptedReasoning(data) {
|
|
3306
3317
|
return data.type === "reasoning" && typeof data.id === "string" && typeof data.encrypted_content === "string";
|
|
@@ -3348,9 +3359,11 @@ async function* consumeResponsesEvents(events, ctx) {
|
|
|
3348
3359
|
});
|
|
3349
3360
|
}
|
|
3350
3361
|
if (type === "response.failed") {
|
|
3351
|
-
const nested = event.error;
|
|
3362
|
+
const nested = event.error ?? event.response?.error;
|
|
3352
3363
|
const message = nested?.message ?? `${ctx.label} response failed.`;
|
|
3353
3364
|
const requestId = extractRequestIdFromMessage(message) ?? event.request_id;
|
|
3365
|
+
const usageLimit = nested ? ctx.errorChunk?.(nested, requestId) : null;
|
|
3366
|
+
if (usageLimit) throw usageLimit;
|
|
3354
3367
|
throw new ProviderError(ctx.provider, message, {
|
|
3355
3368
|
...requestId != null ? { requestId } : {}
|
|
3356
3369
|
});
|
|
@@ -3700,6 +3713,7 @@ function toResponsesInput(messages, options) {
|
|
|
3700
3713
|
...sendItemId ? { id: mapId(itemId ?? callId, idMap) } : {},
|
|
3701
3714
|
call_id: mapId(callId, idMap),
|
|
3702
3715
|
name: part.name,
|
|
3716
|
+
...options.toolNamespace ? { namespace: options.toolNamespace } : {},
|
|
3703
3717
|
arguments: JSON.stringify(part.args)
|
|
3704
3718
|
});
|
|
3705
3719
|
}
|
|
@@ -3763,85 +3777,46 @@ function toResponsesInput(messages, options) {
|
|
|
3763
3777
|
return { system, input };
|
|
3764
3778
|
}
|
|
3765
3779
|
|
|
3766
|
-
// src/providers/openai-
|
|
3767
|
-
var DEFAULT_BASE_URL = "https://
|
|
3768
|
-
var
|
|
3769
|
-
var
|
|
3770
|
-
var zstdInitPromise;
|
|
3771
|
-
async function encodeCodexRequest(body) {
|
|
3772
|
-
const json = JSON.stringify(body);
|
|
3773
|
-
const raw = new TextEncoder().encode(json);
|
|
3774
|
-
if (raw.byteLength < CODEX_REQUEST_COMPRESSION_MIN_BYTES) {
|
|
3775
|
-
return {
|
|
3776
|
-
body: json,
|
|
3777
|
-
compressed: false,
|
|
3778
|
-
rawBytes: raw.byteLength,
|
|
3779
|
-
encodedBytes: raw.byteLength
|
|
3780
|
-
};
|
|
3781
|
-
}
|
|
3782
|
-
try {
|
|
3783
|
-
zstdInitPromise ??= zstd.init();
|
|
3784
|
-
await zstdInitPromise;
|
|
3785
|
-
const compressed = Uint8Array.from(zstd.compress(raw));
|
|
3786
|
-
if (compressed.byteLength >= raw.byteLength) {
|
|
3787
|
-
return {
|
|
3788
|
-
body: json,
|
|
3789
|
-
compressed: false,
|
|
3790
|
-
rawBytes: raw.byteLength,
|
|
3791
|
-
encodedBytes: raw.byteLength
|
|
3792
|
-
};
|
|
3793
|
-
}
|
|
3794
|
-
return {
|
|
3795
|
-
body: compressed,
|
|
3796
|
-
compressed: true,
|
|
3797
|
-
rawBytes: raw.byteLength,
|
|
3798
|
-
encodedBytes: compressed.byteLength
|
|
3799
|
-
};
|
|
3800
|
-
} catch (error) {
|
|
3801
|
-
providerDiag("codex_request_compression_failed", {
|
|
3802
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3803
|
-
rawBytes: raw.byteLength
|
|
3804
|
-
});
|
|
3805
|
-
return {
|
|
3806
|
-
body: json,
|
|
3807
|
-
compressed: false,
|
|
3808
|
-
rawBytes: raw.byteLength,
|
|
3809
|
-
encodedBytes: raw.byteLength
|
|
3810
|
-
};
|
|
3811
|
-
}
|
|
3812
|
-
}
|
|
3780
|
+
// src/providers/openai-responses.ts
|
|
3781
|
+
var DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
3782
|
+
var CHATGPT_PLAN_TOOL_NAMESPACE = "gg";
|
|
3783
|
+
var CHATGPT_USAGE_URL = "https://chatgpt.com/settings/usage";
|
|
3813
3784
|
function usesResponsesLite(model) {
|
|
3814
3785
|
return model.startsWith("gpt-5.6-") || model.startsWith("gpt-6-") || model.startsWith("gpt-6.");
|
|
3815
3786
|
}
|
|
3816
|
-
function
|
|
3787
|
+
function isChatGPTPlanRoute(options) {
|
|
3788
|
+
return Boolean(options.accountId);
|
|
3789
|
+
}
|
|
3790
|
+
function toResponsesToolChoice(choice, tools) {
|
|
3817
3791
|
const resolved = choice ?? "auto";
|
|
3818
3792
|
if (typeof resolved === "object") {
|
|
3819
3793
|
throw new GGAIError(
|
|
3820
|
-
`OpenAI
|
|
3794
|
+
`OpenAI Responses does not support selecting the named tool \`${resolved.name}\`; use auto, none, or required.`,
|
|
3821
3795
|
{ source: "capability" }
|
|
3822
3796
|
);
|
|
3823
3797
|
}
|
|
3824
3798
|
if (resolved === "required" && !tools?.length) {
|
|
3825
|
-
throw new GGAIError("OpenAI
|
|
3799
|
+
throw new GGAIError("OpenAI cannot require a tool call when no tools are configured.", {
|
|
3826
3800
|
source: "capability"
|
|
3827
3801
|
});
|
|
3828
3802
|
}
|
|
3829
3803
|
return resolved;
|
|
3830
3804
|
}
|
|
3831
|
-
function
|
|
3805
|
+
function streamOpenAIResponses(options) {
|
|
3832
3806
|
return new StreamResult(runStream3(options), options.signal);
|
|
3833
3807
|
}
|
|
3834
3808
|
async function* runStream3(options, retriedWithoutReasoning = false) {
|
|
3809
|
+
const plan = isChatGPTPlanRoute(options);
|
|
3835
3810
|
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
3836
|
-
const url = `${baseUrl}/
|
|
3811
|
+
const url = `${baseUrl}/responses`;
|
|
3837
3812
|
const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
3838
3813
|
const downgraded = downgradeUnsupportedVideos(downgradedImages, options.supportsVideo);
|
|
3839
|
-
const deferredPlan = supportsResponsesToolSearch(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgraded) : void 0;
|
|
3814
|
+
const deferredPlan = !plan && supportsResponsesToolSearch(options.model) ? planDeferredTools(options.tools, options.deferredTools, downgraded) : void 0;
|
|
3840
3815
|
const strictTools = options.strictTools ?? true;
|
|
3841
3816
|
const toolSearchLoads = deferredPlan ? new Map(
|
|
3842
3817
|
[...deferredPlan.loaded].map((name) => {
|
|
3843
3818
|
const tool = deferredPlan.byName.get(name);
|
|
3844
|
-
const [wire] = tool ?
|
|
3819
|
+
const [wire] = tool ? toResponsesTools([tool], strictTools) : [];
|
|
3845
3820
|
return [name, { ...wire, defer_loading: true }];
|
|
3846
3821
|
})
|
|
3847
3822
|
) : void 0;
|
|
@@ -3849,28 +3824,36 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
|
|
|
3849
3824
|
const { system, input } = toResponsesInput(downgraded, {
|
|
3850
3825
|
supportsImages: options.supportsImages,
|
|
3851
3826
|
toolSearchLoads,
|
|
3852
|
-
mapId:
|
|
3827
|
+
mapId: remapCallId,
|
|
3828
|
+
...plan ? { toolNamespace: CHATGPT_PLAN_TOOL_NAMESPACE } : {},
|
|
3853
3829
|
// Stamped reasoning belongs to another transport (e.g. Grok's) and would
|
|
3854
|
-
// be rejected here;
|
|
3830
|
+
// be rejected here; OpenAI's own items are never stamped.
|
|
3855
3831
|
keepReasoning: (part) => part.origin === void 0
|
|
3856
3832
|
});
|
|
3857
|
-
const
|
|
3858
|
-
const liteShape = options.responsesLite ?? responsesLite;
|
|
3833
|
+
const gpt6 = usesResponsesLite(options.model);
|
|
3859
3834
|
const body = {
|
|
3860
3835
|
model: options.model,
|
|
3861
3836
|
store: false,
|
|
3862
3837
|
stream: true,
|
|
3863
3838
|
instructions: system,
|
|
3864
3839
|
input,
|
|
3865
|
-
tool_choice:
|
|
3866
|
-
parallel_tool_calls:
|
|
3840
|
+
tool_choice: toResponsesToolChoice(options.toolChoice, liveTools),
|
|
3841
|
+
parallel_tool_calls: true,
|
|
3867
3842
|
include: ["reasoning.encrypted_content"]
|
|
3868
3843
|
};
|
|
3869
3844
|
if (liveTools?.length) {
|
|
3870
|
-
|
|
3845
|
+
const wire = toResponsesTools(liveTools, strictTools);
|
|
3846
|
+
body.tools = plan ? [
|
|
3847
|
+
{
|
|
3848
|
+
type: "namespace",
|
|
3849
|
+
name: CHATGPT_PLAN_TOOL_NAMESPACE,
|
|
3850
|
+
description: "GG Coder's tools for working in the user's project.",
|
|
3851
|
+
tools: wire
|
|
3852
|
+
}
|
|
3853
|
+
] : wire;
|
|
3871
3854
|
}
|
|
3872
3855
|
body.prompt_cache_key = normalizePromptCacheKey(options.promptCacheKey ?? "ggcoder");
|
|
3873
|
-
if (options.temperature != null && !options.thinking) {
|
|
3856
|
+
if (!plan && options.temperature != null && !options.thinking) {
|
|
3874
3857
|
body.temperature = options.temperature;
|
|
3875
3858
|
}
|
|
3876
3859
|
if (options.speed === "fast") {
|
|
@@ -3878,57 +3861,38 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
|
|
|
3878
3861
|
}
|
|
3879
3862
|
body.reasoning = {
|
|
3880
3863
|
// GPT-5.6/6 require at least low; older models still support thinking off.
|
|
3881
|
-
//
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
summary: "auto",
|
|
3885
|
-
...liteShape ? { context: "all_turns" } : {}
|
|
3864
|
+
// `ultra` is a client orchestration preset, not an API effort.
|
|
3865
|
+
effort: options.thinking ? toCodexReasoningEffort(options.thinking, options.model) : gpt6 ? "low" : "none",
|
|
3866
|
+
summary: "auto"
|
|
3886
3867
|
};
|
|
3887
|
-
if (
|
|
3868
|
+
if (gpt6) {
|
|
3888
3869
|
body.text = { verbosity: "low" };
|
|
3889
3870
|
}
|
|
3890
3871
|
const headers = {
|
|
3891
3872
|
"Content-Type": "application/json",
|
|
3892
3873
|
Accept: "text/event-stream",
|
|
3893
3874
|
Authorization: `Bearer ${options.apiKey}`,
|
|
3894
|
-
"
|
|
3895
|
-
originator: responsesLite ? "codex_cli_rs" : "ggcoder",
|
|
3896
|
-
"User-Agent": responsesLite ? `codex_cli_rs/${CODEX_CLIENT_VERSION}` : `ggcoder (${os.platform()} ${os.release()}; ${os.arch()})`,
|
|
3897
|
-
...responsesLite ? { version: CODEX_CLIENT_VERSION } : {},
|
|
3898
|
-
...liteShape ? { "X-OpenAI-Internal-Codex-Responses-Lite": "true" } : {}
|
|
3875
|
+
"User-Agent": GG_CODER_USER_AGENT
|
|
3899
3876
|
};
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
}
|
|
3903
|
-
if (options.transportSessionId) {
|
|
3904
|
-
const transportSessionId = normalizePromptCacheKey(options.transportSessionId);
|
|
3905
|
-
headers["session_id"] = transportSessionId;
|
|
3906
|
-
headers["x-client-request-id"] = transportSessionId;
|
|
3907
|
-
}
|
|
3908
|
-
const encodedRequest = await encodeCodexRequest(
|
|
3909
|
-
mergeProviderOptions(body, options.providerOptions)
|
|
3910
|
-
);
|
|
3911
|
-
if (encodedRequest.compressed) headers["Content-Encoding"] = "zstd";
|
|
3912
|
-
providerDiag("codex_request_body", {
|
|
3913
|
-
rawBytes: encodedRequest.rawBytes,
|
|
3914
|
-
encodedBytes: encodedRequest.encodedBytes,
|
|
3915
|
-
compressed: encodedRequest.compressed
|
|
3916
|
-
});
|
|
3877
|
+
const json = JSON.stringify(mergeProviderOptions(body, options.providerOptions));
|
|
3878
|
+
providerDiag("openai_responses_request_body", { rawBytes: json.length, plan });
|
|
3917
3879
|
const response = await providerFetch(url, {
|
|
3918
3880
|
method: "POST",
|
|
3919
3881
|
headers,
|
|
3920
|
-
body:
|
|
3882
|
+
body: json,
|
|
3921
3883
|
signal: options.signal
|
|
3922
3884
|
});
|
|
3923
3885
|
if (!response.ok) {
|
|
3924
3886
|
const text = await response.text().catch(() => "");
|
|
3925
|
-
const parsed =
|
|
3926
|
-
const message = parsed.message ?? `
|
|
3887
|
+
const parsed = parseErrorBody(text, response.status);
|
|
3888
|
+
const message = parsed.message ?? `OpenAI returned HTTP ${response.status}.`;
|
|
3927
3889
|
const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
|
|
3928
3890
|
if (!retriedWithoutReasoning && !options.signal?.aborted && response.status === 400 && (parsed.errorObj?.code === "invalid_encrypted_content" || /encrypted content.*could not be (?:verified|decrypted|parsed)/i.test(message)) && options.messages.some(
|
|
3929
3891
|
(msg) => msg.role === "assistant" && Array.isArray(msg.content) && msg.content.some((part) => part.type === "raw" && isEncryptedReasoning(part.data))
|
|
3930
3892
|
)) {
|
|
3931
|
-
providerDiag("
|
|
3893
|
+
providerDiag("openai_responses_retry_without_encrypted_reasoning", {
|
|
3894
|
+
status: response.status
|
|
3895
|
+
});
|
|
3932
3896
|
const messages = options.messages.map(
|
|
3933
3897
|
(msg) => msg.role === "assistant" && Array.isArray(msg.content) ? {
|
|
3934
3898
|
...msg,
|
|
@@ -3939,13 +3903,19 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
|
|
|
3939
3903
|
);
|
|
3940
3904
|
return yield* runStream3({ ...options, messages }, true);
|
|
3941
3905
|
}
|
|
3942
|
-
|
|
3943
|
-
|
|
3906
|
+
if (plan) {
|
|
3907
|
+
const planError = chatGPTPlanError(
|
|
3908
|
+
parsed.errorObj,
|
|
3909
|
+
response.status,
|
|
3910
|
+
requestId,
|
|
3911
|
+
message,
|
|
3912
|
+
options.model
|
|
3913
|
+
);
|
|
3914
|
+
if (planError) throw planError;
|
|
3915
|
+
}
|
|
3944
3916
|
let hint;
|
|
3945
|
-
if (response.status ===
|
|
3946
|
-
hint = "This model
|
|
3947
|
-
} else if (response.status === 404 && text.includes("does not exist")) {
|
|
3948
|
-
hint = "This model is not in OpenAI's current catalog for your ChatGPT account. Choose another available model using the model selector.";
|
|
3917
|
+
if (response.status === 404 && text.includes("does not exist")) {
|
|
3918
|
+
hint = plan ? "This model isn't available on your ChatGPT plan. Choose another model using the model selector." : "This model isn't available to your OpenAI API key. Choose another model using the model selector.";
|
|
3949
3919
|
}
|
|
3950
3920
|
throw new ProviderError("openai", message, {
|
|
3951
3921
|
statusCode: response.status,
|
|
@@ -3954,14 +3924,14 @@ async function* runStream3(options, retriedWithoutReasoning = false) {
|
|
|
3954
3924
|
});
|
|
3955
3925
|
}
|
|
3956
3926
|
if (!response.body) {
|
|
3957
|
-
throw new ProviderError("openai", "No response body from
|
|
3927
|
+
throw new ProviderError("openai", "No response body from OpenAI");
|
|
3958
3928
|
}
|
|
3959
3929
|
return yield* consumeResponsesEvents(parseSSE(response.body), {
|
|
3960
3930
|
provider: "openai",
|
|
3961
|
-
label: "
|
|
3962
|
-
diagPrefix: "
|
|
3931
|
+
label: "OpenAI",
|
|
3932
|
+
diagPrefix: "openai_responses",
|
|
3963
3933
|
thinking: !!options.thinking,
|
|
3964
|
-
errorChunk: (errorObj, requestId) =>
|
|
3934
|
+
errorChunk: (errorObj, requestId) => plan ? chatGPTPlanError(errorObj, void 0, requestId, void 0, options.model) : null
|
|
3965
3935
|
});
|
|
3966
3936
|
}
|
|
3967
3937
|
async function* parseSSE(body) {
|
|
@@ -3974,7 +3944,7 @@ async function* parseSSE(body) {
|
|
|
3974
3944
|
}
|
|
3975
3945
|
}
|
|
3976
3946
|
}
|
|
3977
|
-
function
|
|
3947
|
+
function remapCallId(id, idMap) {
|
|
3978
3948
|
const existing = idMap.get(id);
|
|
3979
3949
|
if (existing) return existing;
|
|
3980
3950
|
const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
|
|
@@ -3988,7 +3958,7 @@ function remapCodexId(id, idMap) {
|
|
|
3988
3958
|
idMap.set(id, mapped);
|
|
3989
3959
|
return mapped;
|
|
3990
3960
|
}
|
|
3991
|
-
function
|
|
3961
|
+
function toResponsesTools(tools, strictTools) {
|
|
3992
3962
|
return tools.map((tool) => {
|
|
3993
3963
|
let parameters = resolveToolSchema(tool);
|
|
3994
3964
|
let strict = null;
|
|
@@ -4009,7 +3979,7 @@ function toCodexTools(tools, strictTools) {
|
|
|
4009
3979
|
};
|
|
4010
3980
|
});
|
|
4011
3981
|
}
|
|
4012
|
-
function
|
|
3982
|
+
function parseErrorBody(text, statusCode) {
|
|
4013
3983
|
if (!text) return {};
|
|
4014
3984
|
try {
|
|
4015
3985
|
const parsed = JSON.parse(text);
|
|
@@ -4033,23 +4003,70 @@ function parseCodexErrorBody(text, statusCode) {
|
|
|
4033
4003
|
return bounded ? { message: bounded } : {};
|
|
4034
4004
|
}
|
|
4035
4005
|
}
|
|
4036
|
-
|
|
4037
|
-
var CODEX_RATE_LIMIT_CODE = /rate_limit_exceeded/i;
|
|
4038
|
-
function codexUsageLimitError(errorObj, statusCode, requestId) {
|
|
4006
|
+
function chatGPTPlanError(errorObj, statusCode, requestId, message, model) {
|
|
4039
4007
|
const code = String(errorObj?.code ?? errorObj?.type ?? "");
|
|
4040
|
-
const
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4008
|
+
const base = { ...requestId ? { requestId } : {}, cause: errorObj };
|
|
4009
|
+
switch (code) {
|
|
4010
|
+
// The plan's usage — or the limit the user set for GG Coder — is spent, or
|
|
4011
|
+
// can't be checked right now. Both stop plan requests; the canonical
|
|
4012
|
+
// "usage limit reached" message lets the session fall back to an API key.
|
|
4013
|
+
case "subscription_sharing_usage_limit_exceeded":
|
|
4014
|
+
case "subscription_sharing_usage_unavailable":
|
|
4015
|
+
return new ProviderError("openai", "ChatGPT usage limit reached", {
|
|
4016
|
+
...base,
|
|
4017
|
+
statusCode: statusCode ?? (code.endsWith("unavailable") ? 503 : 429),
|
|
4018
|
+
hint: code === "subscription_sharing_usage_unavailable" ? "OpenAI couldn't check your ChatGPT plan usage just now. Try again shortly, or add an OpenAI API key to keep going." : `Check or raise what GG Coder may use at ${CHATGPT_USAGE_URL}, or add an OpenAI API key to keep going.`
|
|
4019
|
+
});
|
|
4020
|
+
case "subscription_sharing_user_not_eligible":
|
|
4021
|
+
return new ProviderError(
|
|
4022
|
+
"openai",
|
|
4023
|
+
"ChatGPT plan use isn't available for this account or workspace.",
|
|
4024
|
+
{
|
|
4025
|
+
...base,
|
|
4026
|
+
statusCode: statusCode ?? 403,
|
|
4027
|
+
hint: "Add an OpenAI API key, or ask your workspace admin to allow ChatGPT plan use in other apps."
|
|
4028
|
+
}
|
|
4029
|
+
);
|
|
4030
|
+
// The account's plan doesn't include this model (live: 400 model_not_found,
|
|
4031
|
+
// "…not supported when using Codex with a ChatGPT account").
|
|
4032
|
+
case "model_not_found":
|
|
4033
|
+
return new ProviderError(
|
|
4034
|
+
"openai",
|
|
4035
|
+
`${model ?? "This model"} isn't included in your ChatGPT plan.`,
|
|
4036
|
+
{
|
|
4037
|
+
...base,
|
|
4038
|
+
statusCode: statusCode ?? 400,
|
|
4039
|
+
hint: "Pick a model your plan includes (the model picker marks the others), or use an OpenAI API key for this one."
|
|
4040
|
+
}
|
|
4041
|
+
);
|
|
4042
|
+
case "subscription_sharing_unsupported_capability": {
|
|
4043
|
+
const param = typeof errorObj?.param === "string" ? errorObj.param : void 0;
|
|
4044
|
+
if (param?.includes("service_tier")) {
|
|
4045
|
+
return new ProviderError("openai", "Fast mode isn't included in your ChatGPT plan.", {
|
|
4046
|
+
...base,
|
|
4047
|
+
statusCode: statusCode ?? 400,
|
|
4048
|
+
hint: "Switch to the standard model, or use an OpenAI API key for Fast mode."
|
|
4049
|
+
});
|
|
4050
|
+
}
|
|
4051
|
+
return new ProviderError(
|
|
4052
|
+
"openai",
|
|
4053
|
+
message ?? `The ChatGPT plan route doesn't support ${param ?? "part of this request"}.`,
|
|
4054
|
+
{
|
|
4055
|
+
...base,
|
|
4056
|
+
statusCode: statusCode ?? 400,
|
|
4057
|
+
hint: `${param ? `\`${param}\` isn't` : "Part of this request isn't"} available on the ChatGPT plan. Choose another model, or add an OpenAI API key.`
|
|
4058
|
+
}
|
|
4059
|
+
);
|
|
4060
|
+
}
|
|
4061
|
+
case "subscription_sharing_invalid_user":
|
|
4062
|
+
return new ProviderError("openai", message ?? "ChatGPT couldn't validate your sign-in.", {
|
|
4063
|
+
...base,
|
|
4064
|
+
statusCode: statusCode ?? 401,
|
|
4065
|
+
hint: "Continue with ChatGPT again to reconnect."
|
|
4066
|
+
});
|
|
4067
|
+
default:
|
|
4068
|
+
return null;
|
|
4069
|
+
}
|
|
4053
4070
|
}
|
|
4054
4071
|
|
|
4055
4072
|
// src/providers/xai-responses.ts
|
|
@@ -4183,92 +4200,17 @@ async function* synthesizeResponseEvents(response) {
|
|
|
4183
4200
|
}
|
|
4184
4201
|
|
|
4185
4202
|
// src/providers/gemini.ts
|
|
4186
|
-
var
|
|
4187
|
-
var
|
|
4188
|
-
var
|
|
4189
|
-
var GEMINI_CLI_API_CLIENT = "gemini-cli/0.0.0";
|
|
4190
|
-
var CODE_ASSIST_NON_STREAMING_RETRIES = 3;
|
|
4191
|
-
var CODE_ASSIST_NON_STREAMING_RETRY_DELAY_MS = 1e3;
|
|
4203
|
+
var DEFAULT_BASE_URL2 = "https://generativelanguage.googleapis.com/v1beta";
|
|
4204
|
+
var NON_STREAMING_RETRIES = 3;
|
|
4205
|
+
var NON_STREAMING_RETRY_DELAY_MS = 1e3;
|
|
4192
4206
|
var SYNTHETIC_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
|
|
4193
|
-
var CODE_ASSIST_SUPPORTED_MODELS = /* @__PURE__ */ new Set([
|
|
4194
|
-
"gemini-3-pro-preview",
|
|
4195
|
-
"gemini-3.1-pro-preview",
|
|
4196
|
-
"gemini-3.1-pro-preview-customtools",
|
|
4197
|
-
"gemini-3-flash-preview",
|
|
4198
|
-
"gemini-3.5-flash",
|
|
4199
|
-
"gemini-3-flash",
|
|
4200
|
-
"gemini-3.1-flash-lite",
|
|
4201
|
-
"gemini-3.7-flash",
|
|
4202
|
-
"gemini-3.8-flash",
|
|
4203
|
-
"gemini-3.5-flash-lite",
|
|
4204
|
-
"gemini-2.5-pro",
|
|
4205
|
-
"gemini-2.5-flash",
|
|
4206
|
-
"gemma-4-31b-it",
|
|
4207
|
-
"gemma-4-26b-a4b-it"
|
|
4208
|
-
]);
|
|
4209
|
-
function isGeminiApiKeyRoute(options) {
|
|
4210
|
-
return options.baseUrl?.includes("generativelanguage.googleapis.com") === true;
|
|
4211
|
-
}
|
|
4212
|
-
function getGoogleProject(options) {
|
|
4213
|
-
const env = getEnvironment();
|
|
4214
|
-
return options.projectId ?? env?.GOOGLE_CLOUD_PROJECT ?? env?.GOOGLE_CLOUD_PROJECT_ID;
|
|
4215
|
-
}
|
|
4216
|
-
function getCodeAssistEndpoint(method) {
|
|
4217
|
-
const env = getEnvironment();
|
|
4218
|
-
const endpoint = env?.CODE_ASSIST_ENDPOINT ?? DEFAULT_CODE_ASSIST_BASE_URL;
|
|
4219
|
-
const version = env?.CODE_ASSIST_API_VERSION || CODE_ASSIST_API_VERSION;
|
|
4220
|
-
return new URL(`${endpoint}/${version}:${method}`);
|
|
4221
|
-
}
|
|
4222
|
-
function formatUnsupportedModelMessage(model) {
|
|
4223
|
-
return `Gemini OAuth is configured to use the Gemini Code Assist subscription endpoint only. That endpoint does not currently expose model "${model}".`;
|
|
4224
|
-
}
|
|
4225
|
-
var ACCOUNT_GATED_MODELS = /* @__PURE__ */ new Set([
|
|
4226
|
-
"gemini-3-flash",
|
|
4227
|
-
"gemini-3.5-flash",
|
|
4228
|
-
"gemini-3.1-pro-preview",
|
|
4229
|
-
"gemini-3.1-pro-preview-customtools",
|
|
4230
|
-
"gemini-3.7-flash",
|
|
4231
|
-
"gemini-3.8-flash",
|
|
4232
|
-
"gemini-3.5-flash-lite"
|
|
4233
|
-
]);
|
|
4234
|
-
function accountGatedMessage(model) {
|
|
4235
|
-
if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
|
|
4236
|
-
return `${model} is not available through Code Assist for this account. Public Gemini API availability does not guarantee Code Assist OAuth access.`;
|
|
4237
|
-
}
|
|
4238
|
-
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.`;
|
|
4239
|
-
}
|
|
4240
|
-
function accountGatedHint(model) {
|
|
4241
|
-
if (model === "gemini-3.8-flash" || model === "gemini-3.5-flash-lite") {
|
|
4242
|
-
return "Use /model to select Gemini 3.1 Flash Lite, or retry once Google enables this model through Code Assist for your account.";
|
|
4243
|
-
}
|
|
4244
|
-
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.`;
|
|
4245
|
-
}
|
|
4246
|
-
function isCodeAssistLicenseRequired(status, body) {
|
|
4247
|
-
if (status !== 403) return false;
|
|
4248
|
-
try {
|
|
4249
|
-
const parsed = JSON.parse(body);
|
|
4250
|
-
const details = typeof parsed === "object" && parsed !== null && "error" in parsed ? parsed.error?.details : void 0;
|
|
4251
|
-
return Array.isArray(details) && details.some(
|
|
4252
|
-
(detail) => typeof detail === "object" && detail !== null && detail.reason === "SUBSCRIPTION_REQUIRED"
|
|
4253
|
-
);
|
|
4254
|
-
} catch {
|
|
4255
|
-
return false;
|
|
4256
|
-
}
|
|
4257
|
-
}
|
|
4258
4207
|
function isInvalidGeminiApiKey(status, body) {
|
|
4259
4208
|
if (status !== 400 && status !== 401 && status !== 403) return false;
|
|
4260
4209
|
return /"reason"\s*:\s*"API_KEY_INVALID"/.test(body) || /API key not valid/i.test(body);
|
|
4261
4210
|
}
|
|
4262
4211
|
var INVALID_API_KEY_MESSAGE = "Google rejected the Gemini API key: it is not valid.";
|
|
4263
4212
|
var INVALID_API_KEY_HINT = "Create a key at aistudio.google.com/apikey and add it again under Gemini in Providers (or `ggcoder login`).";
|
|
4264
|
-
|
|
4265
|
-
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.";
|
|
4266
|
-
function formatErrorMessage(status, body, model) {
|
|
4267
|
-
if (status === 404 && !CODE_ASSIST_SUPPORTED_MODELS.has(model)) {
|
|
4268
|
-
return `Gemini API error (404): ${body}
|
|
4269
|
-
|
|
4270
|
-
${formatUnsupportedModelMessage(model)}`;
|
|
4271
|
-
}
|
|
4213
|
+
function formatErrorMessage(status, body) {
|
|
4272
4214
|
return `Gemini API error (${status}): ${body}`;
|
|
4273
4215
|
}
|
|
4274
4216
|
function parseRetryDelaySeconds(body) {
|
|
@@ -4474,27 +4416,15 @@ function buildGenerateRequest(options) {
|
|
|
4474
4416
|
...systemInstruction ? { systemInstruction } : {},
|
|
4475
4417
|
...tools ? { tools } : {},
|
|
4476
4418
|
...toolConfig ? { toolConfig } : {},
|
|
4477
|
-
...Object.keys(generationConfig).length > 0 ? { generationConfig } : {}
|
|
4478
|
-
...options.promptCacheKey ? { session_id: options.promptCacheKey } : {}
|
|
4479
|
-
};
|
|
4480
|
-
}
|
|
4481
|
-
function buildCodeAssistRequest(options, request, projectId) {
|
|
4482
|
-
return {
|
|
4483
|
-
model: options.model,
|
|
4484
|
-
...projectId ? { project: projectId } : {},
|
|
4485
|
-
user_prompt_id: crypto.randomUUID(),
|
|
4486
|
-
request
|
|
4419
|
+
...Object.keys(generationConfig).length > 0 ? { generationConfig } : {}
|
|
4487
4420
|
};
|
|
4488
4421
|
}
|
|
4489
4422
|
var API_KEY_MODEL_IDS = {
|
|
4490
4423
|
"gemini-3-flash": "gemini-3.5-flash"
|
|
4491
4424
|
};
|
|
4492
|
-
function
|
|
4493
|
-
const
|
|
4494
|
-
|
|
4495
|
-
options.providerOptions
|
|
4496
|
-
);
|
|
4497
|
-
const base = (options.baseUrl ?? "").replace(/\/+$/, "");
|
|
4425
|
+
function buildRequestPlan(options, method) {
|
|
4426
|
+
const request = mergeProviderOptions(buildGenerateRequest(options), options.providerOptions);
|
|
4427
|
+
const base = (options.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
|
|
4498
4428
|
return {
|
|
4499
4429
|
url: new URL(
|
|
4500
4430
|
`${base}/models/${encodeURIComponent(API_KEY_MODEL_IDS[options.model] ?? options.model)}:${method}`
|
|
@@ -4503,27 +4433,7 @@ function buildApiKeyRequestPlan(options, method) {
|
|
|
4503
4433
|
"Content-Type": "application/json",
|
|
4504
4434
|
"x-goog-api-key": options.apiKey ?? ""
|
|
4505
4435
|
},
|
|
4506
|
-
body: request
|
|
4507
|
-
route: "api-key"
|
|
4508
|
-
};
|
|
4509
|
-
}
|
|
4510
|
-
function buildRequestPlan(options, method) {
|
|
4511
|
-
if (isGeminiApiKeyRoute(options)) return buildApiKeyRequestPlan(options, method);
|
|
4512
|
-
if (!CODE_ASSIST_SUPPORTED_MODELS.has(options.model)) {
|
|
4513
|
-
throw new ProviderError("gemini", formatUnsupportedModelMessage(options.model));
|
|
4514
|
-
}
|
|
4515
|
-
const projectId = getGoogleProject(options);
|
|
4516
|
-
const request = mergeProviderOptions(buildGenerateRequest(options), options.providerOptions);
|
|
4517
|
-
return {
|
|
4518
|
-
url: getCodeAssistEndpoint(method),
|
|
4519
|
-
headers: {
|
|
4520
|
-
Authorization: `Bearer ${options.apiKey}`,
|
|
4521
|
-
"Content-Type": "application/json",
|
|
4522
|
-
"User-Agent": GEMINI_CLI_USER_AGENT,
|
|
4523
|
-
"X-Goog-Api-Client": GEMINI_CLI_API_CLIENT
|
|
4524
|
-
},
|
|
4525
|
-
body: buildCodeAssistRequest(options, request, projectId),
|
|
4526
|
-
route: "code-assist"
|
|
4436
|
+
body: request
|
|
4527
4437
|
};
|
|
4528
4438
|
}
|
|
4529
4439
|
function normalizeGeminiStopReason(reason) {
|
|
@@ -4550,10 +4460,10 @@ async function* streamSse(response) {
|
|
|
4550
4460
|
}
|
|
4551
4461
|
}
|
|
4552
4462
|
function candidatesFromResponse(response) {
|
|
4553
|
-
return response.
|
|
4463
|
+
return response.candidates;
|
|
4554
4464
|
}
|
|
4555
4465
|
function usageFromResponse(response) {
|
|
4556
|
-
return response.
|
|
4466
|
+
return response.usageMetadata;
|
|
4557
4467
|
}
|
|
4558
4468
|
function partsFromResponse(response) {
|
|
4559
4469
|
return candidatesFromResponse(response)?.[0]?.content?.parts ?? [];
|
|
@@ -4575,7 +4485,7 @@ function readFunctionCallPart(part) {
|
|
|
4575
4485
|
function makeToolCallId(index, providerId) {
|
|
4576
4486
|
return providerId ?? `gemini_call_${index}_${crypto.randomUUID().replace(/-/g, "")}`;
|
|
4577
4487
|
}
|
|
4578
|
-
function
|
|
4488
|
+
function shouldRetryStatus(status) {
|
|
4579
4489
|
return status === 429 || status === 499 || status >= 500 && status <= 599;
|
|
4580
4490
|
}
|
|
4581
4491
|
function isAbortError(err) {
|
|
@@ -4598,7 +4508,7 @@ async function sleep(ms, signal) {
|
|
|
4598
4508
|
if (signal?.aborted) onAbort();
|
|
4599
4509
|
});
|
|
4600
4510
|
}
|
|
4601
|
-
async function
|
|
4511
|
+
async function fetchGemini(plan, options) {
|
|
4602
4512
|
try {
|
|
4603
4513
|
const response = await providerFetch(plan.url, {
|
|
4604
4514
|
method: "POST",
|
|
@@ -4609,11 +4519,8 @@ async function fetchCodeAssist(plan, options) {
|
|
|
4609
4519
|
if (!response.ok) {
|
|
4610
4520
|
const text = await response.text().catch(() => "");
|
|
4611
4521
|
const quota = parseGeminiQuota(response.status, text);
|
|
4612
|
-
const
|
|
4613
|
-
|
|
4614
|
-
const licenseRequired = codeAssist && isCodeAssistLicenseRequired(response.status, text);
|
|
4615
|
-
const invalidKey = !codeAssist && isInvalidGeminiApiKey(response.status, text);
|
|
4616
|
-
let message = invalidKey ? INVALID_API_KEY_MESSAGE : licenseRequired ? LICENSE_REQUIRED_MESSAGE : accountGated ? accountGatedMessage(options.model) : formatErrorMessage(response.status, text, options.model);
|
|
4522
|
+
const invalidKey = isInvalidGeminiApiKey(response.status, text);
|
|
4523
|
+
let message = invalidKey ? INVALID_API_KEY_MESSAGE : formatErrorMessage(response.status, text);
|
|
4617
4524
|
let resetsAt;
|
|
4618
4525
|
if (quota?.exhausted) {
|
|
4619
4526
|
message = `Gemini quota exhausted \u2014 usage limit reached. ${message}`;
|
|
@@ -4623,7 +4530,7 @@ async function fetchCodeAssist(plan, options) {
|
|
|
4623
4530
|
throw new ProviderError("gemini", message, {
|
|
4624
4531
|
statusCode: response.status,
|
|
4625
4532
|
...resetsAt !== void 0 ? { resetsAt } : {},
|
|
4626
|
-
...invalidKey ? { hint: INVALID_API_KEY_HINT, source: "auth" } :
|
|
4533
|
+
...invalidKey ? { hint: INVALID_API_KEY_HINT, source: "auth" } : {}
|
|
4627
4534
|
});
|
|
4628
4535
|
}
|
|
4629
4536
|
return response;
|
|
@@ -4631,26 +4538,26 @@ async function fetchCodeAssist(plan, options) {
|
|
|
4631
4538
|
throw toError3(err);
|
|
4632
4539
|
}
|
|
4633
4540
|
}
|
|
4634
|
-
async function
|
|
4541
|
+
async function fetchGeminiWithRetry(plan, options) {
|
|
4635
4542
|
let lastError;
|
|
4636
|
-
for (let attempt = 0; attempt <=
|
|
4543
|
+
for (let attempt = 0; attempt <= NON_STREAMING_RETRIES; attempt++) {
|
|
4637
4544
|
try {
|
|
4638
|
-
return await
|
|
4545
|
+
return await fetchGemini(plan, options);
|
|
4639
4546
|
} catch (err) {
|
|
4640
4547
|
const error = toError3(err);
|
|
4641
4548
|
const statusCode = error instanceof ProviderError ? error.statusCode : void 0;
|
|
4642
|
-
if (options.signal?.aborted || isAbortError(error) || attempt ===
|
|
4549
|
+
if (options.signal?.aborted || isAbortError(error) || attempt === NON_STREAMING_RETRIES || statusCode != null && !shouldRetryStatus(statusCode)) {
|
|
4643
4550
|
throw error;
|
|
4644
4551
|
}
|
|
4645
4552
|
lastError = error;
|
|
4646
4553
|
}
|
|
4647
4554
|
try {
|
|
4648
|
-
await sleep(
|
|
4555
|
+
await sleep(NON_STREAMING_RETRY_DELAY_MS, options.signal);
|
|
4649
4556
|
} catch (err) {
|
|
4650
4557
|
throw toError3(err);
|
|
4651
4558
|
}
|
|
4652
4559
|
}
|
|
4653
|
-
throw lastError ?? new ProviderError("gemini", "Gemini
|
|
4560
|
+
throw lastError ?? new ProviderError("gemini", "Gemini request failed.");
|
|
4654
4561
|
}
|
|
4655
4562
|
function streamGemini(options) {
|
|
4656
4563
|
return new StreamResult(runStream5(options), options.signal);
|
|
@@ -4660,7 +4567,7 @@ async function* runStream5(options) {
|
|
|
4660
4567
|
const method = useStreaming ? "streamGenerateContent" : "generateContent";
|
|
4661
4568
|
const plan = buildRequestPlan(options, method);
|
|
4662
4569
|
if (useStreaming) plan.url.searchParams.set("alt", "sse");
|
|
4663
|
-
const response = useStreaming ? await
|
|
4570
|
+
const response = useStreaming ? await fetchGemini(plan, options) : await fetchGeminiWithRetry(plan, options);
|
|
4664
4571
|
const contentParts = [];
|
|
4665
4572
|
const pendingToolCalls = [];
|
|
4666
4573
|
let textAccum = "";
|
|
@@ -4971,7 +4878,7 @@ async function postJson(request) {
|
|
|
4971
4878
|
}
|
|
4972
4879
|
function httpError(provider, response, text, headerRequestId, clean) {
|
|
4973
4880
|
const status = response.status;
|
|
4974
|
-
const parsed =
|
|
4881
|
+
const parsed = parseErrorBody2(text);
|
|
4975
4882
|
const rawMessage = parsed.message ?? (parsed.isJson ? "" : text.trim());
|
|
4976
4883
|
const message = !rawMessage ? emptyProviderErrorMessage(status) : isRawHtmlErrorEcho(rawMessage) ? providerHtmlErrorMessage(status) : clean(rawMessage);
|
|
4977
4884
|
const requestId = headerRequestId ?? parsed.requestId;
|
|
@@ -4997,7 +4904,7 @@ function httpError(provider, response, text, headerRequestId, clean) {
|
|
|
4997
4904
|
}
|
|
4998
4905
|
return new ProviderError(provider, message, base);
|
|
4999
4906
|
}
|
|
5000
|
-
function
|
|
4907
|
+
function parseErrorBody2(text) {
|
|
5001
4908
|
let parsed;
|
|
5002
4909
|
try {
|
|
5003
4910
|
parsed = JSON.parse(text);
|
|
@@ -5429,10 +5336,7 @@ var OPENAI_EMBED_BATCH_SIZE = 2048;
|
|
|
5429
5336
|
var OPENROUTER_EMBED_BATCH_SIZE = 64;
|
|
5430
5337
|
var LOCAL_EMBED_BATCH_SIZE = 64;
|
|
5431
5338
|
var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
|
|
5432
|
-
var
|
|
5433
|
-
var KIMI_CODE_USER_AGENT = `kimi-code-cli/${process.env.KIMI_CODE_VERSION ?? "1.0.11"}`;
|
|
5434
|
-
var GROK_CLI_PROXY_HOST = "cli-chat-proxy.grok.com";
|
|
5435
|
-
var GROK_CLI_VERSION = process.env.GROK_CLI_VERSION ?? "1.0.67";
|
|
5339
|
+
var GLM_BASE_URL = "https://api.z.ai/api/paas/v4";
|
|
5436
5340
|
providerRegistry.register("anthropic", {
|
|
5437
5341
|
stream: (options) => streamAnthropic(options)
|
|
5438
5342
|
});
|
|
@@ -5445,12 +5349,12 @@ providerRegistry.register("xiaomi", {
|
|
|
5445
5349
|
});
|
|
5446
5350
|
providerRegistry.register("openai", {
|
|
5447
5351
|
stream: (options) => {
|
|
5448
|
-
if (options.accountId) {
|
|
5449
|
-
return
|
|
5352
|
+
if (options.accountId || isOpenAIHost(options.baseUrl)) {
|
|
5353
|
+
return streamOpenAIResponses(options);
|
|
5450
5354
|
}
|
|
5451
5355
|
return streamOpenAI(options);
|
|
5452
5356
|
},
|
|
5453
|
-
// OpenAI API-key auth only; ChatGPT
|
|
5357
|
+
// OpenAI API-key auth only; the ChatGPT plan route has no embeddings API.
|
|
5454
5358
|
// OpenAI has no task/input-type parameter, so `inputType` is ignored.
|
|
5455
5359
|
embed: (request) => embedOpenAICompatible(request, {
|
|
5456
5360
|
provider: "openai",
|
|
@@ -5460,22 +5364,24 @@ providerRegistry.register("openai", {
|
|
|
5460
5364
|
});
|
|
5461
5365
|
providerRegistry.register("gemini", {
|
|
5462
5366
|
stream: (options) => streamGemini(options),
|
|
5463
|
-
// Native Gemini API with an AI Studio key
|
|
5367
|
+
// Native Gemini API with an AI Studio key.
|
|
5464
5368
|
embed: (request) => embedGemini(request),
|
|
5465
5369
|
embedBatchSize: GEMINI_EMBED_BATCH_SIZE
|
|
5466
5370
|
});
|
|
5467
5371
|
providerRegistry.register("glm", {
|
|
5468
5372
|
stream: (options) => streamOpenAI({
|
|
5469
5373
|
...options,
|
|
5470
|
-
baseUrl: options.baseUrl ??
|
|
5374
|
+
baseUrl: options.baseUrl ?? GLM_BASE_URL
|
|
5471
5375
|
})
|
|
5472
5376
|
});
|
|
5473
5377
|
providerRegistry.register("moonshot", {
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5378
|
+
// A Moonshot key goes to the public API; a Kimi Code plan key carries its
|
|
5379
|
+
// own baseUrl (api.kimi.com / api.kimi.ai). Both get GG Coder's own identity.
|
|
5380
|
+
stream: (options) => streamOpenAI({
|
|
5381
|
+
...options,
|
|
5382
|
+
baseUrl: options.baseUrl ?? "https://api.moonshot.ai/v1",
|
|
5383
|
+
defaultHeaders: { "User-Agent": GG_CODER_USER_AGENT, ...options.defaultHeaders }
|
|
5384
|
+
})
|
|
5479
5385
|
});
|
|
5480
5386
|
providerRegistry.register("deepseek", {
|
|
5481
5387
|
stream: (options) => streamOpenAI({
|
|
@@ -5524,27 +5430,10 @@ providerRegistry.register("sakana", {
|
|
|
5524
5430
|
})
|
|
5525
5431
|
});
|
|
5526
5432
|
providerRegistry.register("xai", {
|
|
5527
|
-
//
|
|
5528
|
-
//
|
|
5529
|
-
//
|
|
5530
|
-
|
|
5531
|
-
// Subscription OAuth (SuperGrok / X Premium) routes to the Grok CLI chat proxy
|
|
5532
|
-
// instead, which serves the same `/v1/responses` surface but gates on Grok-CLI
|
|
5533
|
-
// client identity. Inject those headers centrally here — exactly as the Kimi
|
|
5534
|
-
// endpoint above — so EVERY stream (agent loop, compaction, title-gen,
|
|
5535
|
-
// sub-agents) is accepted rather than depending on each call site to thread
|
|
5536
|
-
// headers. Caller-provided headers still win on collision.
|
|
5537
|
-
stream: (options) => {
|
|
5538
|
-
const baseUrl = options.baseUrl ?? "https://api.x.ai/v1";
|
|
5539
|
-
const defaultHeaders = baseUrl.includes(GROK_CLI_PROXY_HOST) ? {
|
|
5540
|
-
"X-XAI-Token-Auth": "xai-grok-cli",
|
|
5541
|
-
"x-grok-client-version": GROK_CLI_VERSION,
|
|
5542
|
-
"x-grok-client-identifier": "ggcoder",
|
|
5543
|
-
"x-grok-model-override": options.model,
|
|
5544
|
-
...options.defaultHeaders
|
|
5545
|
-
} : options.defaultHeaders;
|
|
5546
|
-
return streamXaiResponses({ ...options, baseUrl, defaultHeaders });
|
|
5547
|
-
}
|
|
5433
|
+
// xAI API key on xAI's Responses API (see providers/xai-responses.ts): Chat
|
|
5434
|
+
// Completions is deprecated for Grok reasoning models and can't carry the
|
|
5435
|
+
// encrypted reasoning that keeps long tool loops coherent.
|
|
5436
|
+
stream: (options) => streamXaiResponses({ ...options, baseUrl: options.baseUrl ?? "https://api.x.ai/v1" })
|
|
5548
5437
|
});
|
|
5549
5438
|
providerRegistry.register("minimax", {
|
|
5550
5439
|
stream: (options) => streamAnthropic({
|
|
@@ -5595,6 +5484,14 @@ providerRegistry.register("local", {
|
|
|
5595
5484
|
}
|
|
5596
5485
|
)
|
|
5597
5486
|
});
|
|
5487
|
+
function isOpenAIHost(baseUrl) {
|
|
5488
|
+
if (!baseUrl) return true;
|
|
5489
|
+
try {
|
|
5490
|
+
return new URL(baseUrl).hostname === "api.openai.com";
|
|
5491
|
+
} catch {
|
|
5492
|
+
return false;
|
|
5493
|
+
}
|
|
5494
|
+
}
|
|
5598
5495
|
function requireLocalBaseUrl(baseUrl) {
|
|
5599
5496
|
if (!baseUrl) {
|
|
5600
5497
|
throw new GGAIError(
|
|
@@ -5785,16 +5682,16 @@ function l2Normalize(vector) {
|
|
|
5785
5682
|
|
|
5786
5683
|
// src/utils/preconnect.ts
|
|
5787
5684
|
var PRECONNECT_INTERVAL_MS = 3e4;
|
|
5788
|
-
function defaultBaseUrl(provider
|
|
5685
|
+
function defaultBaseUrl(provider) {
|
|
5789
5686
|
switch (provider) {
|
|
5790
5687
|
case "anthropic":
|
|
5791
5688
|
return "https://api.anthropic.com";
|
|
5792
5689
|
case "openai":
|
|
5793
|
-
return
|
|
5690
|
+
return "https://api.openai.com/v1";
|
|
5794
5691
|
case "gemini":
|
|
5795
|
-
return "https://
|
|
5692
|
+
return "https://generativelanguage.googleapis.com/v1beta";
|
|
5796
5693
|
case "glm":
|
|
5797
|
-
return "https://api.z.ai/api/
|
|
5694
|
+
return "https://api.z.ai/api/paas/v4";
|
|
5798
5695
|
case "moonshot":
|
|
5799
5696
|
return "https://api.moonshot.ai/v1";
|
|
5800
5697
|
case "deepseek":
|
|
@@ -5827,7 +5724,7 @@ function isLocalHost(hostname) {
|
|
|
5827
5724
|
return a === 127 || a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31;
|
|
5828
5725
|
}
|
|
5829
5726
|
function preconnectTarget(options) {
|
|
5830
|
-
const raw = options.baseUrl ?? defaultBaseUrl(options.provider
|
|
5727
|
+
const raw = options.baseUrl ?? defaultBaseUrl(options.provider);
|
|
5831
5728
|
if (!raw || options.provider === "local" || options.provider === "palsu") return void 0;
|
|
5832
5729
|
let url;
|
|
5833
5730
|
try {
|
|
@@ -6341,10 +6238,10 @@ function palsuRerankScore(query, document) {
|
|
|
6341
6238
|
return hits / queryTokens.size;
|
|
6342
6239
|
}
|
|
6343
6240
|
export {
|
|
6344
|
-
CODEX_CLIENT_VERSION,
|
|
6345
6241
|
EventStream,
|
|
6346
6242
|
FAST_MODE_SUFFIX,
|
|
6347
6243
|
GGAIError,
|
|
6244
|
+
GG_CODER_USER_AGENT,
|
|
6348
6245
|
LOCAL_NO_API_KEY,
|
|
6349
6246
|
PRECONNECT_INTERVAL_MS,
|
|
6350
6247
|
ProviderError,
|