@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.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  type Provider = "anthropic" | "xiaomi" | "openai" | "glm" | "moonshot" | "minimax" | "deepseek" | "openrouter" | "palsu";
4
- type ThinkingLevel = "low" | "medium" | "high" | "max";
4
+ type ThinkingLevel = "low" | "medium" | "high" | "xhigh";
5
5
  type CacheRetention = "none" | "short" | "long";
6
6
  interface TextContent {
7
7
  type: "text";
@@ -292,17 +292,74 @@ declare class ProviderRegistryImpl {
292
292
  /** Global provider registry. Import this to register custom providers. */
293
293
  declare const providerRegistry: ProviderRegistryImpl;
294
294
 
295
+ /**
296
+ * Error model for gg-ai and downstream consumers.
297
+ *
298
+ * Every error users see should answer one question: "is this me or them?"
299
+ * That answer drives whether they retry, switch model, log in, or report a
300
+ * ggcoder bug. The `FormattedError` shape captures it in plain English:
301
+ *
302
+ * ✗ OpenAI returned an error.
303
+ * An error occurred while processing your request...
304
+ * → This is an OpenAI issue, not ggcoder. Retry — if it persists, check status.openai.com.
305
+ *
306
+ * ✗ ggcoder hit an unexpected error.
307
+ * Cannot read property 'foo' of undefined
308
+ * → This is a ggcoder bug — please report it.
309
+ */
310
+ type ErrorSource = "provider" | "ggcoder" | "network" | "auth";
311
+ interface FormattedError {
312
+ /** Plain-English headline, e.g. "OpenAI returned an error." */
313
+ headline: string;
314
+ /** Machine-readable classification. */
315
+ source: ErrorSource;
316
+ /** Detailed message body from the underlying error (no JSON, no tag prefix). */
317
+ message: string;
318
+ /** Action line — tells the user whether to retry, switch model, log in, or report a bug. */
319
+ guidance: string;
320
+ /** Provider name when source === "provider". */
321
+ provider?: string;
322
+ /** HTTP status code if known. */
323
+ statusCode?: number;
324
+ /** Provider request ID, kept for telemetry / debug — not shown by default. */
325
+ requestId?: string;
326
+ }
295
327
  declare class GGAIError extends Error {
296
- constructor(message: string, options?: ErrorOptions);
328
+ readonly source: ErrorSource;
329
+ readonly requestId?: string;
330
+ readonly hint?: string;
331
+ constructor(message: string, options?: {
332
+ source?: ErrorSource;
333
+ requestId?: string;
334
+ hint?: string;
335
+ cause?: unknown;
336
+ });
297
337
  }
298
338
  declare class ProviderError extends GGAIError {
299
339
  readonly provider: string;
300
340
  readonly statusCode?: number;
301
341
  constructor(provider: string, message: string, options?: {
302
342
  statusCode?: number;
343
+ requestId?: string;
344
+ hint?: string;
303
345
  cause?: unknown;
304
346
  });
305
347
  }
348
+ /**
349
+ * Normalise any thrown value into a structured display object. Always returns
350
+ * a non-empty `headline` and `guidance` so the UI never has to second-guess
351
+ * what to show the user.
352
+ */
353
+ declare function formatError(err: unknown): FormattedError;
354
+ /**
355
+ * Render a FormattedError as a multi-line string for terminal display.
356
+ *
357
+ * Format:
358
+ * <headline>
359
+ * <message>
360
+ * → <guidance>
361
+ */
362
+ declare function formatErrorForDisplay(err: unknown): string;
306
363
 
307
364
  interface PalsuProviderState {
308
365
  callCount: number;
@@ -373,4 +430,4 @@ interface PalsuProviderConfig {
373
430
  */
374
431
  declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
375
432
 
376
- export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, EventStream, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, stream };
433
+ export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, formatError, formatErrorForDisplay, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, stream };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  type Provider = "anthropic" | "xiaomi" | "openai" | "glm" | "moonshot" | "minimax" | "deepseek" | "openrouter" | "palsu";
4
- type ThinkingLevel = "low" | "medium" | "high" | "max";
4
+ type ThinkingLevel = "low" | "medium" | "high" | "xhigh";
5
5
  type CacheRetention = "none" | "short" | "long";
6
6
  interface TextContent {
7
7
  type: "text";
@@ -292,17 +292,74 @@ declare class ProviderRegistryImpl {
292
292
  /** Global provider registry. Import this to register custom providers. */
293
293
  declare const providerRegistry: ProviderRegistryImpl;
294
294
 
295
+ /**
296
+ * Error model for gg-ai and downstream consumers.
297
+ *
298
+ * Every error users see should answer one question: "is this me or them?"
299
+ * That answer drives whether they retry, switch model, log in, or report a
300
+ * ggcoder bug. The `FormattedError` shape captures it in plain English:
301
+ *
302
+ * ✗ OpenAI returned an error.
303
+ * An error occurred while processing your request...
304
+ * → This is an OpenAI issue, not ggcoder. Retry — if it persists, check status.openai.com.
305
+ *
306
+ * ✗ ggcoder hit an unexpected error.
307
+ * Cannot read property 'foo' of undefined
308
+ * → This is a ggcoder bug — please report it.
309
+ */
310
+ type ErrorSource = "provider" | "ggcoder" | "network" | "auth";
311
+ interface FormattedError {
312
+ /** Plain-English headline, e.g. "OpenAI returned an error." */
313
+ headline: string;
314
+ /** Machine-readable classification. */
315
+ source: ErrorSource;
316
+ /** Detailed message body from the underlying error (no JSON, no tag prefix). */
317
+ message: string;
318
+ /** Action line — tells the user whether to retry, switch model, log in, or report a bug. */
319
+ guidance: string;
320
+ /** Provider name when source === "provider". */
321
+ provider?: string;
322
+ /** HTTP status code if known. */
323
+ statusCode?: number;
324
+ /** Provider request ID, kept for telemetry / debug — not shown by default. */
325
+ requestId?: string;
326
+ }
295
327
  declare class GGAIError extends Error {
296
- constructor(message: string, options?: ErrorOptions);
328
+ readonly source: ErrorSource;
329
+ readonly requestId?: string;
330
+ readonly hint?: string;
331
+ constructor(message: string, options?: {
332
+ source?: ErrorSource;
333
+ requestId?: string;
334
+ hint?: string;
335
+ cause?: unknown;
336
+ });
297
337
  }
298
338
  declare class ProviderError extends GGAIError {
299
339
  readonly provider: string;
300
340
  readonly statusCode?: number;
301
341
  constructor(provider: string, message: string, options?: {
302
342
  statusCode?: number;
343
+ requestId?: string;
344
+ hint?: string;
303
345
  cause?: unknown;
304
346
  });
305
347
  }
348
+ /**
349
+ * Normalise any thrown value into a structured display object. Always returns
350
+ * a non-empty `headline` and `guidance` so the UI never has to second-guess
351
+ * what to show the user.
352
+ */
353
+ declare function formatError(err: unknown): FormattedError;
354
+ /**
355
+ * Render a FormattedError as a multi-line string for terminal display.
356
+ *
357
+ * Format:
358
+ * <headline>
359
+ * <message>
360
+ * → <guidance>
361
+ */
362
+ declare function formatErrorForDisplay(err: unknown): string;
306
363
 
307
364
  interface PalsuProviderState {
308
365
  callCount: number;
@@ -373,4 +430,4 @@ interface PalsuProviderConfig {
373
430
  */
374
431
  declare function registerPalsuProvider(config?: PalsuProviderConfig): PalsuProviderHandle;
375
432
 
376
- export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, EventStream, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, stream };
433
+ export { type AssistantMessage, type CacheRetention, type ContentPart, type DoneEvent, type ErrorEvent, type ErrorSource, EventStream, type FormattedError, GGAIError, type ImageContent, type Message, type PalsuModelConfig, type PalsuModelHandle, type PalsuProviderConfig, type PalsuProviderHandle, type PalsuProviderState, type PalsuResponse, type PalsuResponseFactory, type Provider, type ProviderEntry, ProviderError, type ProviderStreamFn, type RawContent, type ServerToolCall, type ServerToolCallEvent, type ServerToolDefinition, type ServerToolResult, type ServerToolResultEvent, type StopReason, type StreamEvent, type StreamOptions, type StreamResponse, StreamResult, type SystemMessage, type TextContent, type TextDeltaEvent, type ThinkingContent, type ThinkingDeltaEvent, type ThinkingLevel, type Tool, type ToolCall, type ToolCallDeltaEvent, type ToolCallDoneEvent, type ToolChoice, type ToolResult, type ToolResultContent, type ToolResultMessage, type Usage, type UserMessage, formatError, formatErrorForDisplay, palsuAssistantMessage, palsuText, palsuThinking, palsuToolCall, providerRegistry, registerPalsuProvider, stream };
package/dist/index.js CHANGED
@@ -1,20 +1,164 @@
1
1
  // src/errors.ts
2
2
  var GGAIError = class extends Error {
3
+ source;
4
+ requestId;
5
+ hint;
3
6
  constructor(message, options) {
4
- super(message, options);
7
+ super(message, { cause: options?.cause });
5
8
  this.name = "GGAIError";
9
+ this.source = options?.source ?? "ggcoder";
10
+ this.requestId = options?.requestId;
11
+ this.hint = options?.hint;
6
12
  }
7
13
  };
8
14
  var ProviderError = class extends GGAIError {
9
15
  provider;
10
16
  statusCode;
11
17
  constructor(provider, message, options) {
12
- super(`[${provider}] ${message}`, { cause: options?.cause });
18
+ super(message, {
19
+ source: "provider",
20
+ requestId: options?.requestId,
21
+ hint: options?.hint,
22
+ cause: options?.cause
23
+ });
13
24
  this.name = "ProviderError";
14
25
  this.provider = provider;
15
26
  this.statusCode = options?.statusCode;
16
27
  }
17
28
  };
29
+ var PROVIDER_DISPLAY = {
30
+ openai: "OpenAI",
31
+ anthropic: "Anthropic",
32
+ glm: "Z.AI (GLM)",
33
+ moonshot: "Moonshot",
34
+ deepseek: "DeepSeek",
35
+ openrouter: "OpenRouter",
36
+ xiaomi: "Xiaomi (MiMo)",
37
+ minimax: "MiniMax"
38
+ };
39
+ var PROVIDER_STATUS_URL = {
40
+ openai: "status.openai.com",
41
+ anthropic: "status.anthropic.com"
42
+ };
43
+ function providerDisplayName(provider) {
44
+ return PROVIDER_DISPLAY[provider] ?? provider;
45
+ }
46
+ function formatError(err) {
47
+ if (err instanceof ProviderError) {
48
+ const name = providerDisplayName(err.provider);
49
+ const cleanMessage = cleanProviderMessage(err.message);
50
+ return {
51
+ headline: `${name} returned an error.`,
52
+ source: "provider",
53
+ message: cleanMessage,
54
+ provider: err.provider,
55
+ statusCode: err.statusCode,
56
+ requestId: err.requestId,
57
+ guidance: err.hint ?? providerGuidance(err.provider, cleanMessage, err.statusCode)
58
+ };
59
+ }
60
+ if (err instanceof GGAIError) {
61
+ return finaliseBySource(err.source, err.message, err.requestId, err.hint);
62
+ }
63
+ if (err instanceof Error) {
64
+ const source = inferSource(err);
65
+ return finaliseBySource(source, err.message, void 0, void 0);
66
+ }
67
+ return finaliseBySource("ggcoder", String(err), void 0, void 0);
68
+ }
69
+ function finaliseBySource(source, message, requestId, hint) {
70
+ switch (source) {
71
+ case "network":
72
+ return {
73
+ headline: "Network error \u2014 couldn't reach the provider.",
74
+ source,
75
+ message,
76
+ guidance: hint ?? "Check your internet connection. Not a ggcoder issue \u2014 retry shortly.",
77
+ ...requestId ? { requestId } : {}
78
+ };
79
+ case "auth":
80
+ return {
81
+ headline: "Authentication issue.",
82
+ source,
83
+ message,
84
+ guidance: hint ?? "Run `ggcoder login` to refresh your credentials.",
85
+ ...requestId ? { requestId } : {}
86
+ };
87
+ case "provider":
88
+ return {
89
+ headline: "Provider returned an error.",
90
+ source,
91
+ message,
92
+ guidance: hint ?? providerGuidance(void 0, message, void 0),
93
+ ...requestId ? { requestId } : {}
94
+ };
95
+ case "ggcoder":
96
+ return {
97
+ headline: "ggcoder hit an unexpected error.",
98
+ source,
99
+ message,
100
+ guidance: hint ?? "This looks like a ggcoder bug \u2014 please report it to the developer (see /help).",
101
+ ...requestId ? { requestId } : {}
102
+ };
103
+ }
104
+ }
105
+ function formatErrorForDisplay(err) {
106
+ const f = formatError(err);
107
+ const lines = [f.headline];
108
+ if (f.message && f.message !== f.headline) lines.push(` ${f.message}`);
109
+ lines.push(` \u2192 ${f.guidance}`);
110
+ return lines.join("\n");
111
+ }
112
+ function cleanProviderMessage(message) {
113
+ return message.replace(/^\[[^\]]+\]\s*/, "").trim();
114
+ }
115
+ function inferSource(err) {
116
+ const msg = err.message.toLowerCase();
117
+ const code = err.code ?? "";
118
+ if (code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "ENOTFOUND" || code === "ECONNRESET" || msg.includes("fetch failed") || msg.includes("network request failed")) {
119
+ return "network";
120
+ }
121
+ if (msg.includes("not logged in") || msg.includes("token exchange failed") || msg.includes("token refresh failed") || msg.includes("invalid_grant")) {
122
+ return "auth";
123
+ }
124
+ return "ggcoder";
125
+ }
126
+ function providerGuidance(provider, message, statusCode) {
127
+ const name = provider ? providerDisplayName(provider) : "the provider";
128
+ const status = provider ? PROVIDER_STATUS_URL[provider] : void 0;
129
+ const lower = message.toLowerCase();
130
+ if (statusCode === 401 || lower.includes("unauthorized") || lower.includes("invalid api key")) {
131
+ return `Authentication failed with ${name}. Run \`ggcoder login\` to refresh your credentials.`;
132
+ }
133
+ if (lower.includes("overloaded") || lower.includes("engine_overloaded")) {
134
+ return `${name}'s servers are overloaded right now. Retry in a moment \u2014 not a ggcoder issue.`;
135
+ }
136
+ if (lower.includes("insufficient balance") || lower.includes("quota exceeded") || lower.includes("recharge") || lower.includes("no resource package")) {
137
+ return `Your ${name} account has a billing or quota issue \u2014 check your balance. Not a ggcoder issue.`;
138
+ }
139
+ if (statusCode === 429 || lower.includes("rate limit") || lower.includes("too many requests")) {
140
+ return `${name} rate limit hit. Wait a moment then retry \u2014 not a ggcoder issue.`;
141
+ }
142
+ if (statusCode === 502 || lower.includes("bad gateway")) {
143
+ return `${name} returned a bad gateway. Retry \u2014 this is on their side, not ggcoder.`;
144
+ }
145
+ if (statusCode === 503 || lower.includes("service unavailable")) {
146
+ return `${name} is temporarily unavailable. Retry shortly \u2014 not a ggcoder issue.`;
147
+ }
148
+ if (statusCode === 500 || lower.includes("server_error") || lower.includes("500") && lower.includes("internal server error")) {
149
+ 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.`;
150
+ }
151
+ if (lower.includes("timeout") || lower.includes("timed out")) {
152
+ return `Request to ${name} timed out. Their servers may be slow \u2014 retry. Not a ggcoder issue.`;
153
+ }
154
+ if (lower.includes("does not recognize the requested model") || lower.includes("model") && (lower.includes("not exist") || lower.includes("not found") || lower.includes("no access"))) {
155
+ return `${name} doesn't recognise this model on your account. Use /model to switch, or check your subscription tier.`;
156
+ }
157
+ if (lower.includes("context_length_exceeded") || lower.includes("prompt is too long")) {
158
+ return `Context window for this ${name} model is full. Run /compact to shrink history, or start a new session.`;
159
+ }
160
+ 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.`;
161
+ }
18
162
 
19
163
  // src/providers/anthropic.ts
20
164
  import Anthropic from "@anthropic-ai/sdk";
@@ -382,8 +526,8 @@ function supportsAdaptiveThinking(model) {
382
526
  }
383
527
  function toAnthropicThinking(level, maxTokens, model) {
384
528
  if (supportsAdaptiveThinking(model)) {
385
- let effort = level;
386
- if (level === "max" && !model.includes("opus")) {
529
+ let effort = level === "xhigh" ? "max" : level;
530
+ if (effort === "max" && !model.includes("opus")) {
387
531
  effort = "high";
388
532
  }
389
533
  return {
@@ -392,7 +536,7 @@ function toAnthropicThinking(level, maxTokens, model) {
392
536
  outputConfig: { effort }
393
537
  };
394
538
  }
395
- const effectiveLevel = level === "max" ? "high" : level;
539
+ const effectiveLevel = level === "xhigh" ? "high" : level;
396
540
  const budgetMap = {
397
541
  low: Math.max(1024, Math.floor(maxTokens * 0.25)),
398
542
  medium: Math.max(2048, Math.floor(maxTokens * 0.5)),
@@ -525,8 +669,12 @@ function toOpenAIToolChoice(choice) {
525
669
  if (choice === "required") return "required";
526
670
  return { type: "function", function: { name: choice.name } };
527
671
  }
528
- function toOpenAIReasoningEffort(level) {
529
- return level === "max" ? "high" : level;
672
+ function isOpenAIProVariant(model) {
673
+ return /^gpt-5\.\d+-pro$/i.test(model);
674
+ }
675
+ function toOpenAIReasoningEffort(level, model) {
676
+ if (isOpenAIProVariant(model) && level === "low") return "medium";
677
+ return level;
530
678
  }
531
679
  function normalizeAnthropicStopReason(reason) {
532
680
  switch (reason) {
@@ -949,8 +1097,10 @@ function messageToResponse(message) {
949
1097
  }
950
1098
  function toError(err) {
951
1099
  if (err instanceof Anthropic.APIError) {
1100
+ const requestId = err.request_id ?? err.error?.request_id;
952
1101
  return new ProviderError("anthropic", err.message, {
953
1102
  statusCode: err.status,
1103
+ ...requestId ? { requestId } : {},
954
1104
  cause: err
955
1105
  });
956
1106
  }
@@ -993,7 +1143,7 @@ async function* runStream2(options) {
993
1143
  ...effectiveTemp != null && !options.thinking ? { temperature: effectiveTemp } : {},
994
1144
  ...options.topP != null ? { top_p: options.topP } : {},
995
1145
  ...options.stop ? { stop: options.stop } : {},
996
- ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking) } : {},
1146
+ ...options.thinking && !usesThinkingParam ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
997
1147
  ...options.tools?.length ? { tools: toOpenAITools(options.tools) } : {},
998
1148
  ...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
999
1149
  ...useStreaming ? { stream_options: { include_usage: true } } : {}
@@ -1242,19 +1392,19 @@ function completionToResponse(completion) {
1242
1392
  }
1243
1393
  function toError2(err, provider = "openai") {
1244
1394
  if (err instanceof OpenAI.APIError) {
1245
- let msg = err.message;
1246
1395
  const body = err.error;
1247
- if (body) {
1248
- const modelName = body.model || "";
1249
- const _code = body.code || "";
1250
- const message = body.message || "";
1251
- if (modelName === "codex-mini-latest" || message.includes("codex-mini-latest")) {
1252
- 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.`;
1253
- }
1254
- msg += ` | body: ${JSON.stringify(body)}`;
1255
- }
1256
- return new ProviderError(provider, msg, {
1396
+ const bodyMessage = typeof body?.message === "string" && body.message.trim() ? body.message.trim() : void 0;
1397
+ const modelName = typeof body?.model === "string" ? body.model : "";
1398
+ const cleanMessage = bodyMessage ?? err.message;
1399
+ let hint;
1400
+ if (modelName === "codex-mini-latest" || cleanMessage.includes("codex-mini-latest")) {
1401
+ 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.";
1402
+ }
1403
+ const requestId = err.request_id ?? (typeof body?.request_id === "string" ? body.request_id : void 0);
1404
+ return new ProviderError(provider, cleanMessage, {
1257
1405
  statusCode: err.status,
1406
+ ...requestId ? { requestId } : {},
1407
+ ...hint ? { hint } : {},
1258
1408
  cause: err
1259
1409
  });
1260
1410
  }
@@ -1316,19 +1466,19 @@ async function* runStream3(options) {
1316
1466
  });
1317
1467
  if (!response.ok) {
1318
1468
  const text = await response.text().catch(() => "");
1319
- let message = `Codex API error (${response.status}): ${text}`;
1469
+ const parsed = parseCodexErrorBody(text);
1470
+ const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
1471
+ const requestId = parsed.requestId ?? response.headers.get("x-request-id") ?? response.headers.get("openai-request-id") ?? void 0;
1472
+ let hint;
1320
1473
  if (response.status === 400 && text.includes("not supported")) {
1321
- message += `
1322
-
1323
- 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`;
1324
- }
1325
- if (response.status === 404 && text.includes("does not exist")) {
1326
- message += `
1327
-
1328
- 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.`;
1474
+ 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.';
1475
+ } else if (response.status === 404 && text.includes("does not exist")) {
1476
+ 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.";
1329
1477
  }
1330
1478
  throw new ProviderError("openai", message, {
1331
- statusCode: response.status
1479
+ statusCode: response.status,
1480
+ ...requestId ? { requestId } : {},
1481
+ ...hint ? { hint } : {}
1332
1482
  });
1333
1483
  }
1334
1484
  if (!response.body) {
@@ -1343,12 +1493,22 @@ Hint: codex-mini-latest requires an OpenAI Pro ($200/mo) or Max subscription. GP
1343
1493
  const type = event.type;
1344
1494
  if (!type) continue;
1345
1495
  if (type === "error") {
1346
- const msg = event.message || JSON.stringify(event);
1347
- throw new ProviderError("openai", `Codex error: ${msg}`);
1496
+ const nested = event.error ?? void 0;
1497
+ const message = nested?.message ?? event.message ?? "Codex stream emitted an error chunk without a message.";
1498
+ const code = nested?.code ?? nested?.type ?? event.code ?? "server_error";
1499
+ const requestId = extractCodexRequestId(message) ?? event.request_id;
1500
+ throw new ProviderError("openai", message, {
1501
+ ...requestId != null ? { requestId } : {},
1502
+ ...code === "server_error" ? { statusCode: 500 } : {}
1503
+ });
1348
1504
  }
1349
1505
  if (type === "response.failed") {
1350
- const msg = event.error?.message || "Codex response failed";
1351
- throw new ProviderError("openai", msg);
1506
+ const nested = event.error;
1507
+ const message = nested?.message ?? "Codex response failed.";
1508
+ const requestId = extractCodexRequestId(message) ?? event.request_id;
1509
+ throw new ProviderError("openai", message, {
1510
+ ...requestId != null ? { requestId } : {}
1511
+ });
1352
1512
  }
1353
1513
  if (type === "response.output_text.delta") {
1354
1514
  const delta = event.delta;
@@ -1593,6 +1753,23 @@ function toCodexTools(tools) {
1593
1753
  strict: null
1594
1754
  }));
1595
1755
  }
1756
+ function extractCodexRequestId(message) {
1757
+ const match = message.match(/request ID ([a-z0-9-]{8,})/i);
1758
+ return match?.[1];
1759
+ }
1760
+ function parseCodexErrorBody(text) {
1761
+ if (!text) return {};
1762
+ try {
1763
+ const parsed = JSON.parse(text);
1764
+ const error = parsed.error;
1765
+ const message = error?.message ?? parsed.message;
1766
+ const requestId = parsed.request_id ?? error?.request_id ?? (message ? extractCodexRequestId(message) : void 0);
1767
+ return { ...message ? { message } : {}, ...requestId ? { requestId } : {} };
1768
+ } catch {
1769
+ const trimmed = text.trim().slice(0, 240);
1770
+ return trimmed ? { message: trimmed } : {};
1771
+ }
1772
+ }
1596
1773
 
1597
1774
  // src/provider-registry.ts
1598
1775
  var ProviderRegistryImpl = class {
@@ -1852,6 +2029,8 @@ export {
1852
2029
  GGAIError,
1853
2030
  ProviderError,
1854
2031
  StreamResult,
2032
+ formatError,
2033
+ formatErrorForDisplay,
1855
2034
  palsuAssistantMessage,
1856
2035
  palsuText,
1857
2036
  palsuThinking,