@jacobbd/relay-ai 0.9.2 → 0.9.4

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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getTemplateById,
4
4
  init_provider_templates
5
- } from "./chunk-Q2FTCICO.js";
5
+ } from "./chunk-NYKVDBQC.js";
6
6
 
7
7
  // src/constants.ts
8
8
  import { homedir } from "os";
@@ -11,7 +11,7 @@ import { join } from "path";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@jacobbd/relay-ai",
14
- version: "0.9.2",
14
+ version: "0.9.4",
15
15
  publishConfig: {
16
16
  access: "public"
17
17
  },
@@ -385,9 +385,7 @@ function createResponsesLiteNormalizeState() {
385
385
  textDeltaForwarded: false,
386
386
  messageAddedIds: /* @__PURE__ */ new Set(),
387
387
  messageDoneIds: /* @__PURE__ */ new Set(),
388
- functionAddedIndexes: /* @__PURE__ */ new Set(),
389
- functionDeltaIndexes: /* @__PURE__ */ new Set(),
390
- functionDoneCallIds: /* @__PURE__ */ new Set()
388
+ functionCalls: []
391
389
  };
392
390
  }
393
391
  function nextId(state, prefix) {
@@ -411,20 +409,92 @@ function normalizeErrorEvent(event) {
411
409
  }
412
410
  };
413
411
  }
