@vellumai/assistant 0.10.2-dev.202606242023.5e459ba → 0.10.2-dev.202606242135.63f618e

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.
Files changed (28) hide show
  1. package/openapi.yaml +48 -0
  2. package/package.json +1 -1
  3. package/src/__tests__/config-loader-backfill.test.ts +108 -0
  4. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  5. package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
  6. package/src/__tests__/openai-provider.test.ts +22 -12
  7. package/src/__tests__/openai-responses-provider.test.ts +12 -2
  8. package/src/__tests__/provider-error-scenarios.test.ts +5 -4
  9. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
  10. package/src/api/responses/llm-request-log-entry.ts +4 -0
  11. package/src/config/loader.ts +2 -0
  12. package/src/config/prune-seeded-callsite-defaults.ts +110 -0
  13. package/src/daemon/mcp-reload-service.ts +10 -0
  14. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
  15. package/src/mcp/client.ts +15 -1
  16. package/src/mcp/mcp-auth-orchestrator.ts +6 -1
  17. package/src/mcp/mcp-header-store.ts +134 -0
  18. package/src/memory/llm-request-log-store.ts +26 -1
  19. package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
  20. package/src/providers/openai/api-error-normalization.ts +270 -0
  21. package/src/providers/openai/chat-completions-provider.ts +37 -83
  22. package/src/providers/openai/responses-provider.ts +45 -46
  23. package/src/runtime/routes/llm-context-normalization.ts +12 -0
  24. package/src/runtime/routes/mcp-auth-routes.ts +78 -11
  25. package/src/util/errors.ts +26 -1
  26. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
  27. package/src/workspace/migrations/registry.ts +2 -0
  28. package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
package/openapi.yaml CHANGED
@@ -8408,6 +8408,22 @@ paths:
8408
8408
  anyOf:
8409
8409
  - type: number
8410
8410
  - type: "null"
8411
+ apiErrorCode:
8412
+ anyOf:
8413
+ - type: string
8414
+ - type: "null"
8415
+ apiErrorType:
8416
+ anyOf:
8417
+ - type: string
8418
+ - type: "null"
8419
+ apiErrorParam:
8420
+ anyOf:
8421
+ - type: string
8422
+ - type: "null"
8423
+ requestId:
8424
+ anyOf:
8425
+ - type: string
8426
+ - type: "null"
8411
8427
  additionalProperties: false
8412
8428
  - type: "null"
8413
8429
  required:
@@ -15352,6 +15368,22 @@ paths:
15352
15368
  anyOf:
15353
15369
  - type: number
15354
15370
  - type: "null"
15371
+ apiErrorCode:
15372
+ anyOf:
15373
+ - type: string
15374
+ - type: "null"
15375
+ apiErrorType:
15376
+ anyOf:
15377
+ - type: string
15378
+ - type: "null"
15379
+ apiErrorParam:
15380
+ anyOf:
15381
+ - type: string
15382
+ - type: "null"
15383
+ requestId:
15384
+ anyOf:
15385
+ - type: string
15386
+ - type: "null"
15355
15387
  additionalProperties: false
15356
15388
  - type: "null"
15357
15389
  required:
@@ -17809,6 +17841,22 @@ paths:
17809
17841
  anyOf:
17810
17842
  - type: number
17811
17843
  - type: "null"
17844
+ apiErrorCode:
17845
+ anyOf:
17846
+ - type: string
17847
+ - type: "null"
17848
+ apiErrorType:
17849
+ anyOf:
17850
+ - type: string
17851
+ - type: "null"
17852
+ apiErrorParam:
17853
+ anyOf:
17854
+ - type: string
17855
+ - type: "null"
17856
+ requestId:
17857
+ anyOf:
17858
+ - type: string
17859
+ - type: "null"
17812
17860
  additionalProperties: false
17813
17861
  - type: "null"
17814
17862
  required:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.10.2-dev.202606242023.5e459ba",
