@kenkaiiii/gg-ai 4.3.165 → 4.3.167

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);
@@ -292,9 +438,18 @@ function toAnthropicToolResultContent(content) {
292
438
  };
293
439
  });
294
440
  }
441
+ function remapAnthropicToolCallId(id, idMap) {
442
+ if (/^[a-zA-Z0-9_-]+$/.test(id)) return id;
443
+ const existing = idMap.get(id);
444
+ if (existing) return existing;
445
+ const mapped = id.replace(/[^a-zA-Z0-9_-]/g, "_");
446
+ idMap.set(id, mapped);
447
+ return mapped;
448
+ }
295
449
  function toAnthropicMessages(messages, cacheControl) {
296
450
  let systemText;
297
451
  const out = [];
452
+ const idMap = /* @__PURE__ */ new Map();
298
453
  for (const msg of messages) {
299
454
  if (msg.role === "system") {
300
455
  systemText = msg.content;
@@ -329,7 +484,7 @@ function toAnthropicMessages(messages, cacheControl) {
329
484
  if (part.type === "tool_call")
330
485
  return {
331
486
  type: "tool_use",
332
- id: part.id,
487
+ id: remapAnthropicToolCallId(part.id, idMap),
333
488
  name: part.name,
334
489
  input: part.args
335
490
  };
@@ -354,7 +509,7 @@ function toAnthropicMessages(messages, cacheControl) {
354
509
  role: "user",
355
510
  content: msg.content.map((result) => ({
356
511
  type: "tool_result",
357
- tool_use_id: result.toolCallId,
512
+ tool_use_id: remapAnthropicToolCallId(result.toolCallId, idMap),
358
513
  content: toAnthropicToolResultContent(result.content),
359
514
  is_error: result.isError
360
515
  }))
@@ -410,12 +565,19 @@ function toAnthropicMessages(messages, cacheControl) {
410
565
  }
411
566
  return { system, messages: out };
412
567
  }
413
- function toAnthropicTools(tools) {
414
- return tools.map((tool) => ({
415
- name: tool.name,
416
- description: tool.description,
417
- input_schema: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters)
418
- }));
568
+ function toAnthropicTools(tools, options) {
569
+ return tools.map((tool, index) => {
570
+ const anthropicTool = {
571
+ name: tool.name,
572
+ description: tool.description,
573
+ input_schema: tool.rawInputSchema ?? zodToJsonSchema(tool.parameters),
574
+ ...options?.enableFineGrainedToolStreaming ? { eager_input_streaming: true } : {}
575
+ };
576
+ if (options?.cacheControl && index === tools.length - 1) {
577
+ anthropicTool.cache_control = options.cacheControl;
578
+ }
579
+ return anthropicTool;
580
+ });
419
581
  }
420
582
  function toAnthropicToolChoice(choice) {
421
583
  if (choice === "auto") return { type: "auto" };
@@ -634,6 +796,7 @@ async function* runStream(options) {
634
796
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
635
797
  const useStreaming = options.streaming !== false;
636
798
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
799
+ const supportsFirstPartyToolExtras = !options.baseUrl || options.baseUrl.includes("api.anthropic.com");
637
800
  const downgradedMessages = downgradeUnsupportedImages(options.messages, options.supportsImages);
638
801
  const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl);
639
802
  const system = isOAuth ? [
@@ -666,7 +829,10 @@ async function* runStream(options) {
666
829
  ...options.stop ? { stop_sequences: options.stop } : {},
667
830
  ...options.tools?.length || options.serverTools?.length || options.webSearch ? {
668
831
  tools: [
669
- ...options.tools?.length ? toAnthropicTools(options.tools) : [],
832
+ ...options.tools?.length ? toAnthropicTools(options.tools, {
833
+ ...supportsFirstPartyToolExtras && cacheControl ? { cacheControl } : {},
834
+ ...supportsFirstPartyToolExtras ? { enableFineGrainedToolStreaming: true } : {}
835
+ }) : [],
670
836
  ...options.serverTools ?? [],
671
837
  ...options.webSearch ? [{ type: "web_search_20250305", name: "web_search" }] : []
672
838
  ]
@@ -999,8 +1165,10 @@ function messageToResponse(message) {
999
1165
  }
1000
1166
  function toError(err) {
1001
1167
  if (err instanceof import_sdk.default.APIError) {
1168
+ const requestId = err.request_id ?? err.error?.request_id;
1002
1169
  return new ProviderError("anthropic", err.message, {
1003
1170
  statusCode: err.status,
1171
+ ...requestId ? { requestId } : {},
1004
1172
  cause: err
1005
1173
  });
1006
1174
  }
@@ -1050,12 +1218,15 @@ async function* runStream2(options) {
1050
1218
  };
1051
1219
  if (options.provider === "openai" || options.provider === "moonshot") {
1052
1220
  const paramsAny = params;
1053
- paramsAny.prompt_cache_key = "ggcoder";
1221
+ paramsAny.prompt_cache_key = options.promptCacheKey ?? "ggcoder";
1054
1222
  const retention = options.cacheRetention ?? "short";
1055
1223
  if (retention === "long") {
1056
1224
  paramsAny.prompt_cache_retention = "24h";
1057
1225
  }
1058
1226
  }
1227
+ if (options.provider === "openai" && options.serviceTier) {
1228
+ params.service_tier = options.serviceTier;
1229
+ }
1059
1230
  if (usesThinkingParam) {
1060
1231
  if (options.thinking) {
1061
1232
  params.thinking = { type: "enabled" };
@@ -1292,19 +1463,19 @@ function completionToResponse(completion) {
1292
1463
  }
1293
1464
  function toError2(err, provider = "openai") {
1294
1465
  if (err instanceof import_openai.default.APIError) {
1295
- let msg = err.message;
1296
1466
  const body = err.error;
1297
- if (body) {
1298
- const modelName = body.model || "";
1299
- const _code = body.code || "";
1300
- const message = body.message || "";
1301
- if (modelName === "codex-mini-latest" || message.includes("codex-mini-latest")) {
1302
- 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.`;
1303
- }
1304
- msg += ` | body: ${JSON.stringify(body)}`;
1305
- }
1306
- return new ProviderError(provider, msg, {
1467
+ const bodyMessage = typeof body?.message === "string" && body.message.trim() ? body.message.trim() : void 0;
1468
+ const modelName = typeof body?.model === "string" ? body.model : "";
1469
+ const cleanMessage = bodyMessage ?? err.message;
1470
+ let hint;
1471
+ if (modelName === "codex-mini-latest" || cleanMessage.includes("codex-mini-latest")) {
1472
+ 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.";
1473
+ }
1474
+ const requestId = err.request_id ?? (typeof body?.request_id === "string" ? body.request_id : void 0);
1475
+ return new ProviderError(provider, cleanMessage, {
1307
1476
  statusCode: err.status,
1477
+ ...requestId ? { requestId } : {},
1478
+ ...hint ? { hint } : {},
1308
1479
  cause: err
1309
1480
  });
1310
1481
  }
@@ -1338,6 +1509,9 @@ async function* runStream3(options) {
1338
1509
  if (options.tools?.length) {
1339
1510
  body.tools = toCodexTools(options.tools);
1340
1511
  }
1512
+ if (options.promptCacheKey) {
1513
+ body.prompt_cache_key = options.promptCacheKey;
1514
+ }
1341
1515
  if (options.temperature != null && !options.thinking) {
1342
1516
  body.temperature = options.temperature;
1343
1517
  }
@@ -1366,19 +1540,19 @@ async function* runStream3(options) {
1366
1540
  });
1367
1541
  if (!response.ok) {
1368
1542
  const text = await response.text().catch(() => "");
1369
- let message = `Codex API error (${response.status}): ${text}`;
1543
+ const parsed = parseCodexErrorBody(text);
1544
+ const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
1545
+ const requestId = parsed.requestId ?? response.headers.get("x-request-id") ?? response.headers.get("openai-request-id") ?? void 0;
1546
+ let hint;
1370
1547
  if (response.status === 400 && text.includes("not supported")) {
1371
- message += `
1372
-
1373
- 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`;
1374
- }
1375
- if (response.status === 404 && text.includes("does not exist")) {
1376
- message += `
1377
-
1378
- 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.`;
1548
+ 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.';
1549
+ } else if (response.status === 404 && text.includes("does not exist")) {
1550
+ 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.";
1379
1551
  }
1380
1552
  throw new ProviderError("openai", message, {
1381
- statusCode: response.status
1553
+ statusCode: response.status,
1554
+ ...requestId ? { requestId } : {},
1555
+ ...hint ? { hint } : {}
1382
1556
  });
1383
1557
  }
1384
1558
  if (!response.body) {
@@ -1389,16 +1563,27 @@ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GP
1389
1563
  const toolCalls = /* @__PURE__ */ new Map();
1390
1564
  let inputTokens = 0;
1391
1565
  let outputTokens = 0;
1566
+ let cacheRead = 0;
1392
1567
  for await (const event of parseSSE(response.body)) {
1393
1568
  const type = event.type;
1394
1569
  if (!type) continue;
1395
1570
  if (type === "error") {
1396
- const msg = event.message || JSON.stringify(event);
1397
- throw new ProviderError("openai", `Codex error: ${msg}`);
1571
+ const nested = event.error ?? void 0;
1572
+ const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
1573
+ const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
1574
+ const requestId = extractCodexRequestId(message) ?? event.request_id;
1575
+ throw new ProviderError("openai", message, {
1576
+ ...requestId != null ? { requestId } : {},
1577
+ ...code === "server_error" ? { statusCode: 500 } : {}
1578
+ });
1398
1579
  }
1399
1580
  if (type === "response.failed") {
1400
- const msg = event.error?.message || "Codex response failed";
1401
- throw new ProviderError("openai", msg);
1581
+ const nested = event.error;
1582
+ const message = nested?.message ?? "Codex response failed.";
1583
+ const requestId = extractCodexRequestId(message) ?? event.request_id;
1584
+ throw new ProviderError("openai", message, {
1585
+ ...requestId != null ? { requestId } : {}
1586
+ });
1402
1587
  }
1403
1588
  if (type === "response.output_text.delta") {
1404
1589
  const delta = event.delta;
@@ -1471,7 +1656,8 @@ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GP
1471
1656
  const resp = event.response;
1472
1657
  const usage = resp?.usage;
1473
1658
  if (usage) {
1474
- inputTokens = usage.input_tokens ?? 0;
1659
+ cacheRead = usage.input_tokens_details?.cached_tokens ?? 0;
1660
+ inputTokens = (usage.input_tokens ?? 0) - cacheRead;
1475
1661
  outputTokens = usage.output_tokens ?? 0;
1476
1662
  }
1477
1663
  }
@@ -1501,7 +1687,7 @@ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GP
1501
1687
  content: contentParts.length > 0 ? contentParts : textAccum || ""
1502
1688
  },
1503
1689
  stopReason,
1504
- usage: { inputTokens, outputTokens }
1690
+ usage: { inputTokens, outputTokens, ...cacheRead > 0 && { cacheRead } }
1505
1691
  };
1506
1692
  yield { type: "done", stopReason };
1507
1693
  return streamResponse;
@@ -1643,6 +1829,23 @@ function toCodexTools(tools) {
1643
1829
  strict: null
1644
1830
  }));
1645
1831
  }
1832
+ function extractCodexRequestId(message) {
1833
+ const match = message.match(/request ID ([a-z0-9-]{8,})/i);
1834
+ return match?.[1];
1835
+ }
1836
+ function parseCodexErrorBody(text) {
1837
+ if (!text) return {};
1838
+ try {
1839
+ const parsed = JSON.parse(text);
1840
+ const error = parsed.error;
1841
+ const message = error?.message ?? parsed.message;
1842
+ const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractCodexRequestId(message) : void 0);
1843
+ return { ...message ? { message } : {}, ...requestId ? { requestId } : {} };
1844
+ } catch {
1845
+ const trimmed = text.trim().slice(0, 240);
1846
+ return trimmed ? { message: trimmed } : {};
1847
+ }
1848
+ }
1646
1849
 
1647
1850
  // src/provider-registry.ts
1648
1851
  var ProviderRegistryImpl = class {
@@ -1903,6 +2106,8 @@ function registerPalsuProvider(config) {
1903
2106
  GGAIError,
1904
2107
  ProviderError,
1905
2108
  StreamResult,
2109
+ formatError,
2110
+ formatErrorForDisplay,
1906
2111
  palsuAssistantMessage,
1907
2112
  palsuText,
1908
2113
  palsuThinking,