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