3
+ "version": "0.10.2-dev.202606242135.63f618e",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -98,6 +98,64 @@ function writeConfig(obj: unknown): void {
98
98
  writeFileSync(CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n");
99
99
  }
100
100
 
101
+ function latencySeed(): Record<string, unknown> {
102
+ return {
103
+ model: "claude-haiku-4-5-20251001",
104
+ effort: "low",
105
+ thinking: { enabled: false },
106
+ };
107
+ }
108
+
109
+ function fullSeededCallSites(): Record<string, Record<string, unknown>> {
110
+ return {
111
+ guardianQuestionCopy: latencySeed(),
112
+ interactionClassifier: latencySeed(),
113
+ skillCategoryInference: latencySeed(),
114
+ inviteInstructionGenerator: latencySeed(),
115
+ notificationDecision: latencySeed(),
116
+ preferenceExtraction: latencySeed(),
117
+ commitMessage: {
118
+ model: "claude-haiku-4-5-20251001",
119
+ maxTokens: 120,
120
+ temperature: 0.2,
121
+ effort: "low",
122
+ thinking: { enabled: false },
123
+ },
124
+ conversationStarters: latencySeed(),
125
+ conversationSummarization: {
126
+ model: "claude-opus-4-7",
127
+ effort: "low",
128
+ thinking: { enabled: false },
129
+ },
130
+ recall: {
131
+ profile: "cost-optimized",
132
+ maxTokens: 4096,
133
+ effort: "low",
134
+ thinking: { enabled: false, streamThinking: false },
135
+ temperature: 0,
136
+ disableCache: true,
137
+ },
138
+ heartbeatAgent: {
139
+ profile: "cost-optimized",
140
+ maxTokens: 2048,
141
+ effort: "low",
142
+ temperature: 0,
143
+ thinking: { enabled: false, streamThinking: false },
144
+ contextWindow: { maxInputTokens: 16000 },
145
+ },
146
+ replySuggestion: {
147
+ model: "claude-haiku-4-5-20251001",
148
+ effort: "low",
149
+ thinking: { enabled: false },
150
+ disableCache: true,
151
+ },
152
+ memoryRouter: {
153
+ profile: "cost-optimized",
154
+ contextWindow: { maxInputTokens: 1_000_000 },
155
+ },
156
+ };
157
+ }
158
+
101
159
  function mergeDefaultConfigAndSeedInferenceProfiles(db?: DrizzleDb): void {
102
160
  const defaultConfigMerge = mergeDefaultWorkspaceConfig();
103
161
  seedInferenceProfiles({
@@ -372,6 +430,56 @@ describe("loadConfig startup behavior", () => {
372
430
  expect(config.memory.v2.bm25_b).toBe(0.4);
373
431
  });
374
432
 
433
+ test("default workspace config merge prunes exact seeded call-site defaults", () => {
434
+ const seededCallSites = fullSeededCallSites();
435
+ const overlayPath = join(WORKSPACE_DIR, "hatch-overlay.json");
436
+ writeFileSync(
437
+ overlayPath,
438
+ JSON.stringify(
439
+ {
440
+ gateway: {
441
+ unmappedPolicy: "default",
442
+ defaultAssistantId: "self",
443
+ },
444
+ llm: {
445
+ activeProfile: "balanced",
446
+ advisorProfile: "frontier",
447
+ callSites: {
448
+ ...seededCallSites,
449
+ recall: {
450
+ ...seededCallSites.recall,
451
+ disableCache: false,
452
+ },
453
+ customSite: { profile: "frontier" },
454
+ },
455
+ },
456
+ },
457
+ null,
458
+ 2,
459
+ ) + "\n",
460
+ );
461
+ process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH = overlayPath;
462
+
463
+ const result = mergeDefaultWorkspaceConfig();
464
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8")) as Record<
465
+ string,
466
+ Record<string, unknown>
467
+ >;
468
+ const llm = raw.llm as Record<string, unknown>;
469
+ const callSites = llm.callSites as Record<string, Record<string, unknown>>;
470
+
471
+ expect(result.hadOverlay).toBe(true);
472
+ expect(raw.gateway).toEqual({
473
+ unmappedPolicy: "default",
474
+ defaultAssistantId: "self",
475
+ });
476
+ expect(llm.activeProfile).toBe("balanced");
477
+ expect(llm.advisorProfile).toBe("frontier");
478
+ expect(Object.keys(callSites).sort()).toEqual(["customSite", "recall"]);
479
+ expect(callSites.recall?.disableCache).toBe(false);
480
+ expect(callSites.customSite).toEqual({ profile: "frontier" });
481
+ });
482
+
375
483
  test("reloads cached config when config.json is updated externally", () => {
376
484
  // Models a CLI subprocess writing twilio.accountSid while the assistant
377
485
  // process already has an effective config cached in memory.
@@ -182,6 +182,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
182
182
  "daemon/handlers/config-slack-channel.ts", // Slack channel config credential management
183
183
  "providers/platform-proxy/context.ts", // managed proxy API key lookup for provider initialization
184
184
  "platform/client.ts", // platform client credential store fallback for standalone CLI auth
185
+ "mcp/mcp-header-store.ts", // MCP static auth header persistence (credential store CRUD + legacy migration)
185
186
  "mcp/mcp-oauth-provider.ts", // MCP OAuth token/client/discovery persistence
186
187
  "runtime/routes/integrations/slack/token.ts", // shared Slack token resolver (bot/user token lookup for CLI use routes)
187
188
  "mcp/client.ts", // MCP client cached-token lookup
@@ -25,18 +25,15 @@
25
25
  import { describe, expect, test } from "bun:test";
26
26
 
27
27
  import { buildProviderErrorResponsePayload } from "../memory/llm-request-log-store.js";
28
- import {
29
- AssistantError,
30
- ErrorCode,
31
- ProviderError,
32
- } from "../util/errors.js";
28
+ import { AssistantError, ErrorCode, ProviderError } from "../util/errors.js";
33
29
 
34
- function persisted(err: Error): { error: Record<string, unknown> } {
30
+ function persisted(err: Error): {
31
+ error: Record<string, unknown>;
32
+ rawResponse?: unknown;
33
+ } {
35
34
  // Round-trip through JSON to assert on the actual stored shape, not the
36
35
  // in-memory object reference.
37
- return JSON.parse(
38
- JSON.stringify(buildProviderErrorResponsePayload(err)),
39
- );
36
+ return JSON.parse(JSON.stringify(buildProviderErrorResponsePayload(err)));
40
37
  }
41
38
 
42
39
  describe("buildProviderErrorResponsePayload", () => {
@@ -60,6 +57,34 @@ describe("buildProviderErrorResponsePayload", () => {
60
57
  });
61
58
  });
62
59
 
60
+ test("ProviderError serializes upstream provider error metadata when present", () => {
61
+ const err = new ProviderError(
62
+ "OpenAI API error (401): Invalid API key provided",
63
+ "openai",
64
+ 401,
65
+ {
66
+ apiErrorCode: "invalid_api_key",
67
+ apiErrorType: "invalid_request_error",
68
+ apiErrorParam: "api_key",
69
+ requestId: "req_abc123",
70
+ },
71
+ );
72
+ const got = persisted(err);
73
+ expect(got).toEqual({
74
+ error: {
75
+ name: "ProviderError",
76
+ message: "OpenAI API error (401): Invalid API key provided",
77
+ code: ErrorCode.PROVIDER_ERROR,
78
+ provider: "openai",
79
+ statusCode: 401,
80
+ apiErrorCode: "invalid_api_key",
81
+ apiErrorType: "invalid_request_error",
82
+ apiErrorParam: "api_key",
83
+ requestId: "req_abc123",
84
+ },
85
+ });
86
+ });
87
+
63
88
  test("ProviderError without optional metadata omits statusCode + retryAfterMs", () => {
64
89
  const err = new ProviderError(
65
90
  "Gemini API error: surprise internal state",
@@ -127,6 +152,43 @@ describe("buildProviderErrorResponsePayload", () => {
127
152
  });
128
153
  });
129
154
 
155
+ test("captured JSON rawBody is attached as a parsed rawResponse sibling", () => {
156
+ // So the inspector's Raw tab can render the actual upstream provider JSON
157
+ // (like a successful row) instead of only the extracted error fields.
158
+ const err = new ProviderError(
159
+ "Together AI API error (400): Model 'MiniMax-M3' is not supported.",
160
+ "together",
161
+ 400,
162
+ {
163
+ apiErrorCode: "model_not_supported",
164
+ rawBody: JSON.stringify({
165
+ detail: "Model 'MiniMax-M3' is not supported.",
166
+ }),
167
+ },
168
+ );
169
+ const got = persisted(err);
170
+ expect(got.error.apiErrorCode).toBe("model_not_supported");
171
+ expect(got.rawResponse).toEqual({
172
+ detail: "Model 'MiniMax-M3' is not supported.",
173
+ });
174
+ });
175
+
176
+ test("non-JSON rawBody (HTML/text error page) is kept verbatim as a string", () => {
177
+ const err = new ProviderError("Bad gateway", "openai", 400, {
178
+ rawBody: "<html><body>upstream timeout</body></html>",
179
+ });
180
+ const got = persisted(err);
181
+ expect(got.rawResponse).toBe("<html><body>upstream timeout</body></html>");
182
+ });
183
+
184
+ test("no captured rawBody omits the rawResponse sibling entirely", () => {
185
+ const err = new ProviderError("rate limited", "anthropic", 429, {
186
+ retryAfterMs: 1500,
187
+ });
188
+ const got = persisted(err);
189
+ expect("rawResponse" in got).toBe(false);
190
+ });
191
+
130
192
  test("ProviderError with statusCode 0 is still recorded (not coerced to undefined)", () => {
131
193
  // Defensive: `if (err.statusCode !== undefined)` correctly admits 0.
132
194
  // A raw `if (err.statusCode)` would drop it, so the test guards against
@@ -58,6 +58,16 @@ let lastConstructorOptions: Record<string, unknown> | null = null;
58
58
  let shouldThrow: Error | null = null;
59
59
  const DEFAULT_SDK_TIMEOUT_MS = 1_860_000;
60
60
 
61
+ // Each provider installs a `fetch` wrapper to capture raw error bodies, so the
62
+ // constructor options carry a `fetch` function; assert the meaningful options
63
+ // via objectContaining and confirm `fetch` is wired.
64
+ function expectOpenAIConstructorOptions(
65
+ expected: Record<string, unknown>,
66
+ ): void {
67
+ expect(lastConstructorOptions).toEqual(expect.objectContaining(expected));
68
+ expect(typeof lastConstructorOptions?.fetch).toBe("function");
69
+ }
70
+
61
71
  function userMsg(text: string): Message {
62
72
  return { role: "user", content: [{ type: "text", text }] };
63
73
  }
@@ -309,7 +319,7 @@ describe("OpenAIProvider", () => {
309
319
  });
310
320
 
311
321
  expect(compatible.name).toBe("ollama");
312
- expect(lastConstructorOptions).toEqual({
322
+ expectOpenAIConstructorOptions({
313
323
  apiKey: "sk-local",
314
324
  baseURL: "http://127.0.0.1:11434/v1",
315
325
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -322,7 +332,7 @@ describe("OpenAIProvider", () => {
322
332
  delete process.env.OLLAMA_BASE_URL;
323
333
  const ollama = new OllamaProvider("llama3.2");
324
334
  expect(ollama.name).toBe("ollama");
325
- expect(lastConstructorOptions).toEqual({
335
+ expectOpenAIConstructorOptions({
326
336
  apiKey: "ollama",
327
337
  baseURL: "http://127.0.0.1:11434/v1",
328
338
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -342,7 +352,7 @@ describe("OpenAIProvider", () => {
342
352
  process.env.OLLAMA_BASE_URL = " ";
343
353
  const ollama = new OllamaProvider("llama3.2");
344
354
  expect(ollama.name).toBe("ollama");
345
- expect(lastConstructorOptions).toEqual({
355
+ expectOpenAIConstructorOptions({
346
356
  apiKey: "ollama",
347
357
  baseURL: "http://127.0.0.1:11434/v1",
348
358
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -1260,7 +1270,7 @@ describe("custom baseURL initialization", () => {
1260
1270
  });
1261
1271
 
1262
1272
  expect(managed.name).toBe("openai");
1263
- expect(lastConstructorOptions).toEqual({
1273
+ expectOpenAIConstructorOptions({
1264
1274
  apiKey: "ast-key-123",
1265
1275
  baseURL: "https://platform.example.com/v1/runtime-proxy/openai",
1266
1276
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -1270,7 +1280,7 @@ describe("custom baseURL initialization", () => {
1270
1280
  test("OpenAIProvider without baseURL calls provider directly", () => {
1271
1281
  new OpenAIProvider("sk-user-key", "gpt-4o");
1272
1282
 
1273
- expect(lastConstructorOptions).toEqual({
1283
+ expectOpenAIConstructorOptions({
1274
1284
  apiKey: "sk-user-key",
1275
1285
  baseURL: undefined,
1276
1286
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -1282,7 +1292,7 @@ describe("custom baseURL initialization", () => {
1282
1292
  streamTimeoutMs: 300_000,
1283
1293
  });
1284
1294
 
1285
- expect(lastConstructorOptions).toEqual({
1295
+ expectOpenAIConstructorOptions({
1286
1296
  apiKey: "sk-user-key",
1287
1297
  baseURL: undefined,
1288
1298
  timeout: 360_000,
@@ -1299,7 +1309,7 @@ describe("custom baseURL initialization", () => {
1299
1309
  );
1300
1310
 
1301
1311
  expect(managed.name).toBe("fireworks");
1302
- expect(lastConstructorOptions).toEqual({
1312
+ expectOpenAIConstructorOptions({
1303
1313
  apiKey: "ast-key-123",
1304
1314
  baseURL: "https://platform.example.com/v1/runtime-proxy/fireworks",
1305
1315
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -1312,7 +1322,7 @@ describe("custom baseURL initialization", () => {
1312
1322
  "accounts/fireworks/models/llama-v3p1-70b-instruct",
1313
1323
  );
1314
1324
 
1315
- expect(lastConstructorOptions).toEqual({
1325
+ expectOpenAIConstructorOptions({
1316
1326
  apiKey: "fw-user-key",
1317
1327
  baseURL: "https://api.fireworks.ai/inference/v1",
1318
1328
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -1325,7 +1335,7 @@ describe("custom baseURL initialization", () => {
1325
1335
  });
1326
1336
 
1327
1337
  expect(managed.name).toBe("openrouter");
1328
- expect(lastConstructorOptions).toEqual({
1338
+ expectOpenAIConstructorOptions({
1329
1339
  apiKey: "ast-key-123",
1330
1340
  baseURL: "https://platform.example.com/v1/runtime-proxy/openrouter",
1331
1341
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -1335,7 +1345,7 @@ describe("custom baseURL initialization", () => {
1335
1345
  test("OpenRouterProvider without custom baseURL uses default OpenRouter URL", () => {
1336
1346
  new OpenRouterProvider("or-user-key", "openai/gpt-4o");
1337
1347
 
1338
- expect(lastConstructorOptions).toEqual({
1348
+ expectOpenAIConstructorOptions({
1339
1349
  apiKey: "or-user-key",
1340
1350
  baseURL: "https://openrouter.ai/api/v1",
1341
1351
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -1348,7 +1358,7 @@ describe("custom baseURL initialization", () => {
1348
1358
  });
1349
1359
 
1350
1360
  expect(managed.name).toBe("minimax");
1351
- expect(lastConstructorOptions).toEqual({
1361
+ expectOpenAIConstructorOptions({
1352
1362
  apiKey: "ast-key-123",
1353
1363
  baseURL: "https://platform.example.com/v1/runtime-proxy/minimax",
1354
1364
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -1358,7 +1368,7 @@ describe("custom baseURL initialization", () => {
1358
1368
  test("MinimaxProvider without custom baseURL uses default MiniMax URL", () => {
1359
1369
  new MinimaxProvider("mm-user-key", "MiniMax-M2.7");
1360
1370
 
1361
- expect(lastConstructorOptions).toEqual({
1371
+ expectOpenAIConstructorOptions({
1362
1372
  apiKey: "mm-user-key",
1363
1373
  baseURL: "https://api.minimax.io/v1",
1364
1374
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -24,6 +24,16 @@ let lastConstructorOptions: Record<string, unknown> | null = null;
24
24
  let shouldThrow: Error | null = null;
25
25
  const DEFAULT_SDK_TIMEOUT_MS = 1_860_000;
26
26
 
27
+ // Each provider installs a `fetch` wrapper to capture raw error bodies, so the
28
+ // constructor options carry a `fetch` function; assert the meaningful options
29
+ // via objectContaining and confirm `fetch` is wired.
30
+ function expectOpenAIConstructorOptions(
31
+ expected: Record<string, unknown>,
32
+ ): void {
33
+ expect(lastConstructorOptions).toEqual(expect.objectContaining(expected));
34
+ expect(typeof lastConstructorOptions?.fetch).toBe("function");
35
+ }
36
+
27
37
  // Simulate OpenAI.APIError
28
38
  class FakeAPIError extends Error {
29
39
  status: number;
@@ -218,7 +228,7 @@ describe("OpenAIResponsesProvider", () => {
218
228
  providerLabel: "Managed OpenAI",
219
229
  });
220
230
 
221
- expect(lastConstructorOptions).toEqual({
231
+ expectOpenAIConstructorOptions({
222
232
  apiKey: "sk-custom",
223
233
  baseURL: "https://proxy.example.com/v1",
224
234
  timeout: DEFAULT_SDK_TIMEOUT_MS,
@@ -230,7 +240,7 @@ describe("OpenAIResponsesProvider", () => {
230
240
  streamTimeoutMs: 300_000,
231
241
  });
232
242
 
233
- expect(lastConstructorOptions).toEqual({
243
+ expectOpenAIConstructorOptions({
234
244
  apiKey: "sk-custom",
235
245
  baseURL: undefined,
236
246
  timeout: 360_000,
@@ -837,9 +837,10 @@ describe("RetryProvider — streaming response handling", () => {
837
837
 
838
838
  test("does NOT retry OpenAI/Gemini-shaped 'Request was aborted' (no inner-timeout rewrite at those catch-sites)", async () => {
839
839
  // The OpenAI chat-completions, OpenAI responses, and Gemini catch-sites
840
- // format their errors as `"<Provider> API error (undefined): Request
841
- // was aborted."` (note the `(undefined)` parenthetical that the
842
- // Anthropic catch-site intentionally omits) and — unlike the Anthropic
840
+ // format their errors as `"<Provider> API error (<status>): Request
841
+ // was aborted."` (the OpenAI catch-sites render a missing status as
842
+ // `(unknown status)`; the Anthropic catch-site intentionally omits the
843
+ // parenthetical) and — unlike the Anthropic
843
844
  // catch-site — they do NOT rewrite their inner-streamTimeoutMs
844
845
  // deadline failures. A provider-agnostic transport-abort predicate
845
846
  // would burn three retries on what is by construction a deterministic
@@ -848,7 +849,7 @@ describe("RetryProvider — streaming response handling", () => {
848
849
  // wasted retry budget for non-Anthropic providers until their
849
850
  // catch-sites grow the same `innerTimeoutFired` distinction.
850
851
  const openaiAbortError = new ProviderError(
851
- "OpenAI API error (undefined): Request was aborted.",
852
+ "OpenAI API error (unknown status): Request was aborted.",
852
853
  "openai",
853
854
  undefined,
854
855
  );