@kenkaiiii/gg-ai 4.3.164 → 4.3.166

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -34,6 +34,8 @@ __export(index_exports, {
34
34
  GGAIError: () => GGAIError,
35
35
  ProviderError: () => ProviderError,
36
36
  StreamResult: () => StreamResult,
37
+ formatError: () => formatError,
38
+ formatErrorForDisplay: () => formatErrorForDisplay,
37
39
  palsuAssistantMessage: () => palsuAssistantMessage,
38
40
  palsuText: () => palsuText,
39
41
  palsuThinking: () => palsuThinking,
@@ -46,21 +48,165 @@ module.exports = __toCommonJS(index_exports);
46
48
 
47
49
  // src/errors.ts
48
50
  var GGAIError = class extends Error {
51
+ source;
52
+ requestId;
53
+ hint;
49
54
  constructor(message, options) {
50
- super(message, options);
55
+ super(message, { cause: options?.cause });
51
56
  this.name = "GGAIError";
57
+ this.source = options?.source ?? "ggcoder";
58
+ this.requestId = options?.requestId;
59
+ this.hint = options?.hint;
52
60
  }
53
61
  };
54
62
  var ProviderError = class extends GGAIError {
55
63
  provider;
56
64
  statusCode;
57
65
  constructor(provider, message, options) {
58
- super(`[${provider}] ${message}`, { cause: options?.cause });
66
+ super(message, {
67
+ source: "provider",
68
+ requestId: options?.requestId,
69
+ hint: options?.hint,
70
+ cause: options?.cause
71
+ });
59
72
  this.name = "ProviderError";
60
73
  this.provider = provider;
61
74
  this.statusCode = options?.statusCode;
62
75
  }
63
76
  };
77
+ var PROVIDER_DISPLAY = {
78
+ openai: "OpenAI",
79
+ anthropic: "Anthropic",
80
+ glm: "Z.AI (GLM)",
81
+ moonshot: "Moonshot",
82
+ deepseek: "DeepSeek",
83
+ openrouter: "OpenRouter",
84
+ xiaomi: "Xiaomi (MiMo)",
85
+ minimax: "MiniMax"
86
+ };
87
+ var PROVIDER_STATUS_URL = {
88
+ openai: "status.openai.com",
89
+ anthropic: "status.anthropic.com"
90
+ };
91
+ function providerDisplayName(provider) {
92
+ return PROVIDER_DISPLAY[provider] ?? provider;
93
+ }
94
+ function formatError(err) {
95
+ if (err instanceof ProviderError) {
96
+ const name = providerDisplayName(err.provider);
97
+ const cleanMessage = cleanProviderMessage(err.message);
98
+ return {
99
+ headline: `${name} returned an error.`,
100
+ source: "provider",
101
+ message: cleanMessage,
102
+ provider: err.provider,
103
+ statusCode: err.statusCode,
104
+ requestId: err.requestId,
105
+ guidance: err.hint ?? providerGuidance(err.provider, cleanMessage, err.statusCode)
106
+ };
107
+ }
108
+ if (err instanceof GGAIError) {
109
+ return finaliseBySource(err.source, err.message, err.requestId, err.hint);
110
+ }
111
+ if (err instanceof Error) {
112
+ const source = inferSource(err);
113
+ return finaliseBySource(source, err.message, void 0, void 0);
114
+ }
115
+ return finaliseBySource("ggcoder", String(err), void 0, void 0);
116
+ }
117
+ function finaliseBySource(source, message, requestId, hint) {
118
+ switch (source) {
119
+ case "network":
120
+ return {
121
+ headline: "Network error \u2014 couldn't reach the provider.",
122
+ source,
123
+ message,
124
+ guidance: hint ?? "Check your internet connection. Not a ggcoder issue \u2014 retry shortly.",
125
+ ...requestId ? { requestId } : {}
126
+ };
127
+ case "auth":
128
+ return {
129
+ headline: "Authentication issue.",
130
+ source,
131
+ message,
132
+ guidance: hint ?? "Run `ggcoder login` to refresh your credentials.",
133
+ ...requestId ? { requestId } : {}
134
+ };
135
+ case "provider":
136
+ return {
137
+ headline: "Provider returned an error.",
138
+ source,
139
+ message,
140
+ guidance: hint ?? providerGuidance(void 0, message, void 0),
141
+ ...requestId ? { requestId } : {}
142
+ };
143
+ case "ggcoder":
144
+ return {
145
+ headline: "ggcoder hit an unexpected error.",
146
+ source,
147
+ message,
148
+ guidance: hint ?? "This looks like a ggcoder bug \u2014 please report it to the developer (see /help).",
149
+ ...requestId ? { requestId } : {}
150
+ };
151
+ }
152
+ }
153
+ function formatErrorForDisplay(err) {
154
+ const f = formatError(err);
155
+ const lines = [f.headline];
156
+ if (f.message && f.message !== f.headline) lines.push(` ${f.message}`);
157
+ lines.push(` \u2192 ${f.guidance}`);
158
+ return lines.join("\n");
159
+ }
160
+ function cleanProviderMessage(message) {
161
+ return message.replace(/^\[[^\]]+\]\s*/, "").trim();
162
+ }
163
+ function inferSource(err) {
164
+ const msg = err.message.toLowerCase();
165
+ const code = err.code ?? "";
166
+ if (code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "ENOTFOUND" || code === "ECONNRESET" || msg.includes("fetch failed") || msg.includes("network request failed")) {
167
+ return "network";
168
+ }
169
+ if (msg.includes("not logged in") || msg.includes("token exchange failed") || msg.includes("token refresh failed") || msg.includes("invalid_grant")) {
170
+ return "auth";
171
+ }
172
+ return "ggcoder";
173
+ }
174
+ function providerGuidance(provider, message, statusCode) {
175
+ const name = provider ? providerDisplayName(provider) : "the provider";
176
+ const status = provider ? PROVIDER_STATUS_URL[provider] : void 0;
177
+ const lower = message.toLowerCase();
178
+ if (statusCode === 401 || lower.includes("unauthorized") || lower.includes("invalid api key")) {
179
+ return `Authentication failed with ${name}. Run \`ggcoder login\` to refresh your credentials.`;
180
+ }
181
+ if (lower.includes("overloaded") || lower.includes("engine_overloaded")) {
182
+ return `${name}'s servers are overloaded right now. Retry in a moment \u2014 not a ggcoder issue.`;
183
+ }
184
+ if (lower.includes("insufficient balance") || lower.includes("quota exceeded") || lower.includes("recharge") || lower.includes("no resource package")) {
185
+ return `Your ${name} account has a billing or quota issue \u2014 check your balance. Not a ggcoder issue.`;
186
+ }
187
+ if (statusCode === 429 || lower.includes("rate limit") || lower.includes("too many requests")) {
188
+ return `${name} rate limit hit. Wait a moment then retry \u2014 not a ggcoder issue.`;
189
+ }
190
+ if (statusCode === 502 || lower.includes("bad gateway")) {
191
+ return `${name} returned a bad gateway. Retry \u2014 this is on their side, not ggcoder.`;
192
+ }
193
+ if (statusCode === 503 || lower.includes("service unavailable")) {
194
+ return `${name} is temporarily unavailable. Retry shortly \u2014 not a ggcoder issue.`;
195
+ }
196
+ if (statusCode === 500 || lower.includes("server_error") || lower.includes("500") && lower.includes("internal server error")) {
197
+ return status ? `This is an error from ${name}, not ggcoder. Retry \u2014 if it keeps happening, check ${status}.` : `This is an error from ${name}, not ggcoder. Retry \u2014 if it keeps happening, try a different model with /model.`;
198
+ }
199
+ if (lower.includes("timeout") || lower.includes("timed out")) {
200
+ return `Request to ${name} timed out. Their servers may be slow \u2014 retry. Not a ggcoder issue.`;
201
+ }
202
+ if (lower.includes("does not recognize the requested model") || lower.includes("model") && (lower.includes("not exist") || lower.includes("not found") || lower.includes("no access"))) {
203
+ return `${name} doesn't recognise this model on your account. Use /model to switch, or check your subscription tier.`;
204
+ }
205
+ if (lower.includes("context_length_exceeded") || lower.includes("prompt is too long")) {
206
+ return `Context window for this ${name} model is full. Run /compact to shrink history, or start a new session.`;
207
+ }
208
+ return status ? `This is an error from ${name}, not ggcoder. Retry \u2014 if it persists, check ${status}.` : `This is an error from ${name}, not ggcoder. Retry \u2014 if it persists, try a different model with /model.`;
209
+ }
64
210
 
65
211
  // src/providers/anthropic.ts
66
212
  var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
@@ -428,8 +574,8 @@ function supportsAdaptiveThinking(model) {
428
574
  }
429
575
  function toAnthropicThinking(level, maxTokens, model) {
430
576
  if (supportsAdaptiveThinking(model)) {
431
- let effort = level;
432
- if (level === "max" && !model.includes("opus")) {
577
+ let effort = level === "xhigh" ? "max" : level;
578
+ if (effort === "max" && !model.includes("opus")) {
433
579
  effort = "high";
434
580
  }
435
581
  return {
@@ -438,7 +584,7 @@ function toAnthropicThinking(level, maxTokens, model) {
438
584
  outputConfig: { effort }
439
585
  };
440
586
  }
441
- const effectiveLevel = level === "max" ? "high" : level;
587
+ const effectiveLevel = level === "xhigh" ? "high" : level;
442
588
  const budgetMap = {
443
589
  low: Math.max(1024, Math.floor(maxTokens * 0.25)),
444
590
  medium: Math.max(2048, Math.floor(maxTokens * 0.5)),
@@ -571,8 +717,12 @@ function toOpenAIToolChoice(choice) {
571
717
  if (choice === "required") return "required";
572
718
  return { type: "function", function: { name: choice.name } };
573
719
  }
574
- function toOpenAIReasoningEffort(level) {
575
- return level === "max" ? "high" : level;
720
+ function isOpenAIProVariant(model) {
721
+ return /^gpt-5\.\d+-pro$/i.test(model);
722
+ }
723
+ function toOpenAIReasoningEffort(level, model) {
724
+ if (isOpenAIProVariant(model) && level === "low") return "medium";
725
+ return level;
576
726
  }
577
727
  function normalizeAnthropicStopReason(reason) {
578
728
  switch (reason) {
@@ -995,8 +1145,10 @@ function messageToResponse(message) {
995
1145
  }
996
1146
  function toError(err) {
997
1147
  if (err instanceof import_sdk.default.APIError) {
1148
+ const requestId = err.request_id ?? err.error?.request_id;
998
1149
  return new ProviderError("anthropic", err.message, {
999
1150
  statusCode: err.status,
1151
+ ...requestId ? { requestId } : {},
1000
1152
  cause: err
1001
1153
  });
1002
1154
  }
@@ -1039,7 +1191,7 @@ async function* runStream2(options) {
1039
1191
  ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
1040
1192
  ...options.topP != null ? { top_p: options.topP } : {},
1041
1193
  ...options.stop ? { stop: options.stop } : {},
1042
- ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking) } : {},
1194
+ ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
1043
1195
  ...options.tools?.length ? { tools: toOpenAITools(options.tools) } : {},
1044
1196
  ...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
1045
1197
  ...useStreaming ? { stream_options: { include_usage: true } } : {}
@@ -1288,19 +1440,19 @@ function completionToResponse(completion) {
1288
1440
  }
1289
1441
  function toError2(err, provider = "openai") {
1290
1442
  if (err instanceof import_openai.default.APIError) {
1291
- let msg = err.message;
1292
1443
  const body = err.error;
1293
- if (body) {
1294
- const modelName = body.model || "";
1295
- const _code = body.code || "";
1296
- const message = body.message || "";
1297
- if (modelName === "codex-mini-latest" || message.includes("codex-mini-latest")) {
1298
- msg = `codex-mini-latest requires an OpenAI Pro or Max subscription. You currently have access to GPT-5.4 and GPT-5.4 Mini with your account.`;
1299
- }
1300
- msg += ` | body: ${JSON.stringify(body)}`;
1301
- }
1302
- return new ProviderError(provider, msg, {
1444
+ const bodyMessage = typeof body?.message === "string" && body.message.trim() ? body.message.trim() : void 0;
1445
+ const modelName = typeof body?.model === "string" ? body.model : "";
1446
+ const cleanMessage = bodyMessage ?? err.message;
1447
+ let hint;
1448
+ if (modelName === "codex-mini-latest" || cleanMessage.includes("codex-mini-latest")) {
1449
+ hint = "codex-mini-latest requires an OpenAI Pro or Max subscription. Your account currently has access to GPT-5.4 and GPT-5.4 Mini.";
1450
+ }
1451
+ const requestId = err.request_id ?? (typeof body?.request_id === "string" ? body.request_id : void 0);
1452
+ return new ProviderError(provider, cleanMessage, {
1303
1453
  statusCode: err.status,
1454
+ ...requestId ? { requestId } : {},
1455
+ ...hint ? { hint } : {},
1304
1456
  cause: err
1305
1457
  });
1306
1458
  }
@@ -1362,19 +1514,19 @@ async function* runStream3(options) {
1362
1514
  });
1363
1515
  if (!response.ok) {
1364
1516
  const text = await response.text().catch(() => "");
1365
- let message = `Codex API error (${response.status}): ${text}`;
1517
+ const parsed = parseCodexErrorBody(text);
1518
+ const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
1519
+ const requestId = parsed.requestId ?? response.headers.get("x-request-id") ?? response.headers.get("openai-request-id") ?? void 0;
1520
+ let hint;
1366
1521
  if (response.status === 400 && text.includes("not supported")) {
1367
- message += `
1368
-
1369
- Hint: Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription. The "codex-spark" variants require ChatGPT Pro. Ensure your account has an active subscription at https://chatgpt.com/settings`;
1370
- }
1371
- if (response.status === 404 && text.includes("does not exist")) {
1372
- message += `
1373
-
1374
- Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GPT-5.4 and GPT-5.4 Mini work with any active ChatGPT plan.`;
1522
+ hint = 'Codex models require a ChatGPT Plus ($20/mo) or Pro ($200/mo) subscription. The "codex-spark" variants require ChatGPT Pro. Check your subscription at https://chatgpt.com/settings.';
1523
+ } else if (response.status === 404 && text.includes("does not exist")) {
1524
+ hint = "codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GPT-5.4 and GPT-5.4 Mini work with any active ChatGPT plan.";
1375
1525
  }
1376
1526
  throw new ProviderError("openai", message, {
1377
- statusCode: response.status
1527
+ statusCode: response.status,
1528
+ ...requestId ? { requestId } : {},
1529
+ ...hint ? { hint } : {}
1378
1530
  });
1379
1531
  }
1380
1532
  if (!response.body) {
@@ -1389,12 +1541,22 @@ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GP
1389
1541
  const type = event.type;
1390
1542
  if (!type) continue;
1391
1543
  if (type === "error") {
1392
- const msg = event.message || JSON.stringify(event);
1393
- throw new ProviderError("openai", `Codex error: ${msg}`);
1544
+ const nested = event.error ?? void 0;
1545
+ const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
1546
+ const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
1547
+ const requestId = extractCodexRequestId(message) ?? event.request_id;
1548
+ throw new ProviderError("openai", message, {
1549
+ ...requestId != null ? { requestId } : {},
1550
+ ...code === "server_error" ? { statusCode: 500 } : {}
1551
+ });
1394
1552
  }
1395
1553
  if (type === "response.failed") {
1396
- const msg = event.error?.message || "Codex response failed";
1397
- throw new ProviderError("openai", msg);
1554
+ const nested = event.error;
1555
+ const message = nested?.message ?? "Codex response failed.";
1556
+ const requestId = extractCodexRequestId(message) ?? event.request_id;
1557
+ throw new ProviderError("openai", message, {
1558
+ ...requestId != null ? { requestId } : {}
1559
+ });
1398
1560
  }
1399
1561
  if (type === "response.output_text.delta") {
1400
1562
  const delta = event.delta;
@@ -1639,6 +1801,23 @@ function toCodexTools(tools) {
1639
1801
  strict: null
1640
1802
  }));
1641
1803
  }
1804
+ function extractCodexRequestId(message) {
1805
+ const match = message.match(/request ID ([a-z0-9-]{8,})/i);
1806
+ return match?.[1];
1807
+ }
1808
+ function parseCodexErrorBody(text) {
1809
+ if (!text) return {};
1810
+ try {
1811
+ const parsed = JSON.parse(text);
1812
+ const error = parsed.error;
1813
+ const message = error?.message ?? parsed.message;
1814
+ const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractCodexRequestId(message) : void 0);
1815
+ return { ...message ? { message } : {}, ...requestId ? { requestId } : {} };
1816
+ } catch {
1817
+ const trimmed = text.trim().slice(0, 240);
1818
+ return trimmed ? { message: trimmed } : {};
1819
+ }
1820
+ }
1642
1821
 
1643
1822
  // src/provider-registry.ts
1644
1823
  var ProviderRegistryImpl = class {
@@ -1899,6 +2078,8 @@ function registerPalsuProvider(config) {
1899
2078
  GGAIError,
1900
2079
  ProviderError,
1901
2080
  StreamResult,
2081
+ formatError,
2082
+ formatErrorForDisplay,
1902
2083
  palsuAssistantMessage,
1903
2084
  palsuText,
1904
2085
  palsuThinking,