414
- function normalizeFunctionItem(item, state, forDone = false) {
415
- const callId = asString(item.call_id) ?? asString(item.id) ?? nextId(state, "call");
416
- const id = asString(item.id) ?? nextId(state, "fc");
417
- state.lastFunctionItemId = id;
412
+ function resolveFunctionCall(state, hint) {
413
+ if (hint.callId) {
414
+ const byCallId = state.functionCalls.find((entry2) => entry2.callId === hint.callId);
415
+ if (byCallId) return byCallId;
416
+ }
417
+ if (hint.itemId) {
418
+ const byItemId = state.functionCalls.find((entry2) => entry2.itemId === hint.itemId);
419
+ if (byItemId) return byItemId;
420
+ }
421
+ if (hint.outputIndex !== void 0) {
422
+ const open4 = [...state.functionCalls].reverse().find((entry2) => entry2.outputIndex === hint.outputIndex && !entry2.done && !(hint.callId && entry2.callId !== hint.callId));
423
+ if (open4) return open4;
424
+ }
425
+ if (hint.callId === void 0 && hint.itemId === void 0 && hint.outputIndex === void 0 && state.lastFunctionCall) {
426
+ return state.lastFunctionCall;
427
+ }
428
+ const entry = {
429
+ itemId: hint.itemId ?? nextId(state, "fc"),
430
+ callId: hint.callId ?? hint.itemId ?? nextId(state, "call"),
431
+ name: "",
432
+ args: "",
433
+ upstream: {},
434
+ outputIndex: hint.outputIndex ?? state.lastOutputIndex,
435
+ added: false,
436
+ deltaForwarded: false,
437
+ doneSeen: false,
438
+ done: false
439
+ };
440
+ state.functionCalls.push(entry);
441
+ return entry;
442
+ }
443
+ function absorbFunctionItem(entry, item, authoritative) {
444
+ entry.upstream = { ...entry.upstream, ...item };
445
+ const name = asString(item.name);
446
+ if (name) entry.name = name;
447
+ const callId = asString(item.call_id);
448
+ if (callId) entry.callId = callId;
449
+ if (authoritative && typeof item.arguments === "string") entry.upstreamArgs = item.arguments;
450
+ }
451
+ function resolveFunctionArgs(entry) {
452
+ if (entry.upstreamArgs) return entry.upstreamArgs;
453
+ if (entry.args) return entry.args;
454
+ return entry.upstreamArgs;
455
+ }
456
+ function functionItemPayload(entry, extra) {
418
457
  return {
419
- ...item,
458
+ ...entry.upstream,
420
459
  type: "function_call",
421
- id,
422
- call_id: callId,
423
- name: asString(item.name) ?? "",
424
- arguments: typeof item.arguments === "string" ? item.arguments : "",
425
- ...forDone ? { status: "completed" } : {}
460
+ id: entry.itemId,
461
+ call_id: entry.callId,
462
+ name: entry.name,
463
+ ...extra
464
+ };
465
+ }
466
+ function functionAddedEvent(entry) {
467
+ entry.added = true;
468
+ return {
469
+ type: "response.output_item.added",
470
+ output_index: entry.outputIndex,
471
+ item: functionItemPayload(entry, { arguments: "" })
472
+ };
473
+ }
474
+ function functionDoneEvent(entry, args) {
475
+ entry.done = true;
476
+ return {
477
+ type: "response.output_item.done",
478
+ output_index: entry.outputIndex,
479
+ item: functionItemPayload(entry, { arguments: args, status: "completed" })
426
480
  };
427
481
  }
482
+ function completeFunctionCall(entry, args) {
483
+ if (entry.done) return [];
484
+ const events = [];
485
+ if (!entry.added) events.push(functionAddedEvent(entry));
486
+ if (!entry.deltaForwarded && args.length > 0) {
487
+ entry.deltaForwarded = true;
488
+ events.push({
489
+ type: "response.function_call_arguments.delta",
490
+ item_id: entry.itemId,
491
+ output_index: entry.outputIndex,
492
+ delta: args
493
+ });
494
+ }
495
+ events.push(functionDoneEvent(entry, args));
496
+ return events;
497
+ }
428
498
  function messageText(item) {
429
499
  if (typeof item.text === "string") return item.text;
430
500
  if (!Array.isArray(item.content)) return "";
@@ -451,47 +521,43 @@ function synthesizeMessage(item, outputIndex, state) {
451
521
  ];
452
522
  }
453
523
  function synthesizeFunctionCall(item, outputIndex, state) {
454
- const normalized = normalizeFunctionItem(item, state);
455
- const callId = String(normalized.call_id);
456
- if (state.functionDoneCallIds.has(callId)) return [];
457
- const events = [];
458
- if (!state.functionAddedIndexes.has(outputIndex)) {
459
- events.push({
460
- type: "response.output_item.added",
461
- output_index: outputIndex,
462
- item: { ...normalized, arguments: "" }
463
- });
464
- state.functionAddedIndexes.add(outputIndex);
465
- }
466
- if (!state.functionDeltaIndexes.has(outputIndex) && typeof normalized.arguments === "string" && normalized.arguments.length > 0) {
467
- events.push({
468
- type: "response.function_call_arguments.delta",
469
- item_id: normalized.id,
470
- output_index: outputIndex,
471
- delta: normalized.arguments
472
- });
473
- state.functionDeltaIndexes.add(outputIndex);
474
- }
475
- events.push({
476
- type: "response.output_item.done",
477
- output_index: outputIndex,
478
- item: { ...normalized, status: "completed" }
524
+ const entry = resolveFunctionCall(state, {
525
+ itemId: asString(item.id),
526
+ callId: asString(item.call_id),
527
+ outputIndex
479
528
  });
480
- state.functionDoneCallIds.add(callId);
481
- state.lastOutputIndex = outputIndex;
482
- return events;
529
+ absorbFunctionItem(entry, item, true);
530
+ state.lastFunctionCall = entry;
531
+ state.lastOutputIndex = entry.outputIndex;
532
+ const args = resolveFunctionArgs(entry);
533
+ if (args === void 0) {
534
+ entry.doneSeen = true;
535
+ return [];
536
+ }
537
+ return completeFunctionCall(entry, args);
483
538
  }
484
539
  function recoverFromCompletedOutput(response, state) {
485
- if (!Array.isArray(response.output)) return [];
486
540
  const recovered = [];
487
- response.output.forEach((item, index) => {
488
- if (!isRecord(item) || typeof item.type !== "string") return;
489
- if (item.type === "message" && !state.textDeltaForwarded) {
490
- recovered.push(...synthesizeMessage(item, index, state));
491
- } else if (item.type === "function_call") {
492
- recovered.push(...synthesizeFunctionCall(item, index, state));
493
- }
494
- });
541
+ if (Array.isArray(response.output)) {
542
+ response.output.forEach((item, index) => {
543
+ if (!isRecord(item) || typeof item.type !== "string") return;
544
+ if (item.type === "message" && !state.textDeltaForwarded) {
545
+ recovered.push(...synthesizeMessage(item, index, state));
546
+ } else if (item.type === "function_call") {
547
+ recovered.push(...synthesizeFunctionCall(item, index, state));
548
+ }
549
+ });
550
+ }
551
+ for (const entry of state.functionCalls) {
552
+ if (entry.done || !entry.doneSeen) continue;
553
+ recovered.push(normalizeErrorEvent({
554
+ error: {
555
+ type: "invalid_response",
556
+ code: "incomplete_function_call",
557
+ message: `Provider ended the response without arguments for function call "${entry.callId}"${entry.name ? ` (${entry.name})` : ""}.`
558
+ }
559
+ }));
560
+ }
495
561
  return recovered;
496
562
  }
497
563
  function normalizeResponsesLiteEvent(event, state) {
@@ -507,19 +573,40 @@ function normalizeResponsesLiteEvent(event, state) {
507
573
  return [{ ...event, output_index: outputIndex, item: { ...event.item, id } }];
508
574
  }
509
575
  if (event.item.type === "function_call") {
510
- const item = normalizeFunctionItem(event.item, state);
511
- state.functionAddedIndexes.add(outputIndex);
512
- return [{ ...event, output_index: outputIndex, item }];
576
+ const entry = resolveFunctionCall(state, {
577
+ itemId: asString(event.item.id),
578
+ callId: asString(event.item.call_id),
579
+ outputIndex
580
+ });
581
+ absorbFunctionItem(entry, event.item, false);
582
+ entry.outputIndex = outputIndex;
583
+ entry.added = true;
584
+ state.lastFunctionCall = entry;
585
+ return [{ ...event, output_index: outputIndex, item: functionItemPayload(entry, { arguments: "" }) }];
513
586
  }
514
587
  return [{ ...event, output_index: outputIndex }];
515
588
  }
516
589
  if (event.type === "response.output_item.done" && isRecord(event.item)) {
517
590
  const outputIndex = typeof event.output_index === "number" ? event.output_index : state.lastOutputIndex;
518
591
  if (event.item.type === "function_call") {
519
- const item = normalizeFunctionItem(event.item, state, true);
520
- const callId = String(item.call_id);
521
- state.functionDoneCallIds.add(callId);
522
- return [{ ...event, output_index: outputIndex, item }];
592
+ const entry = resolveFunctionCall(state, {
593
+ itemId: asString(event.item.id),
594
+ callId: asString(event.item.call_id),
595
+ outputIndex
596
+ });
597
+ absorbFunctionItem(entry, event.item, true);
598
+ entry.outputIndex = outputIndex;
599
+ entry.doneSeen = true;
600
+ state.lastFunctionCall = entry;
601
+ state.lastOutputIndex = outputIndex;
602
+ const args = resolveFunctionArgs(entry);
603
+ if (args === void 0 || !entry.name) return [];
604
+ if (entry.done) return [];
605
+ const events = [];
606
+ if (!entry.added) events.push(functionAddedEvent(entry));
607
+ events.push({ ...event, output_index: outputIndex, item: functionItemPayload(entry, { arguments: args, status: "completed" }) });
608
+ entry.done = true;
609
+ return events;
523
610
  }
524
611
  if (event.item.type === "message") {
525
612
  const id = asString(event.item.id) ?? state.lastMessageItemId ?? nextId(state, "msg");
@@ -546,12 +633,16 @@ function normalizeResponsesLiteEvent(event, state) {
546
633
  return events;
547
634
  }
548
635
  if (event.type === "response.function_call_arguments.delta") {
549
- const itemId = asString(event.item_id) ?? state.lastFunctionItemId ?? nextId(state, "fc");
550
- const outputIndex = typeof event.output_index === "number" ? event.output_index : state.lastOutputIndex;
551
- state.lastFunctionItemId = itemId;
552
- state.lastOutputIndex = outputIndex;
553
- state.functionDeltaIndexes.add(outputIndex);
554
- return [{ ...event, item_id: itemId, output_index: outputIndex, delta: typeof event.delta === "string" ? event.delta : "" }];
636
+ const entry = resolveFunctionCall(state, {
637
+ itemId: asString(event.item_id),
638
+ outputIndex: typeof event.output_index === "number" ? event.output_index : void 0
639
+ });
640
+ const delta = typeof event.delta === "string" ? event.delta : "";
641
+ entry.args += delta;
642
+ entry.deltaForwarded = true;
643
+ state.lastFunctionCall = entry;
644
+ state.lastOutputIndex = entry.outputIndex;
645
+ return [{ ...event, item_id: entry.itemId, output_index: entry.outputIndex, delta }];
555
646
  }
556
647
  if (event.type === "response.completed" || event.type === "response.incomplete") {
557
648
  const response = isRecord(event.response) ? event.response : {};
@@ -1051,10 +1142,11 @@ async function createLanguageModel(spec) {
1051
1142
  return model;
1052
1143
  }
1053
1144
  var ANTHROPIC_EFFORT_LEVELS = ["low", "medium", "high"];
1054
- var OPENAI_EFFORT_LEVELS = ["low", "medium", "high", "xhigh"];
1145
+ var OPENAI_EFFORT_LEVELS = ["low", "medium", "high"];
1055
1146
  var GEMINI_EFFORT_LEVELS = ["low", "medium", "high"];
1056
1147
  var MISTRAL_EFFORT_LEVELS = ["high", "off"];
1057
- var XAI_EFFORT_LEVELS = ["none", "low", "medium", "high"];
1148
+ var XAI_CHAT_EFFORT_LEVELS = ["low", "high"];
1149
+ var XAI_RESPONSES_EFFORT_LEVELS = ["low", "medium", "high"];
1058
1150
  var OPENROUTER_EFFORT_LEVELS = ["none", "minimal", "low", "medium", "high", "xhigh"];
1059
1151
  var DEEPSEEK_EFFORT_LEVELS = ["high", "max", "off"];
1060
1152
  var GLM_52_EFFORT_LEVELS = ["high", "xhigh"];
@@ -1225,7 +1317,50 @@ function mapCodexEffortToAnthropic(effort) {
1225
1317
  return void 0;
1226
1318
  }
1227
1319
  }
1228
- function mapCodexEffortToOpenAI(effort) {
1320
+ var OPENAI_MODEL_REASONING = {
1321
+ "gpt-5-pro": { levels: ["high"], defaultLevel: "high" },
1322
+ "gpt-5.1": { levels: ["none", "low", "medium", "high"], defaultLevel: "none" },
1323
+ "gpt-5.1-codex-max": { levels: ["low", "medium", "high", "xhigh"], defaultLevel: "medium" },
1324
+ "gpt-5.2": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "none" },
1325
+ "gpt-5.2-codex": { levels: ["low", "medium", "high", "xhigh"], defaultLevel: "medium" },
1326
+ "gpt-5.2-pro": { levels: ["medium", "high", "xhigh"], defaultLevel: "medium" },
1327
+ "gpt-5.3-codex": { levels: ["low", "medium", "high", "xhigh"], defaultLevel: "medium" },
1328
+ "gpt-5.4": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "none" },
1329
+ "gpt-5.4-mini": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "none" },
1330
+ "gpt-5.4-nano": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "none" },
1331
+ "gpt-5.4-pro": { levels: ["medium", "high", "xhigh"], defaultLevel: "medium" },
1332
+ "gpt-5.5": { levels: ["none", "low", "medium", "high", "xhigh"], defaultLevel: "medium" },
1333
+ "gpt-5.5-pro": { levels: ["medium", "high", "xhigh"], defaultLevel: "high" },
1334
+ "gpt-5.6": { levels: ["none", "low", "medium", "high", "xhigh", "max"], defaultLevel: "medium" },
1335
+ "gpt-5.6-luna": { levels: ["none", "low", "medium", "high", "xhigh", "max"], defaultLevel: "medium" },
1336
+ "gpt-5.6-sol": { levels: ["none", "low", "medium", "high", "xhigh", "max"], defaultLevel: "medium" },
1337
+ "gpt-5.6-terra": { levels: ["none", "low", "medium", "high", "xhigh", "max"], defaultLevel: "medium" }
1338
+ };
1339
+ var OPENAI_NON_REASONING_MODELS = /* @__PURE__ */ new Set([
1340
+ "chat-latest",
1341
+ "gpt-5-chat-latest",
1342
+ "gpt-5.1-chat-latest",
1343
+ "gpt-5.2-chat-latest",
1344
+ "gpt-5.3-chat-latest"
1345
+ ]);
1346
+ var OPENAI_DATED_SNAPSHOT_SUFFIX = /-\d{4}-\d{2}-\d{2}$/;
1347
+ function canonicalOpenAiModelId(modelId, metadata) {
1348
+ return (metadata?.upstreamModelId ?? modelId ?? "").toLowerCase();
1349
+ }
1350
+ function openAiReasoningProfile(modelId, metadata) {
1351
+ const id = canonicalOpenAiModelId(modelId, metadata);
1352
+ if (!id) return void 0;
1353
+ return OPENAI_MODEL_REASONING[id] ?? OPENAI_MODEL_REASONING[id.replace(OPENAI_DATED_SNAPSHOT_SUFFIX, "")];
1354
+ }
1355
+ function openAiModelReasons(modelId, metadata) {
1356
+ const id = canonicalOpenAiModelId(modelId, metadata);
1357
+ if (OPENAI_NON_REASONING_MODELS.has(id.replace(OPENAI_DATED_SNAPSHOT_SUFFIX, ""))) return false;
1358
+ return !!openAiReasoningProfile(modelId, metadata) || modelPrefersResponsesApi(id) || !!metadata?.reasoning;
1359
+ }
1360
+ function mapCodexEffortToOpenAI(effort, allowed) {
1361
+ return allowed.includes(effort) ? effort : void 0;
1362
+ }
1363
+ function mapCodexEffortToOpenAICompatible(effort) {
1229
1364
  if (effort === "xhigh") return "high";
1230
1365
  const allowed = ["low", "medium", "high"];
1231
1366
  return allowed.includes(effort) ? effort : void 0;
@@ -1241,16 +1376,12 @@ function mapCodexEffortToGlm52(effort) {
1241
1376
  return void 0;
1242
1377
  }
1243
1378
  }
1244
- function mapCodexEffortToXai(effort) {
1379
+ function mapCodexEffortToXai(effort, supportsMedium) {
1245
1380
  switch (effort) {
1246
- case "none":
1247
- case "minimal":
1248
- return void 0;
1249
- // xAI SDK only accepts 'low'|'high'; omit param for 'none'
1250
1381
  case "low":
1251
- case "medium":
1252
1382
  return "low";
1253
- // 'medium' has no xAI equivalent — nearest valid value
1383
+ case "medium":
1384
+ return supportsMedium ? "medium" : void 0;
1254
1385
  case "high":
1255
1386
  case "xhigh":
1256
1387
  case "max":
@@ -1282,7 +1413,31 @@ function mapCodexEffortToGeminiBudget(effort) {
1282
1413
  if (!level) return void 0;
1283
1414
  return GEMINI_25_BUDGETS[level];
1284
1415
  }
1416
+ function withMappableLevels(caps, npm, modelId, metadata) {
1417
+ if (caps.mode !== "controllable") return caps;
1418
+ const seen = /* @__PURE__ */ new Set();
1419
+ const levels = caps.levels.filter((level) => {
1420
+ const mapped = effortProviderOptions(npm, level, modelId, metadata);
1421
+ if (mapped === void 0) return false;
1422
+ const wire = JSON.stringify(mapped);
1423
+ if (seen.has(wire)) return false;
1424
+ seen.add(wire);
1425
+ return true;
1426
+ });
1427
+ if (levels.length === caps.levels.length) return caps;
1428
+ if (levels.length === 0) {
1429
+ return { ...caps, levels: [], defaultLevel: "", mode: "internal-only" };
1430
+ }
1431
+ return {
1432
+ ...caps,
1433
+ levels,
1434
+ defaultLevel: levels.includes(caps.defaultLevel) ? caps.defaultLevel : levels[levels.length - 1]
1435
+ };
1436
+ }
1285
1437
  function getReasoningCapabilities(npm, modelId, metadata) {
1438
+ return withMappableLevels(resolveRawReasoningCapabilities(npm, modelId, metadata), npm, modelId, metadata);
1439
+ }
1440
+ function resolveRawReasoningCapabilities(npm, modelId, metadata) {
1286
1441
  const id = modelId.toLowerCase();
1287
1442
  if (isOpenRouterRoute(npm, metadata)) {
1288
1443
  return openRouterReasoningCapabilities(metadata);
@@ -1303,15 +1458,18 @@ function getReasoningCapabilities(npm, modelId, metadata) {
1303
1458
  return EMPTY_REASONING;
1304
1459
  }
1305
1460
  if (npm === "@ai-sdk/openai" || npm === "@ai-sdk/azure") {
1306
- const prefersResponses = modelPrefersResponsesApi(modelId);
1307
- if (prefersResponses || metadata?.reasoning) {
1461
+ const canonicalId = canonicalOpenAiModelId(modelId, metadata);
1462
+ const profile = openAiReasoningProfile(modelId, metadata);
1463
+ const prefersResponses = modelPrefersResponsesApi(canonicalId);
1464
+ if (openAiModelReasons(modelId, metadata) && shouldUseOpenAiResponsesEndpoint(canonicalId)) {
1465
+ const levels = profile?.levels ?? [...OPENAI_EFFORT_LEVELS];
1308
1466
  return {
1309
- levels: [...OPENAI_EFFORT_LEVELS],
1310
- defaultLevel: "medium",
1467
+ levels: [...levels],
1468
+ defaultLevel: profile?.defaultLevel ?? (levels.includes("medium") ? "medium" : levels[levels.length - 1]),
1311
1469
  supportsSummaries: true,
1470
+ source: profile || prefersResponses ? "provider-rule" : "model-metadata",
1471
+ confidence: profile || prefersResponses ? "documented" : "inferred",
1312
1472
  mode: "controllable",
1313
- source: prefersResponses ? "provider-rule" : "model-metadata",
1314
- confidence: prefersResponses ? "documented" : "inferred",
1315
1473
  wireFormat: { kind: "openai-reasoning-effort" }
1316
1474
  };
1317
1475
  }
@@ -1347,7 +1505,7 @@ function getReasoningCapabilities(npm, modelId, metadata) {
1347
1505
  }
1348
1506
  if (npm === "@ai-sdk/xai") {
1349
1507
  if (isXaiReasoningEffortModel(modelId)) {
1350
- const levels = modelPrefersResponsesApi(modelId) ? ["low", "medium", "high", "xhigh"] : [...XAI_EFFORT_LEVELS];
1508
+ const levels = modelPrefersResponsesApi(modelId) ? [...XAI_RESPONSES_EFFORT_LEVELS] : [...XAI_CHAT_EFFORT_LEVELS];
1351
1509
  return {
1352
1510
  levels,
1353
1511
  defaultLevel: xaiDefaultReasoningEffort(modelId),
@@ -1444,13 +1602,15 @@ function effortProviderOptions(npm, effort, modelId, metadata) {
1444
1602
  return mapped ? { openrouter: { reasoning: { effort: mapped, exclude: false } } } : void 0;
1445
1603
  }
1446
1604
  if (npm === "@ai-sdk/openai" || npm === "@ai-sdk/azure") {
1447
- if (!modelId || !modelPrefersResponsesApi(modelId)) return void 0;
1448
- const reasoningEffort = mapCodexEffortToOpenAI(effort);
1605
+ if (!modelId || !shouldUseOpenAiResponsesEndpoint(canonicalOpenAiModelId(modelId, metadata))) return void 0;
1606
+ if (!openAiModelReasons(modelId, metadata)) return void 0;
1607
+ const allowed = openAiReasoningProfile(modelId, metadata)?.levels ?? OPENAI_EFFORT_LEVELS;
1608
+ const reasoningEffort = mapCodexEffortToOpenAI(effort, allowed);
1449
1609
  return reasoningEffort ? { openai: { reasoningEffort } } : void 0;
1450
1610
  }
1451
1611
  if (npm === "@ai-sdk/xai") {
1452
1612
  if (!modelId || !isXaiReasoningEffortModel(modelId)) return void 0;
1453
- const reasoningEffort = mapCodexEffortToXai(effort);
1613
+ const reasoningEffort = mapCodexEffortToXai(effort, modelPrefersResponsesApi(modelId));
1454
1614
  return reasoningEffort ? { xai: { reasoningEffort } } : void 0;
1455
1615
  }
1456
1616
  if (npm === "@ai-sdk/anthropic" || npm === VERTEX_ANTHROPIC_NPM) {
@@ -1478,7 +1638,7 @@ function effortProviderOptions(npm, effort, modelId, metadata) {
1478
1638
  return deepSeekEffortProviderOptions(effort);
1479
1639
  }
1480
1640
  if (isKimiReasoningModel(modelId)) {
1481
- const reasoningEffort = mapCodexEffortToOpenAI(effort);
1641
+ const reasoningEffort = mapCodexEffortToOpenAICompatible(effort);
1482
1642
  if (reasoningEffort) {
1483
1643
  const key = metadata?.providerId ? toCamelCase(metadata.providerId) : "openaiCompatible";
1484
1644
  return { [key]: { reasoningEffort } };
@@ -1494,7 +1654,7 @@ function effortProviderOptions(npm, effort, modelId, metadata) {
1494
1654
  return void 0;
1495
1655
  }
1496
1656
  if (hasSupportedParameter(metadata, "reasoning_effort")) {
1497
- const reasoningEffort = mapCodexEffortToOpenAI(effort);
1657
+ const reasoningEffort = mapCodexEffortToOpenAICompatible(effort);
1498
1658
  return reasoningEffort ? { openai: { reasoningEffort }, openaiCompatible: { reasoningEffort } } : void 0;
1499
1659
  }
1500
1660
  if (hasSupportedParameter(metadata, "reasoning")) {
@@ -3586,8 +3746,11 @@ function buildChildEnv(baseUrl, model, apiKey, proxyPort, contextWindow2, enable
3586
3746
  env["ANTHROPIC_API_KEY"] = apiKey;
3587
3747
  const bareModel = stripOneMContextSuffix(model);
3588
3748
  env["ANTHROPIC_MODEL"] = claudeCodeClientModelId(model, contextWindow2);
3589
- env["CLAUDE_CODE_MAX_CONTEXT_TOKENS"] = String(resolveContextWindow(bareModel, contextWindow2));
3749
+ if (!enableGatewayDiscovery) {
3750
+ env["CLAUDE_CODE_MAX_CONTEXT_TOKENS"] = String(resolveContextWindow(bareModel, contextWindow2));
3751
+ }
3590
3752
  if (enableGatewayDiscovery) {
3753
+ delete env["CLAUDE_CODE_MAX_CONTEXT_TOKENS"];
3591
3754
  env["CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY"] = "1";
3592
3755
  }
3593
3756
  applyClaudeCodeThirdPartyCompat(env);
@@ -6929,8 +7092,8 @@ function anthropicError(res, status, message) {
6929
7092
  });
6930
7093
  }
6931
7094
  function aliasModelId(realId, providerId) {
6932
- if (realId.startsWith("claude-")) return realId;
6933
7095
  const sanitized = providerId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
7096
+ if (realId.startsWith("claude-") && !sanitized.startsWith("custom-")) return realId;
6934
7097
  return `anthropic-${sanitized}__${realId}`;
6935
7098
  }
6936
7099
  function buildProxySubagentModelRouting(routes, parentRoute) {
@@ -9244,6 +9407,80 @@ function goRegistryStub() {
9244
9407
  };
9245
9408
  }
9246
9409
 
9410
+ // src/registry/fetch-anthropic-models.ts
9411
+ async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
9412
+ const root = baseUrl.replace(/\/v1\/?$/, "").replace(/\/$/, "");
9413
+ const modelsUrl2 = `${root}/v1/models`;
9414
+ const controller = new AbortController();
9415
+ const timer = setTimeout(() => controller.abort(), 1e4);
9416
+ try {
9417
+ const response = await fetch(modelsUrl2, {
9418
+ method: "GET",
9419
+ headers: {
9420
+ "x-api-key": apiKey,
9421
+ "anthropic-version": "2023-06-01",
9422
+ Accept: "application/json",
9423
+ ...extraHeaders
9424
+ },
9425
+ redirect: "manual",
9426
+ signal: controller.signal
9427
+ });
9428
+ let logTrace;
9429
+ if (process.env.RELAY_AI_TRACE === "1") {
9430
+ logTrace = makeTraceLogger(getProviderDebugLogPath());
9431
+ }
9432
+ const rawBodyText = await response.text().catch(() => "");
9433
+ if (logTrace) {
9434
+ logTrace(`[fetchAnthropicModels] HTTP ${response.status} from ${modelsUrl2}`);
9435
+ logTrace(`[fetchAnthropicModels] Body: ${rawBodyText}`);
9436
+ }
9437
+ if (response.ok) {
9438
+ let json = {};
9439
+ try {
9440
+ if (rawBodyText.trim()) {
9441
+ json = JSON.parse(rawBodyText);
9442
+ }
9443
+ } catch {
9444
+ }
9445
+ const models = [];
9446
+ for (const row of json.data ?? []) {
9447
+ const id = row.id?.trim();
9448
+ if (!id) continue;
9449
+ models.push({
9450
+ id,
9451
+ name: row.name?.trim() || id,
9452
+ upstreamModelId: id,
9453
+ family: id.split("-")[0] ?? id,
9454
+ brand: deriveBrand(id),
9455
+ contextWindow: resolveContextWindow(id),
9456
+ modelFormat: "anthropic",
9457
+ npm: "@ai-sdk/anthropic",
9458
+ apiUrl: root
9459
+ });
9460
+ }
9461
+ if (models.length > 0) return { models, baseUrl: root };
9462
+ }
9463
+ if (response.status === 401 || response.status === 403) {
9464
+ return { models: [], baseUrl: root, error: "API key was rejected.", hint: "Check your Anthropic-compatible API key." };
9465
+ }
9466
+ return {
9467
+ models: [],
9468
+ baseUrl: root,
9469
+ error: `Could not list models (HTTP ${response.status}).`,
9470
+ hint: "Verify the base URL supports Anthropic-compatible /v1/models or try the OpenAI-compatible option instead."
9471
+ };
9472
+ } catch {
9473
+ return {
9474
+ models: [],
9475
+ baseUrl: root,
9476
+ error: "Could not reach the Anthropic-compatible server.",
9477
+ hint: "Check the base URL and that the server is running."
9478
+ };
9479
+ } finally {
9480
+ clearTimeout(timer);
9481
+ }
9482
+ }
9483
+
9247
9484
  // src/registry/fetch-template-models.ts
9248
9485
  var TEST_TIMEOUT_MS = 1e4;
9249
9486
  function modelFormatForNpm(npm) {
@@ -9604,77 +9841,28 @@ function npmForKind(kind) {
9604
9841
  function modelFormatForKind(kind) {
9605
9842
  return kind === "anthropic" ? "anthropic" : "openai";
9606
9843
  }
9607
- async function fetchAnthropicModels(baseUrl, apiKey, extraHeaders) {
9608
- const root = baseUrl.replace(/\/v1\/?$/, "").replace(/\/$/, "");
9609
- const modelsUrl2 = `${root}/v1/models`;
9610
- const controller = new AbortController();
9611
- const timer = setTimeout(() => controller.abort(), 1e4);
9612
- try {
9613
- const response = await fetch(modelsUrl2, {
9614
- method: "GET",
9615
- headers: {
9616
- "x-api-key": apiKey,
9617
- "anthropic-version": "2023-06-01",
9618
- Accept: "application/json",
9619
- ...extraHeaders
9620
- },
9621
- redirect: "manual",
9622
- signal: controller.signal
9623
- });
9624
- let logTrace;
9625
- if (process.env.RELAY_AI_TRACE === "1") {
9626
- logTrace = makeTraceLogger(getProviderDebugLogPath());
9627
- }
9628
- const rawBodyText = await response.text().catch(() => "");
9629
- if (logTrace) {
9630
- logTrace(`[fetchAnthropicModels] HTTP ${response.status} from ${modelsUrl2}`);
9631
- logTrace(`[fetchAnthropicModels] Body: ${rawBodyText}`);
9632
- }
9633
- if (response.ok) {
9634
- let json = {};
9635
- try {
9636
- if (rawBodyText.trim()) {
9637
- json = JSON.parse(rawBodyText);
9638
- }
9639
- } catch {
9640
- }
9641
- const models = [];
9642
- for (const row of json.data ?? []) {
9643
- const id = row.id?.trim();
9644
- if (!id) continue;
9645
- models.push({
9646
- id,
9647
- name: row.name?.trim() || id,
9648
- upstreamModelId: id,
9649
- family: id.split("-")[0] ?? id,
9650
- brand: deriveBrand(id),
9651
- contextWindow: resolveContextWindow(id),
9652
- modelFormat: "anthropic",
9653
- npm: "@ai-sdk/anthropic",
9654
- apiUrl: root
9655
- });
9656
- }
9657
- if (models.length > 0) return { models, baseUrl: root };
9658
- }
9659
- if (response.status === 401 || response.status === 403) {
9660
- return { models: [], baseUrl: root, error: "API key was rejected.", hint: "Check your Anthropic-compatible API key." };
9661
- }
9662
- return {
9663
- models: [],
9664
- baseUrl: root,
9665
- error: `Could not list models (HTTP ${response.status}).`,
9666
- hint: "Verify the base URL supports Anthropic-compatible /v1/models or try the OpenAI-compatible option instead."
9667
- };
9668
- } catch {
9669
- return {
9670
- models: [],
9671
- baseUrl: root,
9672
- error: "Could not reach the Anthropic-compatible server.",
9673
- hint: "Check the base URL and that the server is running."
9674
- };
9675
- } finally {
9676
- clearTimeout(timer);
9844
+ function customEndpointKind(provider) {
9845
+ if (provider.templateId === "custom-anthropic") return "anthropic";
9846
+ if (provider.templateId === "custom-openai") return "openai";
9847
+ return null;
9848
+ }
9849
+ function sameHeaders(a, b) {
9850
+ const norm = (h) => JSON.stringify(Object.entries(h ?? {}).sort(([x], [y]) => x.localeCompare(y)));
9851
+ return norm(a) === norm(b);
9852
+ }
9853
+ function compareableUrl(url) {
9854
+ return url.replace(/\/v1\/?$/, "").replace(/\/$/, "");
9855
+ }
9856
+ async function findDuplicateCustomProvider(registry, normalizedUrl, apiKey, headers) {
9857
+ const target = compareableUrl(normalizedUrl);
9858
+ for (const existing of registry.providers) {
9859
+ if (!customEndpointKind(existing)) continue;
9860
+ if (compareableUrl(existing.api.url ?? "") !== target) continue;
9861
+ if (!sameHeaders(existing.api.headers, headers)) continue;
9862
+ const storedKey = await readStoredProviderCredential(existing.authRef);
9863
+ if ((storedKey ?? "") === apiKey) return existing.id;
9677
9864
  }
9865
+ return null;
9678
9866
  }
9679
9867
  function uniqueProviderId(displayName, registry) {
9680
9868
  let base = customProviderId(displayName);
@@ -9689,6 +9877,25 @@ function uniqueProviderId(displayName, registry) {
9689
9877
  }
9690
9878
  return `${base}-${Date.now()}`;
9691
9879
  }
9880
+ async function fetchCustomEndpointModels(input) {
9881
+ if (input.kind === "anthropic") {
9882
+ return fetchAnthropicModels(input.normalizedBaseUrl, input.apiKey, input.headers);
9883
+ }
9884
+ return fetchTemplateModels(
9885
+ {
9886
+ id: input.providerId,
9887
+ name: input.displayName,
9888
+ authType: input.apiKey === "local" ? "none" : "api",
9889
+ npm: npmForKind(input.kind),
9890
+ defaultBaseUrl: input.normalizedBaseUrl,
9891
+ modelSource: "api-list",
9892
+ supported: true
9893
+ },
9894
+ input.apiKey,
9895
+ input.normalizedBaseUrl,
9896
+ input.headers
9897
+ );
9898
+ }
9692
9899
  async function addCustomEndpointProvider(input) {
9693
9900
  const urlCheck = await validateCustomEndpointUrl(input.baseUrl, {
9694
9901
  allowInsecureLocal: input.allowInsecureLocal
@@ -9697,29 +9904,34 @@ async function addCustomEndpointProvider(input) {
9697
9904
  return { added: false, error: urlCheck.error, hint: urlCheck.hint };
9698
9905
  }
9699
9906
  const registry = loadRegistry();
9700
- const providerId = uniqueProviderId(input.displayName.trim(), registry);
9701
- const npm = npmForKind(input.kind);
9702
9907
  const apiKey = input.apiKey.trim() || "local";
9703
9908
  const headers = input.headers && Object.keys(input.headers).length > 0 ? input.headers : void 0;
9704
- let fetched;
9705
- if (input.kind === "anthropic") {
9706
- fetched = await fetchAnthropicModels(urlCheck.normalizedUrl, apiKey, headers);
9707
- } else {
9708
- fetched = await fetchTemplateModels(
9709
- {
9710
- id: providerId,
9711
- name: input.displayName,
9712
- authType: apiKey === "local" ? "none" : "api",
9713
- npm,
9714
- defaultBaseUrl: urlCheck.normalizedUrl,
9715
- modelSource: "api-list",
9716
- supported: true
9717
- },
9718
- apiKey,
9909
+ if (!input.confirmDuplicate) {
9910
+ const duplicateOf = await findDuplicateCustomProvider(
9911
+ registry,
9719
9912
  urlCheck.normalizedUrl,
9913
+ apiKey,
9720
9914
  headers
9721
9915
  );
9916
+ if (duplicateOf) {
9917
+ return {
9918
+ added: false,
9919
+ duplicateOf,
9920
+ error: `A backend with the same URL, key and headers already exists (${duplicateOf}).`,
9921
+ hint: "Add it anyway to keep both, or cancel."
9922
+ };
9923
+ }
9722
9924
  }
9925
+ const providerId = uniqueProviderId(input.displayName.trim(), registry);
9926
+ const npm = npmForKind(input.kind);
9927
+ const fetched = await fetchCustomEndpointModels({
9928
+ providerId,
9929
+ displayName: input.displayName,
9930
+ kind: input.kind,
9931
+ normalizedBaseUrl: urlCheck.normalizedUrl,
9932
+ apiKey,
9933
+ headers
9934
+ });
9723
9935
  if (fetched.error || fetched.models.length === 0) {
9724
9936
  return { added: false, error: fetched.error ?? "No models returned.", hint: fetched.hint };
9725
9937
  }
@@ -9756,6 +9968,115 @@ async function addCustomEndpointProvider(input) {
9756
9968
  saveRegistry(registry);
9757
9969
  return { added: true, provider: entry, modelCount: fetched.models.length };
9758
9970
  }
9971
+ async function updateCustomEndpointProvider(input) {
9972
+ const registry = loadRegistry();
9973
+ const provider = registry.providers.find((pr) => pr.id === input.providerId);
9974
+ if (!provider) {
9975
+ return { updated: false, error: `Provider not found: ${input.providerId}` };
9976
+ }
9977
+ const kind = customEndpointKind(provider);
9978
+ if (!kind) {
9979
+ return {
9980
+ updated: false,
9981
+ error: "Edit is only available for custom backends.",
9982
+ hint: "Template providers can only change their API key."
9983
+ };
9984
+ }
9985
+ const nextName = input.displayName?.trim();
9986
+ let nextBaseUrl = provider.api.url ?? "";
9987
+ let urlChanged = false;
9988
+ const requestedUrl = input.baseUrl?.trim();
9989
+ if (requestedUrl) {
9990
+ const urlCheck = await validateCustomEndpointUrl(requestedUrl, {
9991
+ allowInsecureLocal: input.allowInsecureLocal
9992
+ });
9993
+ if (!urlCheck.ok || !urlCheck.normalizedUrl) {
9994
+ return { updated: false, error: urlCheck.error, hint: urlCheck.hint };
9995
+ }
9996
+ urlChanged = urlCheck.normalizedUrl !== nextBaseUrl;
9997
+ nextBaseUrl = urlCheck.normalizedUrl;
9998
+ }
9999
+ const newKey = input.apiKey?.trim();
10000
+ const headersChanged = input.headers !== void 0 && !sameHeaders(input.headers, provider.api.headers);
10001
+ const nextHeaders = input.headers !== void 0 ? Object.keys(input.headers).length > 0 ? input.headers : void 0 : provider.api.headers;
10002
+ const nameChanged = Boolean(nextName) && nextName !== provider.name;
10003
+ const needsTest = urlChanged || Boolean(newKey) || headersChanged;
10004
+ if (!needsTest) {
10005
+ if (!nameChanged) return { updated: false, error: "Nothing to change." };
10006
+ provider.name = nextName;
10007
+ saveRegistry(registry);
10008
+ return {
10009
+ updated: true,
10010
+ provider,
10011
+ modelCount: provider.modelsCache?.models.length ?? 0
10012
+ };
10013
+ }
10014
+ const apiKey = newKey || await readStoredProviderCredential(provider.authRef) || "";
10015
+ if (!apiKey) {
10016
+ return {
10017
+ updated: false,
10018
+ error: "No stored API key was found for this backend.",
10019
+ hint: "Enter an API key to continue."
10020
+ };
10021
+ }
10022
+ const fetched = await fetchCustomEndpointModels({
10023
+ providerId: provider.id,
10024
+ displayName: nextName || provider.name,
10025
+ kind,
10026
+ normalizedBaseUrl: nextBaseUrl,
10027
+ apiKey,
10028
+ headers: nextHeaders
10029
+ });
10030
+ const testFailed = Boolean(fetched.error) || fetched.models.length === 0;
10031
+ if (testFailed && !input.saveAnyway) {
10032
+ return {
10033
+ updated: false,
10034
+ error: fetched.error ?? "No models returned.",
10035
+ hint: fetched.hint,
10036
+ canSaveAnyway: true
10037
+ };
10038
+ }
10039
+ if (newKey) {
10040
+ const saved = await saveProviderCredential(provider.authRef, newKey);
10041
+ if (!saved) {
10042
+ return {
10043
+ updated: false,
10044
+ error: "Could not save API key to credential store.",
10045
+ hint: "Grant Keychain access, or ensure RELAY_AI_HOME is writable (file fallback)."
10046
+ };
10047
+ }
10048
+ }
10049
+ const now = (/* @__PURE__ */ new Date()).toISOString();
10050
+ if (nameChanged) provider.name = nextName;
10051
+ const storedBaseUrl = kind === "anthropic" ? nextBaseUrl.replace(/\/v1\/?$/, "").replace(/\/$/, "") : nextBaseUrl;
10052
+ provider.api.url = testFailed ? storedBaseUrl : fetched.baseUrl || storedBaseUrl;
10053
+ if (nextHeaders) provider.api.headers = nextHeaders;
10054
+ else delete provider.api.headers;
10055
+ if (!testFailed) {
10056
+ provider.modelsCache = {
10057
+ fetchedAt: now,
10058
+ models: fetched.models.map((m) => ({
10059
+ ...m,
10060
+ modelFormat: modelFormatForKind(kind),
10061
+ npm: npmForKind(kind),
10062
+ apiUrl: fetched.baseUrl || storedBaseUrl
10063
+ }))
10064
+ };
10065
+ provider.refreshedAt = now;
10066
+ } else if (provider.modelsCache) {
10067
+ provider.modelsCache = {
10068
+ ...provider.modelsCache,
10069
+ models: provider.modelsCache.models.map((m) => ({ ...m, apiUrl: storedBaseUrl }))
10070
+ };
10071
+ }
10072
+ saveRegistry(registry);
10073
+ return {
10074
+ updated: true,
10075
+ provider,
10076
+ modelCount: provider.modelsCache?.models.length ?? 0,
10077
+ ...testFailed ? { modelsStale: true } : {}
10078
+ };
10079
+ }
9759
10080
 
9760
10081
  // src/registry/fetch-cline-pass-models.ts
9761
10082
  var REQUEST_TIMEOUT_MS = 1e4;
@@ -10283,7 +10604,7 @@ async function refreshApiListProvider(provider, apiKey) {
10283
10604
  const templateDefault = catalogTemplate?.defaultBaseUrl?.trim();
10284
10605
  if (configuredUrl && configuredUrl !== templateDefault) {
10285
10606
  const urlCheck = await validateCustomEndpointUrl(baseUrl, {
10286
- allowInsecureLocal: catalogTemplate?.apiKeyOptional === true
10607
+ allowInsecureLocal: catalogTemplate?.apiKeyOptional === true || customEndpointKind(provider) !== null
10287
10608
  });
10288
10609
  if (!urlCheck.ok || !urlCheck.normalizedUrl) {
10289
10610
  return { models: [], error: `${urlCheck.error ?? "Invalid API base URL."} ${urlCheck.hint ?? ""}`.trim() };
@@ -10291,8 +10612,9 @@ async function refreshApiListProvider(provider, apiKey) {
10291
10612
  safeBaseUrl = urlCheck.normalizedUrl;
10292
10613
  }
10293
10614
  const template = catalogTemplate ?? syntheticTemplate(provider, safeBaseUrl);
10615
+ const extraHeaders = provider.api.headers && Object.keys(provider.api.headers).length > 0 ? provider.api.headers : void 0;
10294
10616
  if (npm === "@ai-sdk/anthropic") {
10295
- const fetched2 = await fetchAnthropicModels(safeBaseUrl, apiKey);
10617
+ const fetched2 = await fetchAnthropicModels(safeBaseUrl, apiKey, extraHeaders);
10296
10618
  if (fetched2.error || fetched2.models.length === 0) {
10297
10619
  return { models: [], error: fetched2.error ?? "No models returned.", baseUrl: fetched2.baseUrl };
10298
10620
  }
@@ -10301,7 +10623,7 @@ async function refreshApiListProvider(provider, apiKey) {
10301
10623
  baseUrl: fetched2.baseUrl
10302
10624
  };
10303
10625
  }
10304
- const fetched = await fetchTemplateModels(template, apiKey, safeBaseUrl);
10626
+ const fetched = await fetchTemplateModels(template, apiKey, safeBaseUrl, extraHeaders);
10305
10627
  if (fetched.error || fetched.models.length === 0) {
10306
10628
  return { models: [], error: fetched.error ?? "No models returned." };
10307
10629
  }
@@ -12234,7 +12556,7 @@ function removeFavorite(list, fav) {
12234
12556
  // src/favorite-provider-display.ts
12235
12557
  var OAUTH_FAVORITE_NAMES = {
12236
12558
  "claude-code": "Claude Code OAuth (Anthropic subscription)",
12237
- antigravity: "Antigravity OAuth (Google Cloud Code Assist)",
12559
+ antigravity: "Cloud Code Assist OAuth (Google)",
12238
12560
  "openai-oauth": "OpenAI OAuth (ChatGPT)",
12239
12561
  "xai-oauth": "xAI OAuth (SuperGrok)"
12240
12562
  };
@@ -12437,7 +12759,7 @@ var PROVIDER_DISPLAY = {
12437
12759
  "openai-oauth": OPENAI_DISPLAY,
12438
12760
  "github-copilot": "GitHub Copilot",
12439
12761
  "claude-code": "Claude Code (Anthropic subscription)",
12440
- antigravity: "Antigravity (Google Cloud Code Assist)",
12762
+ antigravity: "Cloud Code Assist OAuth (Google)",
12441
12763
  "cline-pass": "ClinePass"
12442
12764
  };
12443
12765
  function openBrowser(url) {
@@ -12540,7 +12862,7 @@ async function runNativeBrowserOAuth(providerId) {
12540
12862
  p5.log.info(`Opening: ${pc6.cyan(url)}`);
12541
12863
  spinner3.start("Waiting for authorization\u2026");
12542
12864
  });
12543
- spinner3.stop(pc6.green("Signed in to Antigravity"));
12865
+ spinner3.stop(pc6.green("Signed in to Cloud Code Assist"));
12544
12866
  const providerData = {};
12545
12867
  if (projectId) providerData.projectId = projectId;
12546
12868
  if (tierId) providerData.tier = tierId;
@@ -13414,14 +13736,13 @@ export {
13414
13736
  makeTraceLogger,
13415
13737
  writeSecureLogLine,
13416
13738
  printTraceLog,
13417
- fetchTemplateModels,
13418
- validateCustomEndpointUrl,
13419
13739
  fetchAnthropicModels,
13420
- addCustomEndpointProvider,
13740
+ fetchTemplateModels,
13421
13741
  resolveProviderTemplate,
13422
13742
  effectiveProviderBaseUrl,
13423
13743
  syntheticTemplate,
13424
13744
  resolveModelSource,
13745
+ validateCustomEndpointUrl,
13425
13746
  readBody,
13426
13747
  extractApiKey,
13427
13748
  sendJson,
@@ -13476,6 +13797,9 @@ export {
13476
13797
  routableModelsForTarget,
13477
13798
  providersForTarget,
13478
13799
  providersForCodexSubagents,
13800
+ customEndpointKind,
13801
+ addCustomEndpointProvider,
13802
+ updateCustomEndpointProvider,
13479
13803
  refreshProviderModels,
13480
13804
  refreshAllProviderModels,
13481
13805
  removeProviderFromRegistry,
@@ -13520,4 +13844,4 @@ export {
13520
13844
  supportsClaudeTransparentMode,
13521
13845
  buildHttpProxyRoutes
13522
13846
  };
13523
- //# sourceMappingURL=chunk-GQCFLSEM.js.map
13847
+ //# sourceMappingURL=chunk-KDIY732Q.js.map