@omnicross/core 0.4.4 → 0.4.6

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 (109) hide show
  1. package/dist/{CompletionService-BniP7j88.d.cts → CompletionService-BJu4AWVc.d.cts} +1 -1
  2. package/dist/{CompletionService-BqrKPwfA.d.ts → CompletionService-C4W2gBi_.d.ts} +1 -1
  3. package/dist/{ProviderProxy-Ckr6oTrE.d.ts → ProviderProxy-DxsNi3cO.d.ts} +1 -1
  4. package/dist/{ProviderProxy-Bz_nkOwq.d.cts → ProviderProxy-OuQPYW2I.d.cts} +1 -1
  5. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +4 -4
  6. package/dist/auth/GeminiCodeAssistProjectResolver.js +3 -3
  7. package/dist/{chunk-NKVZ6Z3Y.cjs → chunk-4HMAS74X.cjs} +8 -3
  8. package/dist/{chunk-MPFOC7AT.js → chunk-5XG6FR5F.js} +2 -2
  9. package/dist/{chunk-VFFCV4J6.cjs → chunk-CXRJMNJJ.cjs} +12 -6
  10. package/dist/{chunk-E5RCCGE7.cjs → chunk-DF5JXK36.cjs} +2 -2
  11. package/dist/{chunk-NCZDUBC2.js → chunk-DRUT7H5H.js} +1 -1
  12. package/dist/{chunk-RX3BCX53.cjs → chunk-DV7656NN.cjs} +1208 -527
  13. package/dist/{chunk-CGEZQC2G.cjs → chunk-F3TT5XPV.cjs} +2 -2
  14. package/dist/{chunk-EE7VP4O3.js → chunk-HQ7C3VB6.js} +1 -1
  15. package/dist/{chunk-ZJJBITUN.js → chunk-IAWLTZUP.js} +2 -2
  16. package/dist/{chunk-57Y2EA2G.cjs → chunk-JGRNVH7C.cjs} +1 -1
  17. package/dist/{chunk-NTF7TLT7.js → chunk-LBIXTHP7.js} +11 -5
  18. package/dist/{chunk-GWC7HCZI.cjs → chunk-LWBXQXZ3.cjs} +1 -1
  19. package/dist/{chunk-7PIUVCKK.js → chunk-MZCJL3QU.js} +1 -1
  20. package/dist/{chunk-K6J2RIEX.js → chunk-NDJSLJIA.js} +1 -1
  21. package/dist/{chunk-UOOLU64V.cjs → chunk-QLVRJ5FS.cjs} +4 -4
  22. package/dist/{chunk-F6P4DIBL.js → chunk-TLYFFUW4.js} +1 -1
  23. package/dist/{chunk-YV4JSBPA.js → chunk-TU4BB776.js} +789 -108
  24. package/dist/{chunk-GL6XM527.js → chunk-TX7ZM4OI.js} +8 -3
  25. package/dist/{chunk-ZMERU7D3.js → chunk-VXHN5IDL.js} +1 -1
  26. package/dist/{chunk-NDIBVTBZ.cjs → chunk-XYFCNDJK.cjs} +2 -2
  27. package/dist/{chunk-X6GHTNDL.cjs → chunk-ZG57GAVU.cjs} +7 -7
  28. package/dist/{chunk-QWNUOR7F.cjs → chunk-ZTN2PVKO.cjs} +1 -1
  29. package/dist/completion/CompletionService.cjs +12 -12
  30. package/dist/completion/CompletionService.d.cts +2 -2
  31. package/dist/completion/CompletionService.d.ts +2 -2
  32. package/dist/completion/CompletionService.js +11 -11
  33. package/dist/completion.cjs +12 -12
  34. package/dist/completion.d.cts +2 -2
  35. package/dist/completion.d.ts +2 -2
  36. package/dist/completion.js +11 -11
  37. package/dist/image-generation.d.cts +4 -4
  38. package/dist/image-generation.d.ts +4 -4
  39. package/dist/index.cjs +14 -12
  40. package/dist/index.d.cts +7 -7
  41. package/dist/index.d.ts +7 -7
  42. package/dist/index.js +13 -11
  43. package/dist/openai-operation.d.cts +1 -1
  44. package/dist/openai-operation.d.ts +1 -1
  45. package/dist/outbound-api/routeResolver.cjs +2 -2
  46. package/dist/outbound-api/routeResolver.d.cts +5 -5
  47. package/dist/outbound-api/routeResolver.d.ts +5 -5
  48. package/dist/outbound-api/routeResolver.js +1 -1
  49. package/dist/outbound-api/subscriptionRegistryPort.d.cts +9 -1
  50. package/dist/outbound-api/subscriptionRegistryPort.d.ts +9 -1
  51. package/dist/outbound-api/types.d.cts +5 -5
  52. package/dist/outbound-api/types.d.ts +5 -5
  53. package/dist/outbound-api.cjs +14 -12
  54. package/dist/outbound-api.d.cts +49 -8
  55. package/dist/outbound-api.d.ts +49 -8
  56. package/dist/outbound-api.js +13 -11
  57. package/dist/pipeline/AccountAllowanceScheduling.d.cts +4 -4
  58. package/dist/pipeline/AccountAllowanceScheduling.d.ts +4 -4
  59. package/dist/pipeline/AccountRouteActivity.cjs +2 -2
  60. package/dist/pipeline/AccountRouteActivity.d.cts +33 -8
  61. package/dist/pipeline/AccountRouteActivity.d.ts +33 -8
  62. package/dist/pipeline/AccountRouteActivity.js +1 -1
  63. package/dist/pipeline/SubscriptionAuthSource.d.cts +11 -0
  64. package/dist/pipeline/SubscriptionAuthSource.d.ts +11 -0
  65. package/dist/pipeline/upstreamFetch.cjs +3 -3
  66. package/dist/pipeline/upstreamFetch.d.cts +15 -1
  67. package/dist/pipeline/upstreamFetch.d.ts +15 -1
  68. package/dist/pipeline/upstreamFetch.js +2 -2
  69. package/dist/ports.d.cts +4 -4
  70. package/dist/ports.d.ts +4 -4
  71. package/dist/provider-proxy/ProviderProxy.cjs +12 -12
  72. package/dist/provider-proxy/ProviderProxy.d.cts +2 -2
  73. package/dist/provider-proxy/ProviderProxy.d.ts +2 -2
  74. package/dist/provider-proxy/ProviderProxy.js +11 -11
  75. package/dist/provider-proxy/identity/codexCliHeaders.d.cts +6 -4
  76. package/dist/provider-proxy/identity/codexCliHeaders.d.ts +6 -4
  77. package/dist/provider-proxy/identity/openCodeGoHeaders.cjs +2 -2
  78. package/dist/provider-proxy/identity/openCodeGoHeaders.js +1 -1
  79. package/dist/provider-proxy/ingress/providerProxyShared.cjs +20 -12
  80. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +54 -2
  81. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +54 -2
  82. package/dist/provider-proxy/ingress/providerProxyShared.js +19 -11
  83. package/dist/provider-proxy/types.d.cts +1 -1
  84. package/dist/provider-proxy/types.d.ts +1 -1
  85. package/dist/provider-proxy.cjs +12 -12
  86. package/dist/provider-proxy.d.cts +6 -6
  87. package/dist/provider-proxy.d.ts +6 -6
  88. package/dist/provider-proxy.js +11 -11
  89. package/dist/{routeResolver-C2z0pp-q.d.cts → routeResolver-BQYMEXer.d.cts} +2 -2
  90. package/dist/{routeResolver-BLTleO4H.d.ts → routeResolver-DTfyzaFb.d.ts} +2 -2
  91. package/dist/transformer/transformers/AntigravityTransformer.cjs +6 -6
  92. package/dist/transformer/transformers/AntigravityTransformer.js +5 -5
  93. package/dist/transformer/transformers/OpenAITransformer.cjs +2 -2
  94. package/dist/transformer/transformers/OpenAITransformer.js +1 -1
  95. package/dist/transformer/transformers/antigravityFailover.cjs +7 -7
  96. package/dist/transformer/transformers/antigravityFailover.js +6 -6
  97. package/dist/transformer/transformers/antigravityIdentity.cjs +4 -4
  98. package/dist/transformer/transformers/antigravityIdentity.js +3 -3
  99. package/dist/transformer/transformers.cjs +9 -9
  100. package/dist/transformer/transformers.js +8 -8
  101. package/dist/transformer.cjs +9 -9
  102. package/dist/transformer.js +8 -8
  103. package/dist/{types-C6m-Db26.d.cts → types-BpSkj2JA.d.cts} +66 -5
  104. package/dist/{types-BVwJQ9ZS.d.cts → types-BsZ1s_a_.d.cts} +1 -1
  105. package/dist/{types-By9Tg6-I.d.ts → types-Di8VH1c9.d.ts} +12 -0
  106. package/dist/{types-BSIJdsyl.d.cts → types-Dpr5IAaP.d.cts} +12 -0
  107. package/dist/{types-Da735j2d.d.ts → types-EReErShW.d.ts} +66 -5
  108. package/dist/{types-VAHRysdP.d.ts → types-USaixzVP.d.ts} +1 -1
  109. package/package.json +2 -2
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  fetchWithAntigravityFailover,
13
13
  maybeAntigravityFailoverUrl
14
- } from "./chunk-K6J2RIEX.js";
14
+ } from "./chunk-NDJSLJIA.js";
15
15
  import {
16
16
  GeminiTransformer
17
17
  } from "./chunk-OXBGNEYR.js";
@@ -53,7 +53,7 @@ import {
53
53
  } from "./chunk-XX6NQJMA.js";
54
54
  import {
55
55
  extractOpenCodeSessionHeader
56
- } from "./chunk-NCZDUBC2.js";
56
+ } from "./chunk-DRUT7H5H.js";
57
57
  import {
58
58
  NEVER_FORWARD_HEADERS,
59
59
  fillMissingHeaders,
@@ -118,7 +118,7 @@ import {
118
118
  parseModelRef,
119
119
  resolvePrefixTarget,
120
120
  resolveRoute
121
- } from "./chunk-ZMERU7D3.js";
121
+ } from "./chunk-VXHN5IDL.js";
122
122
  import {
123
123
  getSubscriptionRegistryForOutbound
124
124
  } from "./chunk-AQ5TNQB7.js";
@@ -147,19 +147,20 @@ import {
147
147
  } from "./chunk-5NJLQ6T6.js";
148
148
  import {
149
149
  getAntigravityProjectResolver
150
- } from "./chunk-EE7VP4O3.js";
150
+ } from "./chunk-HQ7C3VB6.js";
151
151
  import {
152
+ extractReasoningIntent,
152
153
  resolveReasoningPlan
153
154
  } from "./chunk-ZLNHPUFA.js";
154
155
  import {
155
156
  fetchUpstream
156
- } from "./chunk-NTF7TLT7.js";
157
+ } from "./chunk-LBIXTHP7.js";
157
158
  import {
158
159
  getSharedAccountAllowanceStore
159
160
  } from "./chunk-FYPKKMB3.js";
160
161
  import {
161
162
  getSharedAccountRouteActivity
162
- } from "./chunk-GL6XM527.js";
163
+ } from "./chunk-TX7ZM4OI.js";
163
164
  import {
164
165
  DEFAULT_IMAGE_API_LIMITS,
165
166
  ImageGenerationError,
@@ -896,6 +897,124 @@ async function aggregateAnthropicSseToJsonBody(response, rewriteModel, signal) {
896
897
  if (rewriteModel) message["model"] = rewriteModel;
897
898
  return JSON.stringify(message);
898
899
  }
900
+ async function aggregateResponsesSseToJsonBody(response, rewriteModel, signal) {
901
+ const text = await readResponseTextWithSignal(response, signal);
902
+ let terminal = null;
903
+ for (const raw of text.split("\n")) {
904
+ const line = raw.trim();
905
+ if (!line.startsWith("data:")) continue;
906
+ const payload = line.slice(5).trim();
907
+ if (!payload || payload === "[DONE]") continue;
908
+ try {
909
+ const ev = JSON.parse(payload);
910
+ if (ev["type"] === "response.completed" || ev["type"] === "response.failed" || ev["type"] === "response.incomplete") {
911
+ const resp = ev["response"];
912
+ if (resp && typeof resp === "object") terminal = resp;
913
+ }
914
+ } catch {
915
+ }
916
+ }
917
+ if (!terminal) return null;
918
+ if (rewriteModel && typeof terminal["model"] === "string") terminal["model"] = rewriteModel;
919
+ return JSON.stringify(terminal);
920
+ }
921
+ async function aggregateOpenAIChatChunksToJsonBody(response, rewriteModel, signal) {
922
+ const text = await readResponseTextWithSignal(response, signal);
923
+ let id;
924
+ let created;
925
+ let model;
926
+ let content = "";
927
+ let reasoning = "";
928
+ let finishReason = null;
929
+ let usage;
930
+ const toolCalls = /* @__PURE__ */ new Map();
931
+ let sawChoice = false;
932
+ for (const raw of text.split("\n")) {
933
+ const line = raw.trim();
934
+ if (!line.startsWith("data:")) continue;
935
+ const payload = line.slice(5).trim();
936
+ if (!payload || payload === "[DONE]") continue;
937
+ let ev;
938
+ try {
939
+ ev = JSON.parse(payload);
940
+ } catch {
941
+ continue;
942
+ }
943
+ if (typeof ev["id"] === "string" && id === void 0) id = ev["id"];
944
+ if (typeof ev["created"] === "number" && created === void 0) created = ev["created"];
945
+ if (typeof ev["model"] === "string" && model === void 0) model = ev["model"];
946
+ if (ev["usage"] && typeof ev["usage"] === "object") {
947
+ usage = ev["usage"];
948
+ }
949
+ const choices = ev["choices"];
950
+ if (!Array.isArray(choices) || choices.length === 0) continue;
951
+ const choice = choices[0];
952
+ if (!choice || typeof choice !== "object") continue;
953
+ sawChoice = true;
954
+ if (typeof choice["finish_reason"] === "string") finishReason = choice["finish_reason"];
955
+ const delta = choice["delta"];
956
+ if (!delta || typeof delta !== "object") continue;
957
+ const d = delta;
958
+ if (typeof d["content"] === "string") content += d["content"];
959
+ if (typeof d["reasoning_content"] === "string") reasoning += d["reasoning_content"];
960
+ const thinking = d["thinking"];
961
+ if (thinking && typeof thinking === "object") {
962
+ const tc = thinking["content"];
963
+ if (typeof tc === "string") reasoning += tc;
964
+ } else if (typeof thinking === "string") {
965
+ reasoning += thinking;
966
+ }
967
+ const tcs = d["tool_calls"];
968
+ if (Array.isArray(tcs)) {
969
+ for (const tcRaw of tcs) {
970
+ if (!tcRaw || typeof tcRaw !== "object") continue;
971
+ const tc = tcRaw;
972
+ const idx = typeof tc["index"] === "number" ? tc["index"] : 0;
973
+ const fn = tc["function"] ?? {};
974
+ const slot = toolCalls.get(idx) ?? { id: "", name: "", arguments: "" };
975
+ if (typeof tc["id"] === "string" && tc["id"]) slot.id = tc["id"];
976
+ if (typeof fn["name"] === "string" && fn["name"]) slot.name = fn["name"];
977
+ if (typeof fn["arguments"] === "string") slot.arguments += fn["arguments"];
978
+ toolCalls.set(idx, slot);
979
+ }
980
+ }
981
+ }
982
+ if (!sawChoice && !usage) return null;
983
+ const message = { role: "assistant", content };
984
+ if (reasoning) message["reasoning_content"] = reasoning;
985
+ if (toolCalls.size > 0) {
986
+ message["tool_calls"] = [...toolCalls.entries()].sort((a, b) => a[0] - b[0]).map(([index, tc]) => ({
987
+ index,
988
+ id: tc.id,
989
+ type: "function",
990
+ function: { name: tc.name, arguments: tc.arguments || "{}" }
991
+ }));
992
+ }
993
+ const completion = {
994
+ id: id ?? "chatcmpl-aggregated",
995
+ object: "chat.completion",
996
+ created: created ?? Math.floor(Date.now() / 1e3),
997
+ model: rewriteModel ?? model ?? "",
998
+ choices: [{ index: 0, message, finish_reason: finishReason ?? "stop" }]
999
+ };
1000
+ if (usage) completion["usage"] = usage;
1001
+ return JSON.stringify(completion);
1002
+ }
1003
+ async function sniffIsSseResponse(response) {
1004
+ const contentType = response.headers.get("Content-Type") ?? "";
1005
+ if (contentType.includes("text/event-stream")) return true;
1006
+ if (!response.body) return false;
1007
+ const peek = response.clone();
1008
+ try {
1009
+ const reader = peek.body.getReader();
1010
+ const { value } = await reader.read();
1011
+ reader.releaseLock();
1012
+ const head = value ? new TextDecoder().decode(value).trimStart() : "";
1013
+ return head.startsWith("event:") || head.startsWith("data:");
1014
+ } catch {
1015
+ return false;
1016
+ }
1017
+ }
899
1018
  function writeError(res, status, message) {
900
1019
  if (isAnthropicProtocolResponse(res)) {
901
1020
  writeAnthropicError(res, status, message);
@@ -953,6 +1072,21 @@ async function resolvePoolBoundKey(deps, providerId, provider, sessionId, prefer
953
1072
  }
954
1073
  return resolveApiKey(resolveProviderEndpoint(provider).apiKey);
955
1074
  }
1075
+ function buildByoRouteActivityMeta(deps, providerId, sessionId) {
1076
+ return {
1077
+ providerId,
1078
+ resolveKeyId: () => {
1079
+ if (!sessionId) return void 0;
1080
+ const pool = deps.apiKeyPool;
1081
+ if (!pool || typeof pool.getKeyIdForSession !== "function") return void 0;
1082
+ try {
1083
+ return pool.getKeyIdForSession(providerId, sessionId) ?? void 0;
1084
+ } catch {
1085
+ return void 0;
1086
+ }
1087
+ }
1088
+ };
1089
+ }
956
1090
 
957
1091
  // src/provider-proxy/providerProxyRouteMap.ts
958
1092
  import { randomBytes } from "crypto";
@@ -1666,6 +1800,31 @@ function collectText(value) {
1666
1800
  function proxyProviderId(plan) {
1667
1801
  return plan.isSubscription ? plan.transformerProvider.name : "byo";
1668
1802
  }
1803
+ function buildAnthropicRouteActivity(plan, proxyAccountId, model, onRecorded) {
1804
+ if (!plan.isSubscription) {
1805
+ const routeSessionId = plan.byoSessionId || void 0;
1806
+ return {
1807
+ providerId: plan.byoActivity?.providerId ?? plan.transformerProvider.name,
1808
+ credentialKind: "provider-key",
1809
+ keyId: plan.byoActivity?.resolveKeyId(),
1810
+ endpoint: "messages",
1811
+ sessionKey: routeSessionId,
1812
+ sessionSource: routeSessionId ? "route-session-id" : "none",
1813
+ model,
1814
+ onRecorded
1815
+ };
1816
+ }
1817
+ return {
1818
+ providerId: proxyProviderId(plan),
1819
+ credentialKind: "subscription-account",
1820
+ accountId: proxyAccountId,
1821
+ endpoint: "messages",
1822
+ sessionKey: plan.sessionKey,
1823
+ sessionSource: plan.sessionKey ? "content-fingerprint" : "none",
1824
+ model,
1825
+ onRecorded
1826
+ };
1827
+ }
1669
1828
  async function runPipeline(anthropicBody, plan, reportSelection, signal, callerOpenCodeSession) {
1670
1829
  const executor = getSharedExecutor();
1671
1830
  const endpointTransformer = getAnthropicEndpointTransformer();
@@ -1715,15 +1874,14 @@ async function runPipeline(anthropicBody, plan, reportSelection, signal, callerO
1715
1874
  {
1716
1875
  providerId: proxyProviderId(plan),
1717
1876
  accountId: proxyAccountId,
1718
- routeActivity: plan.isSubscription ? {
1719
- endpoint: "messages",
1720
- sessionKey: plan.sessionKey,
1721
- sessionSource: plan.sessionKey ? "content-fingerprint" : "none",
1722
- model: resolvedModel,
1723
- onRecorded: (record5) => {
1877
+ routeActivity: buildAnthropicRouteActivity(
1878
+ plan,
1879
+ proxyAccountId,
1880
+ resolvedModel,
1881
+ (record5) => {
1724
1882
  activityRecordId = record5.id;
1725
1883
  }
1726
- } : void 0
1884
+ )
1727
1885
  }
1728
1886
  ).then((r) => {
1729
1887
  rawStatus = r.status;
@@ -1786,15 +1944,14 @@ async function runSubscriptionSameFormatFetch(rawBody, plan, reportSelection, op
1786
1944
  {
1787
1945
  providerId: proxyProviderId(plan),
1788
1946
  accountId: proxyAccountId,
1789
- routeActivity: plan.isSubscription ? {
1790
- endpoint: "messages",
1791
- sessionKey: plan.sessionKey,
1792
- sessionSource: plan.sessionKey ? "content-fingerprint" : "none",
1793
- model: outboundModel,
1794
- onRecorded: (record5) => {
1947
+ routeActivity: buildAnthropicRouteActivity(
1948
+ plan,
1949
+ proxyAccountId,
1950
+ outboundModel,
1951
+ (record5) => {
1795
1952
  activityRecordId = record5.id;
1796
1953
  }
1797
- } : void 0
1954
+ )
1798
1955
  }
1799
1956
  );
1800
1957
  return {
@@ -2818,7 +2975,7 @@ async function resolveResponsesRouteProfile(route, deps, resolvedModel) {
2818
2975
  }
2819
2976
  async function buildResponsesCallPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, requestHeaders, affinity) {
2820
2977
  if (route.authMode === "byo") {
2821
- return buildByoPlan(route, deps, resolved, resolvedModel, isStream, affinity);
2978
+ return buildByoPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, affinity);
2822
2979
  }
2823
2980
  return buildSubscriptionPlan2(
2824
2981
  route,
@@ -2832,7 +2989,7 @@ async function buildResponsesCallPlan(route, deps, resolved, resolvedModel, isSt
2832
2989
  affinity
2833
2990
  );
2834
2991
  }
2835
- async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, affinity) {
2992
+ async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, affinity) {
2836
2993
  const providerId = route.providerId;
2837
2994
  const provider = resolved.provider;
2838
2995
  const affinityKeyId = affinity?.credential.kind === "byo-key" ? affinity.credential.id : void 0;
@@ -2883,7 +3040,11 @@ async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, affi
2883
3040
  resolveCredential: () => {
2884
3041
  const keyId = route.sessionId && typeof deps.apiKeyPool?.getKeyIdForSession === "function" ? deps.apiKeyPool.getKeyIdForSession(providerId, route.sessionId) : null;
2885
3042
  return keyId ? { kind: "byo-key", id: keyId } : credential;
2886
- }
3043
+ },
3044
+ byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
3045
+ byoSessionId: route.sessionId ?? null,
3046
+ sessionKey,
3047
+ sessionSource
2887
3048
  };
2888
3049
  }
2889
3050
  async function buildSubscriptionPlan2(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, requestHeaders, affinity) {
@@ -2954,6 +3115,7 @@ async function resolveGeminiProject(profile, hints) {
2954
3115
  if (!accessToken) return void 0;
2955
3116
  return getGeminiCodeAssistResolver()?.resolveProject(accessToken);
2956
3117
  }
3118
+ var resolveGeminiProjectForChatBridge = resolveGeminiProject;
2957
3119
  async function resolveAntigravityProjectThreaded(profile, hints) {
2958
3120
  const headers = {};
2959
3121
  await profile.authStrategy.applyHeaders(headers, hints);
@@ -2985,6 +3147,32 @@ async function executeResponsesUpstream(body, plan, operation, signal) {
2985
3147
  }
2986
3148
  return result;
2987
3149
  }
3150
+ function buildResponsesRouteActivity(plan, accountId, actualModel, onRecorded) {
3151
+ if (plan.proxyProviderId === "byo") {
3152
+ const contentKey = plan.sessionKey?.trim() || void 0;
3153
+ const routeSessionId = plan.byoSessionId || void 0;
3154
+ return {
3155
+ providerId: plan.byoActivity?.providerId ?? plan.providerIdentity,
3156
+ credentialKind: "provider-key",
3157
+ keyId: plan.byoActivity?.resolveKeyId(),
3158
+ endpoint: "responses",
3159
+ sessionKey: contentKey ?? routeSessionId,
3160
+ sessionSource: contentKey ? plan.sessionSource ?? "none" : routeSessionId ? "route-session-id" : "none",
3161
+ model: actualModel,
3162
+ onRecorded
3163
+ };
3164
+ }
3165
+ return {
3166
+ providerId: plan.proxyProviderId,
3167
+ credentialKind: "subscription-account",
3168
+ accountId,
3169
+ endpoint: "responses",
3170
+ sessionKey: plan.sessionKey,
3171
+ sessionSource: plan.sessionSource ?? "none",
3172
+ model: actualModel,
3173
+ onRecorded
3174
+ };
3175
+ }
2988
3176
  async function applyPlanAuth(body, plan) {
2989
3177
  let accountId;
2990
3178
  let actualModel = plan.resolvedModel;
@@ -3004,12 +3192,16 @@ async function applyPlanAuth(body, plan) {
3004
3192
  });
3005
3193
  body.model = actualModel;
3006
3194
  plan.transformerProvider.models = [actualModel];
3195
+ if (plan.proxyProviderId === "codex") {
3196
+ body.store = false;
3197
+ if (!plan.isStream) body.stream = true;
3198
+ }
3007
3199
  return { headers, accountId, actualModel };
3008
3200
  }
3009
3201
  function decorateCodexHeaders(headers, plan) {
3010
3202
  if (plan.proxyProviderId !== "codex") return;
3011
3203
  fillMissingHeaders(headers, plan.callerClientHeaders ?? {});
3012
- fillMissingHeaders(headers, { accept: codexAcceptHeader(plan.isStream) });
3204
+ fillMissingHeaders(headers, { accept: codexAcceptHeader(true) });
3013
3205
  fillMissingCodexCliIdentity(headers);
3014
3206
  }
3015
3207
  async function runNative(body, plan, operation, signal) {
@@ -3025,15 +3217,9 @@ async function runNative(body, plan, operation, signal) {
3025
3217
  {
3026
3218
  providerId: plan.proxyProviderId,
3027
3219
  accountId,
3028
- routeActivity: plan.proxyProviderId === "byo" ? void 0 : {
3029
- endpoint: "responses",
3030
- sessionKey: plan.sessionKey,
3031
- sessionSource: plan.sessionSource ?? "none",
3032
- model: actualModel,
3033
- onRecorded: (record5) => {
3034
- activityRecordId = record5.id;
3035
- }
3036
- }
3220
+ routeActivity: buildResponsesRouteActivity(plan, accountId, actualModel, (record5) => {
3221
+ activityRecordId = record5.id;
3222
+ })
3037
3223
  }
3038
3224
  );
3039
3225
  return {
@@ -3078,15 +3264,9 @@ async function runReduced(body, plan, signal) {
3078
3264
  {
3079
3265
  providerId: plan.proxyProviderId,
3080
3266
  accountId,
3081
- routeActivity: plan.proxyProviderId === "byo" ? void 0 : {
3082
- endpoint: "responses",
3083
- sessionKey: plan.sessionKey,
3084
- sessionSource: plan.sessionSource ?? "none",
3085
- model: actualModel,
3086
- onRecorded: (record5) => {
3087
- activityRecordId = record5.id;
3088
- }
3089
- }
3267
+ routeActivity: buildResponsesRouteActivity(plan, accountId, actualModel, (record5) => {
3268
+ activityRecordId = record5.id;
3269
+ })
3090
3270
  }
3091
3271
  );
3092
3272
  const upstream = plan.proxyProviderId === "antigravity" ? await fetchWithAntigravityFailover(url, send, signal) : await send(url);
@@ -4070,6 +4250,30 @@ async function handleResponsesOperation(res, responsesBody, route, deps, request
4070
4250
  })
4071
4251
  };
4072
4252
  }
4253
+ if (!isStream) {
4254
+ if (await sniffIsSseResponse(providerResponse.response)) {
4255
+ const collapsed = await aggregateResponsesSseToJsonBody(
4256
+ providerResponse.response,
4257
+ route.requestedModel,
4258
+ signal
4259
+ );
4260
+ if (collapsed !== null) {
4261
+ providerResponse = {
4262
+ ...providerResponse,
4263
+ response: new Response(collapsed, {
4264
+ status: providerResponse.response.status,
4265
+ headers: { "Content-Type": "application/json" }
4266
+ })
4267
+ };
4268
+ } else {
4269
+ throw new OpenAIOperationError({
4270
+ status: 502,
4271
+ code: "upstream_stream_collapse_failed",
4272
+ message: "upstream streamed no terminal response event"
4273
+ });
4274
+ }
4275
+ }
4276
+ }
4073
4277
  let overloadRecorded = false;
4074
4278
  let usageLimitRecorded = false;
4075
4279
  const observeSse = (event) => {
@@ -4329,7 +4533,9 @@ async function buildByoPlan2(res, route, deps, resolvedModel, isStream) {
4329
4533
  isSubscription: false,
4330
4534
  provider,
4331
4535
  apiKey,
4332
- extendedContextEnabled: route.anthropicSdkHints?.extendedContext?.enabled ?? false
4536
+ extendedContextEnabled: route.anthropicSdkHints?.extendedContext?.enabled ?? false,
4537
+ byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
4538
+ byoSessionId: route.sessionId ?? null
4333
4539
  };
4334
4540
  }
4335
4541
  async function runSameFormatFetch(bodyToSend, plan, options, keyOverride, urlOverride) {
@@ -4355,12 +4561,30 @@ async function runSameFormatFetch(bodyToSend, plan, options, keyOverride, urlOve
4355
4561
  headers["anthropic-version"] = callerVersion || DEFAULT_ANTHROPIC_VERSION;
4356
4562
  const url = urlOverride ?? buildProviderApiUrl(provider, { model: resolvedModel, stream: isStream });
4357
4563
  console.info(`[ProviderProxy:anthropic] (same-format) -> ${url} model=${resolvedModel} stream=${isStream}`);
4564
+ let activityRecordId;
4358
4565
  const response = await fetchUpstream(
4359
4566
  url,
4360
4567
  { method: "POST", headers, body: bodyToSend, signal: options.signal },
4361
- { providerId: "byo" }
4568
+ {
4569
+ providerId: "byo",
4570
+ routeActivity: {
4571
+ providerId: plan.byoActivity?.providerId ?? provider.name,
4572
+ credentialKind: "provider-key",
4573
+ // LIVE resolution: a pool rebind retry re-invokes this fetch, and the
4574
+ // binding has already rotated, so the retried attempt attributes the
4575
+ // NEW key. `keyOverride` is a key STRING (never recorded).
4576
+ keyId: plan.byoActivity?.resolveKeyId(),
4577
+ endpoint: "messages",
4578
+ sessionKey: plan.byoSessionId || void 0,
4579
+ sessionSource: plan.byoSessionId ? "route-session-id" : "none",
4580
+ model: resolvedModel,
4581
+ onRecorded: (record5) => {
4582
+ activityRecordId = record5.id;
4583
+ }
4584
+ }
4585
+ }
4362
4586
  );
4363
- return { response, rawStatus: response.status };
4587
+ return { response, rawStatus: response.status, activityRecordId };
4364
4588
  }
4365
4589
  function resolveSameFormatBody(anthropicBody, rawBody, plan) {
4366
4590
  let originalModel;
@@ -4903,7 +5127,9 @@ async function buildByoPlan3(res, route, deps, resolvedModel, isStream) {
4903
5127
  resolvedModel,
4904
5128
  isStream,
4905
5129
  resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : byoUrl,
4906
- upstreamUrl: byoUrl
5130
+ upstreamUrl: byoUrl,
5131
+ byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
5132
+ byoSessionId: route.sessionId ?? null
4907
5133
  };
4908
5134
  }
4909
5135
  async function runPipeline2(geminiBody, plan) {
@@ -4934,7 +5160,18 @@ async function runPipeline2(geminiBody, plan) {
4934
5160
  return fetchUpstream(
4935
5161
  url,
4936
5162
  { method: "POST", headers, body: JSON.stringify(body) },
4937
- { providerId: "byo" }
5163
+ {
5164
+ providerId: "byo",
5165
+ routeActivity: {
5166
+ providerId: plan.byoActivity?.providerId ?? transformerProvider.name,
5167
+ credentialKind: "provider-key",
5168
+ keyId: plan.byoActivity?.resolveKeyId(),
5169
+ endpoint: "generateContent",
5170
+ sessionKey: plan.byoSessionId || void 0,
5171
+ sessionSource: plan.byoSessionId ? "route-session-id" : "none",
5172
+ model: resolvedModel
5173
+ }
5174
+ }
4938
5175
  ).then((r) => {
4939
5176
  rawStatus = r.status;
4940
5177
  return r;
@@ -5015,7 +5252,16 @@ function recordChatCompletionsStreamUsage(recorder, rawUsage, attribution) {
5015
5252
  }
5016
5253
 
5017
5254
  // src/provider-proxy/ingress/openaiChatIngress.ts
5018
- var CHAT_BRIDGE_SUBSCRIPTION_PROVIDERS = /* @__PURE__ */ new Set(["claude"]);
5255
+ var CHAT_BRIDGE_SUBSCRIPTION_PROVIDERS = /* @__PURE__ */ new Set([
5256
+ "claude",
5257
+ "kimi",
5258
+ "grok",
5259
+ "opencodego",
5260
+ "codex",
5261
+ "gemini",
5262
+ "copilot",
5263
+ "antigravity"
5264
+ ]);
5019
5265
  var IDENTITY_ENDPOINT_TRANSFORMER = { name: "identity-openai-chat" };
5020
5266
  async function handleOpenAIChatRequest(res, rawBody, route, deps) {
5021
5267
  let chatBody;
@@ -5029,9 +5275,26 @@ async function handleOpenAIChatRequest(res, rawBody, route, deps) {
5029
5275
  const resolvedModel = route.model;
5030
5276
  chatBody.model = resolvedModel;
5031
5277
  try {
5032
- const plan = route.authMode === "subscription" ? await buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream) : await buildByoPlan4(res, route, deps, resolvedModel, isStream);
5278
+ const plan = route.authMode === "subscription" ? await buildSubscriptionPlan3(res, route, deps, chatBody, resolvedModel, isStream) : await buildByoPlan4(res, route, deps, resolvedModel, isStream);
5033
5279
  if (!plan) return;
5034
- const providerResponse = plan.isSubscription ? await runPipelineWithSubscriptionRetry2(chatBody, plan) : await runPipelineWithPoolReporting3(chatBody, plan);
5280
+ let providerResponse = plan.isSubscription ? await runPipelineWithSubscriptionRetry2(chatBody, plan) : await runPipelineWithPoolReporting3(chatBody, plan);
5281
+ if (!isStream) {
5282
+ if (await sniffIsSseResponse(providerResponse.response)) {
5283
+ const collapsed = await aggregateOpenAIChatChunksToJsonBody(providerResponse.response);
5284
+ if (collapsed !== null) {
5285
+ providerResponse = {
5286
+ response: new Response(collapsed, {
5287
+ status: providerResponse.response.status,
5288
+ headers: { "Content-Type": "application/json" }
5289
+ }),
5290
+ rawStatus: providerResponse.rawStatus
5291
+ };
5292
+ } else {
5293
+ writeError(res, 502, "upstream streamed no completable chat chunks");
5294
+ return;
5295
+ }
5296
+ }
5297
+ }
5035
5298
  const usageAttribution = {
5036
5299
  sessionId: route.sessionId,
5037
5300
  providerId: route.providerId ?? "openai",
@@ -5118,10 +5381,12 @@ async function buildByoPlan4(res, route, deps, resolvedModel, isStream) {
5118
5381
  resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : byoUrl,
5119
5382
  upstreamUrl: byoUrl,
5120
5383
  proxyProviderId: "byo",
5121
- isSubscription: false
5384
+ isSubscription: false,
5385
+ byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
5386
+ routeSessionId: route.sessionId ?? null
5122
5387
  };
5123
5388
  }
5124
- async function buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream) {
5389
+ async function buildSubscriptionPlan3(res, route, deps, chatBody, resolvedModel, isStream) {
5125
5390
  const profile = route.subscriptionProfile;
5126
5391
  if (!profile) {
5127
5392
  writeError(res, 502, "Subscription mode requires a subscription profile");
@@ -5132,27 +5397,56 @@ async function buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream)
5132
5397
  writeError(
5133
5398
  res,
5134
5399
  501,
5135
- `the OpenAI-chat\u2192subscription bridge currently supports claude only; subscription provider '${providerId}' is not implemented on this endpoint yet (use the /v1/responses endpoint, a Claude subscription, or a BYO provider)`
5400
+ `the OpenAI-chat\u2192subscription bridge does not implement subscription provider '${providerId}' on this endpoint yet (use the /v1/responses endpoint, a Claude/Kimi/Grok/OpenCodeGo subscription, or a BYO provider)`
5136
5401
  );
5137
5402
  return null;
5138
5403
  }
5139
- const upstreamUrl = profile.resolveUpstreamUrl?.(resolvedModel);
5404
+ let mappedModel = resolvedModel;
5405
+ if (profile.modelMapper) {
5406
+ const summary = buildSubscriptionRequestSummary(chatBody);
5407
+ const mapped = profile.modelMapper(resolvedModel, summary, route.subscriptionConfig);
5408
+ mappedModel = mapped.resolvedModel;
5409
+ }
5410
+ const upstreamUrl = profile.resolveUpstreamUrl?.(mappedModel, route.subscriptionConfig);
5140
5411
  if (!upstreamUrl) {
5141
5412
  writeError(res, 502, "Subscription profile is missing resolveUpstreamUrl");
5142
5413
  return null;
5143
5414
  }
5144
5415
  const auth = route.auth ?? new SubscriptionAuthSource(profile);
5416
+ const chatChainNames = profile.chatBridgeTransformerNames?.(mappedModel, route.subscriptionConfig) ?? profile.resolveProviderTransformerNames?.(mappedModel, route.subscriptionConfig);
5145
5417
  const chain = resolveSubscriptionChain(
5146
5418
  profile,
5147
5419
  deps.llmConfig.getTransformerService(),
5148
- IDENTITY_ENDPOINT_TRANSFORMER
5420
+ IDENTITY_ENDPOINT_TRANSFORMER,
5421
+ chatChainNames
5149
5422
  );
5150
5423
  const transformerProvider = {
5151
5424
  name: providerId,
5152
5425
  baseUrl: upstreamUrl,
5153
5426
  apiKey: "",
5154
- models: [resolvedModel]
5427
+ models: [mappedModel]
5155
5428
  };
5429
+ const sessionKey = deriveSubscriptionSessionKey(chatBody);
5430
+ if (providerId === "gemini") {
5431
+ transformerProvider.geminiProject = await resolveGeminiProjectForChatBridge(profile, {
5432
+ sessionKey,
5433
+ preferredAccountId: route.preferredAccountId,
5434
+ preferredAccountGroup: route.preferredAccountGroup,
5435
+ boundAccountFallbackPolicy: route.boundAccountFallbackPolicy
5436
+ });
5437
+ } else if (providerId === "antigravity") {
5438
+ transformerProvider.geminiProject = await resolveAntigravityProjectThreaded(profile, {
5439
+ resolvedModel: mappedModel,
5440
+ reportSelection: () => {
5441
+ },
5442
+ sessionKey,
5443
+ preferredAccountId: route.preferredAccountId,
5444
+ preferredAccountGroup: route.preferredAccountGroup,
5445
+ boundAccountFallbackPolicy: route.boundAccountFallbackPolicy
5446
+ });
5447
+ }
5448
+ chatBody.model = mappedModel;
5449
+ const resolveUrl = providerId === "antigravity" ? (config) => config.url instanceof URL ? config.url.toString() : typeof config.url === "string" ? config.url : upstreamUrl : () => upstreamUrl;
5156
5450
  return {
5157
5451
  auth,
5158
5452
  preferredAccountId: route.preferredAccountId,
@@ -5160,12 +5454,14 @@ async function buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream)
5160
5454
  boundAccountFallbackPolicy: route.boundAccountFallbackPolicy,
5161
5455
  chain,
5162
5456
  transformerProvider,
5163
- resolvedModel,
5457
+ resolvedModel: mappedModel,
5164
5458
  isStream,
5165
- resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : typeof config.url === "string" ? config.url : upstreamUrl,
5459
+ resolveUrl,
5166
5460
  upstreamUrl,
5167
5461
  proxyProviderId: providerId,
5168
- isSubscription: true
5462
+ isSubscription: true,
5463
+ contentSessionKey: sessionKey,
5464
+ routeSessionId: route.sessionId ?? null
5169
5465
  };
5170
5466
  }
5171
5467
  async function runPipeline3(chatBody, plan) {
@@ -5176,6 +5472,7 @@ async function runPipeline3(chatBody, plan) {
5176
5472
  await auth.applyHeaders(authHeaders, {
5177
5473
  upstreamUrl,
5178
5474
  model: resolvedModel,
5475
+ sessionKey: plan.contentSessionKey,
5179
5476
  preferredAccountId: plan.preferredAccountId,
5180
5477
  preferredAccountGroup: plan.preferredAccountGroup,
5181
5478
  boundAccountFallbackPolicy: plan.boundAccountFallbackPolicy,
@@ -5197,14 +5494,36 @@ async function runPipeline3(chatBody, plan) {
5197
5494
  if (value !== void 0 && !(key in headers)) headers[key] = value;
5198
5495
  }
5199
5496
  }
5497
+ if (plan.proxyProviderId === "codex") fillMissingCodexCliIdentity(headers);
5200
5498
  return headers;
5201
5499
  },
5202
5500
  fetchFn: (url, headers, body) => {
5203
5501
  console.log(`[ProviderProxy:chat] -> ${url} model=${resolvedModel} stream=${isStream}`);
5502
+ const sessionKey = plan.routeSessionId || void 0;
5204
5503
  return fetchUpstream(
5205
5504
  url,
5206
5505
  { method: "POST", headers, body: JSON.stringify(body) },
5207
- { providerId: plan.proxyProviderId, accountId: proxyAccountId }
5506
+ {
5507
+ providerId: plan.proxyProviderId,
5508
+ accountId: proxyAccountId,
5509
+ routeActivity: plan.isSubscription ? {
5510
+ providerId: plan.proxyProviderId,
5511
+ credentialKind: "subscription-account",
5512
+ accountId: proxyAccountId,
5513
+ endpoint: "chat",
5514
+ sessionKey,
5515
+ sessionSource: sessionKey ? "route-session-id" : "none",
5516
+ model: resolvedModel
5517
+ } : {
5518
+ providerId: plan.byoActivity?.providerId ?? transformerProvider.name,
5519
+ credentialKind: "provider-key",
5520
+ keyId: plan.byoActivity?.resolveKeyId(),
5521
+ endpoint: "chat",
5522
+ sessionKey,
5523
+ sessionSource: sessionKey ? "route-session-id" : "none",
5524
+ model: resolvedModel
5525
+ }
5526
+ }
5208
5527
  ).then((r) => {
5209
5528
  rawStatus = r.status;
5210
5529
  return r;
@@ -5983,17 +6302,22 @@ function wildcardMatches(pattern, value) {
5983
6302
  const escaped = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join(".*");
5984
6303
  return new RegExp(`^${escaped}$`, "iu").test(value);
5985
6304
  }
5986
- function resolveGatewayModelMapping(mappings, requestedModel) {
6305
+ function resolveGatewayModelMappingRow(mappings, requestedModel) {
5987
6306
  if (!nonBlank(requestedModel)) return void 0;
5988
6307
  const wanted = requestedModel.trim();
5989
6308
  const exact = (mappings ?? []).find(
5990
6309
  (mapping) => nonBlank(mapping.source) && !mapping.source.includes("*") && mapping.source.trim().toLocaleLowerCase() === wanted.toLocaleLowerCase()
5991
6310
  );
5992
- if (exact && nonBlank(exact.target)) return exact.target.trim();
6311
+ if (exact && nonBlank(exact.target)) {
6312
+ return { target: exact.target.trim(), effort: exact.effort };
6313
+ }
5993
6314
  const wildcard = (mappings ?? []).find(
5994
6315
  (mapping) => nonBlank(mapping.source) && mapping.source.includes("*") && wildcardMatches(mapping.source.trim(), wanted)
5995
6316
  );
5996
- return wildcard && nonBlank(wildcard.target) ? wildcard.target.trim() : void 0;
6317
+ return wildcard && nonBlank(wildcard.target) ? { target: wildcard.target.trim(), effort: wildcard.effort } : void 0;
6318
+ }
6319
+ function resolveGatewayModelMapping(mappings, requestedModel) {
6320
+ return resolveGatewayModelMappingRow(mappings, requestedModel)?.target;
5997
6321
  }
5998
6322
  function routeCanServe(binding, requestedModel, role) {
5999
6323
  if (binding.modelMode === "passthrough") return nonBlank(requestedModel);
@@ -6067,9 +6391,11 @@ function gatewayBindingToEndpointConfig(binding, requestedModel) {
6067
6391
  useSubscription: target.kind !== "provider"
6068
6392
  };
6069
6393
  const usesGenericModelHandling = binding.modelMode === "passthrough" || Boolean(binding.modelMappings?.length);
6070
- const dynamicModel = binding.modelMode === "passthrough" ? requestedModel : resolveGatewayModelMapping(binding.modelMappings, requestedModel);
6394
+ const winningMapping = binding.modelMode === "passthrough" ? void 0 : resolveGatewayModelMappingRow(binding.modelMappings, requestedModel);
6395
+ const dynamicModel = binding.modelMode === "passthrough" ? requestedModel : winningMapping?.target;
6071
6396
  if (usesGenericModelHandling) {
6072
6397
  if (nonBlank(dynamicModel)) applySingleModel(config, binding, dynamicModel);
6398
+ if (winningMapping?.effort) config.reasoningEffort = winningMapping.effort;
6073
6399
  } else if (isKindMappedEndpoint(binding.endpoint)) {
6074
6400
  config.modelMap = Object.fromEntries(
6075
6401
  Object.entries(binding.modelMap ?? {}).map(([kind, ref]) => [kind, targetRef(target, ref)])
@@ -8494,6 +8820,7 @@ function normalizeBindingTarget(raw) {
8494
8820
  }
8495
8821
  return null;
8496
8822
  }
8823
+ var MAPPING_EFFORT_MAX_LENGTH = 32;
8497
8824
  function normalizeGatewayModelMappings(raw) {
8498
8825
  if (!Array.isArray(raw)) return [];
8499
8826
  const mappings = [];
@@ -8503,7 +8830,9 @@ function normalizeGatewayModelMappings(raw) {
8503
8830
  const source = typeof value.source === "string" ? value.source.trim() : "";
8504
8831
  const target = typeof value.target === "string" ? value.target.trim() : "";
8505
8832
  if (!source || !target) continue;
8506
- mappings.push({ source, target });
8833
+ const rawEffort = typeof value.effort === "string" ? value.effort.trim() : "";
8834
+ const effort = rawEffort && rawEffort.length <= MAPPING_EFFORT_MAX_LENGTH ? rawEffort : void 0;
8835
+ mappings.push(effort ? { source, target, effort } : { source, target });
8507
8836
  }
8508
8837
  return mappings;
8509
8838
  }
@@ -8795,6 +9124,61 @@ function deriveAuditSessionKey(body, headers = {}, options = {}) {
8795
9124
  return gateway.key;
8796
9125
  }
8797
9126
 
9127
+ // src/outbound-api/mappingEffortInjection.ts
9128
+ function isPlainObject(value) {
9129
+ return !!value && typeof value === "object" && !Array.isArray(value);
9130
+ }
9131
+ function hasOpenAiWireReasoningIntent(body) {
9132
+ return extractReasoningIntent({
9133
+ model: typeof body["model"] === "string" ? body["model"] : "",
9134
+ reasoning: isPlainObject(body["reasoning"]) ? body["reasoning"] : void 0,
9135
+ reasoning_effort: body["reasoning_effort"]
9136
+ }) !== void 0;
9137
+ }
9138
+ function injectMappingEffortDefault(endpoint, effort, body) {
9139
+ if (!effort) return false;
9140
+ switch (endpoint) {
9141
+ case "chat":
9142
+ if (hasOpenAiWireReasoningIntent(body)) {
9143
+ return false;
9144
+ }
9145
+ body["reasoning_effort"] = effort;
9146
+ return true;
9147
+ case "responses": {
9148
+ if (hasOpenAiWireReasoningIntent(body)) {
9149
+ return false;
9150
+ }
9151
+ const reasoning = body["reasoning"];
9152
+ body["reasoning"] = {
9153
+ ...isPlainObject(reasoning) ? reasoning : {},
9154
+ effort
9155
+ };
9156
+ return true;
9157
+ }
9158
+ case "messages": {
9159
+ if (body["thinking"] !== void 0) return false;
9160
+ if (effort === "none") return false;
9161
+ body["thinking"] = { type: "adaptive" };
9162
+ const outputConfig = body["output_config"];
9163
+ body["output_config"] = {
9164
+ ...isPlainObject(outputConfig) ? outputConfig : {},
9165
+ effort
9166
+ };
9167
+ return true;
9168
+ }
9169
+ case "gemini": {
9170
+ const generationConfig = body["generationConfig"];
9171
+ const config = isPlainObject(generationConfig) ? generationConfig : {};
9172
+ if (config["thinkingConfig"] !== void 0) return false;
9173
+ body["generationConfig"] = {
9174
+ ...config,
9175
+ thinkingConfig: { thinkingLevel: effort }
9176
+ };
9177
+ return true;
9178
+ }
9179
+ }
9180
+ }
9181
+
8798
9182
  // src/outbound-api/outboundApiKeyAuth.ts
8799
9183
  import { createHash as createHash3, randomBytes as randomBytes4 } from "crypto";
8800
9184
 
@@ -8981,8 +9365,23 @@ function toVerifiedKey(row) {
8981
9365
  if (rateLimit) key.rateLimit = rateLimit;
8982
9366
  const modelRestriction = extractModelRestriction(row);
8983
9367
  if (modelRestriction) key.modelRestriction = modelRestriction;
9368
+ const directTarget = extractBoundUpstream(row);
9369
+ if (directTarget) key.boundUpstream = directTarget;
8984
9370
  return key;
8985
9371
  }
9372
+ function extractBoundUpstream(row) {
9373
+ if (row.boundUpstream && typeof row.boundUpstream === "object") {
9374
+ const target = row.boundUpstream;
9375
+ if (target.kind === "provider" && typeof target.providerId === "string" && target.providerId || target.kind === "account-pool" && typeof target.providerId === "string" && target.providerId || target.kind === "account" && target.providerId && target.accountId || target.kind === "account-group" && target.providerId && target.group) {
9376
+ return target;
9377
+ }
9378
+ return void 0;
9379
+ }
9380
+ if (typeof row.boundUpstreamProviderId === "string" && row.boundUpstreamProviderId) {
9381
+ return { kind: "provider", providerId: row.boundUpstreamProviderId };
9382
+ }
9383
+ return void 0;
9384
+ }
8986
9385
  async function verifyKey(db, presentedKey, now = Date.now()) {
8987
9386
  if (!presentedKey) return { status: "invalid" };
8988
9387
  const trimmed = presentedKey.trim();
@@ -9908,7 +10307,102 @@ function resolveModelsShape(modelsShape, allowedEndpoints) {
9908
10307
  if (!allowedEndpoints || allowedEndpoints.length === 0) return "anthropic";
9909
10308
  return allowedEndpoints.includes("messages") ? "anthropic" : "openai";
9910
10309
  }
10310
+ var UPSTREAM_MODELS_TTL_MS = 6e4;
10311
+ var UPSTREAM_MODELS_NEGATIVE_TTL_MS = 5e3;
10312
+ var UPSTREAM_MODELS_TIMEOUT_MS = 1e4;
10313
+ var upstreamModelsCache = /* @__PURE__ */ new Map();
10314
+ var upstreamModelsInFlight = /* @__PURE__ */ new Map();
10315
+ function resolveProviderKeyLiteral(apiKey) {
10316
+ if (!apiKey) return "";
10317
+ if (apiKey.startsWith("$")) return process.env[apiKey.slice(1)] || "";
10318
+ return apiKey;
10319
+ }
10320
+ function buildProviderModelsUrl(baseUrl, format) {
10321
+ const base = baseUrl.trim().replace(/\/+$/, "");
10322
+ if (!base) return null;
10323
+ if (format === "google") {
10324
+ return /\/models$/.test(base) ? base : `${base}/models`;
10325
+ }
10326
+ if (format === "anthropic") {
10327
+ if (base.endsWith("/messages")) return `${base.slice(0, -"/messages".length)}/models`;
10328
+ if (/\/v\d+$/.test(base)) return `${base}/models`;
10329
+ return `${base}/v1/models`;
10330
+ }
10331
+ const completionSuffix = format === "openai-response" ? "/responses" : "/chat/completions";
10332
+ const root = base.endsWith(completionSuffix) ? base.slice(0, -completionSuffix.length) : base;
10333
+ if (root !== base || /\/v\d+$/.test(base)) return `${root}/models`;
10334
+ return `${base}/v1/models`;
10335
+ }
10336
+ async function fetchUpstreamModelsUncached(provider) {
10337
+ const format = resolveApiFormat(provider);
10338
+ const url = buildProviderModelsUrl(provider.api_base_url ?? "", format);
10339
+ if (!url) return [];
10340
+ const headers = getProviderHeaders(provider, resolveProviderKeyLiteral(provider.api_key));
10341
+ delete headers["Content-Type"];
10342
+ headers["Accept"] = "application/json";
10343
+ const response = await fetchUpstream(
10344
+ url,
10345
+ { method: "GET", headers, signal: AbortSignal.timeout(UPSTREAM_MODELS_TIMEOUT_MS) },
10346
+ { providerId: "byo" }
10347
+ );
10348
+ if (!response.ok) return [];
10349
+ const body = await response.json();
10350
+ const fromData = Array.isArray(body?.data) ? body.data.map((entry) => typeof entry?.id === "string" ? entry.id.trim() : "").filter((id) => id !== "") : [];
10351
+ if (fromData.length > 0) return fromData;
10352
+ return Array.isArray(body?.models) ? body.models.map(
10353
+ (entry) => typeof entry?.name === "string" ? entry.name.replace(/^models\//, "").trim() : ""
10354
+ ).filter((id) => id !== "") : [];
10355
+ }
10356
+ async function discoverUpstreamModels(provider) {
10357
+ const cached = upstreamModelsCache.get(provider.id);
10358
+ if (cached) {
10359
+ const ttl = cached.ok ? UPSTREAM_MODELS_TTL_MS : UPSTREAM_MODELS_NEGATIVE_TTL_MS;
10360
+ if (Date.now() - cached.at < ttl) return cached.ids;
10361
+ upstreamModelsCache.delete(provider.id);
10362
+ }
10363
+ const existing = upstreamModelsInFlight.get(provider.id);
10364
+ if (existing) return existing;
10365
+ const pending = (async () => {
10366
+ let ids = [];
10367
+ try {
10368
+ ids = await fetchUpstreamModelsUncached(provider);
10369
+ } catch {
10370
+ ids = [];
10371
+ }
10372
+ upstreamModelsCache.set(provider.id, { at: Date.now(), ids, ok: ids.length > 0 });
10373
+ return ids;
10374
+ })();
10375
+ upstreamModelsInFlight.set(provider.id, pending);
10376
+ try {
10377
+ return await pending;
10378
+ } finally {
10379
+ upstreamModelsInFlight.delete(provider.id);
10380
+ }
10381
+ }
10382
+ async function passthroughProviderModelIds(llmConfig, providerId) {
10383
+ const provider = await llmConfig.getProvider(providerId);
10384
+ if (!provider) return [];
10385
+ const configured = provider.models ?? [];
10386
+ return configured.length > 0 ? configured : await discoverUpstreamModels(provider);
10387
+ }
9911
10388
  var ANTHROPIC_MODELS_CREATED_AT = (/* @__PURE__ */ new Date()).toISOString();
10389
+ function hasWildcardMapping(binding) {
10390
+ return (binding.modelMappings ?? []).some(
10391
+ (mapping) => mapping.source.includes("*") && mapping.target.trim() !== ""
10392
+ );
10393
+ }
10394
+ async function pushTargetCatalog(llmConfig, target, push) {
10395
+ if (target.kind === "provider") {
10396
+ for (const id of await passthroughProviderModelIds(llmConfig, target.providerId)) {
10397
+ push(id);
10398
+ }
10399
+ } else if (Object.prototype.hasOwnProperty.call(SUBSCRIPTION_MODEL_CATALOG, target.providerId)) {
10400
+ for (const id of SUBSCRIPTION_MODEL_CATALOG[target.providerId]) {
10401
+ push(id);
10402
+ }
10403
+ }
10404
+ }
10405
+ var MODEL_LIST_ENDPOINTS = ["chat", "responses", "messages", "gemini"];
9912
10406
  function parseModelsLimit(url) {
9913
10407
  const query = url?.split("?")[1];
9914
10408
  if (!query) return void 0;
@@ -9917,8 +10411,7 @@ function parseModelsLimit(url) {
9917
10411
  const n = Number.parseInt(raw, 10);
9918
10412
  return Number.isFinite(n) && n > 0 ? n : void 0;
9919
10413
  }
9920
- async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowedEndpoints, limit) {
9921
- void allowedEndpoints;
10414
+ async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowedEndpoints, limit, directTarget) {
9922
10415
  const entries2 = [];
9923
10416
  const seen = /* @__PURE__ */ new Set();
9924
10417
  const push = (id, displayName) => {
@@ -9932,29 +10425,37 @@ async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowe
9932
10425
  const modelId = ref ? parseModelRef(ref)?.modelId : void 0;
9933
10426
  return modelId ? `${name} (via ${modelId})` : void 0;
9934
10427
  };
9935
- for (const binding of candidateGatewayBindings(config.bindings, apiKeyId, "messages")) {
9936
- if (binding.modelMode === "passthrough") {
9937
- if (binding.target.kind === "provider") {
9938
- const provider = await llmConfig.getProvider(binding.target.providerId);
9939
- for (const id of provider?.models ?? []) push(id);
9940
- } else if (Object.prototype.hasOwnProperty.call(SUBSCRIPTION_MODEL_CATALOG, binding.target.providerId)) {
9941
- for (const id of SUBSCRIPTION_MODEL_CATALOG[binding.target.providerId]) {
9942
- push(id);
10428
+ for (const endpoint of MODEL_LIST_ENDPOINTS) {
10429
+ if (allowedEndpoints && !allowedEndpoints.includes(endpoint)) continue;
10430
+ for (const binding of candidateGatewayBindings(config.bindings, apiKeyId, endpoint)) {
10431
+ if (binding.modelMode === "passthrough") {
10432
+ await pushTargetCatalog(llmConfig, binding.target, push);
10433
+ continue;
10434
+ }
10435
+ if (binding.modelMappings?.length) {
10436
+ for (const mapping of binding.modelMappings) {
10437
+ const source = mapping.source.trim();
10438
+ if (source === "" || source.includes("*")) continue;
10439
+ push(source, viaLabel(source, mapping.target));
10440
+ }
10441
+ if (hasWildcardMapping(binding)) {
10442
+ await pushTargetCatalog(llmConfig, binding.target, push);
10443
+ for (const mapping of binding.modelMappings ?? []) {
10444
+ if (!mapping.source.includes("*") || mapping.target.trim() === "") continue;
10445
+ push(parseModelRef(mapping.target)?.modelId ?? mapping.target.trim());
10446
+ }
9943
10447
  }
10448
+ continue;
9944
10449
  }
9945
- continue;
9946
- }
9947
- if (binding.modelMappings?.length) {
9948
- for (const mapping of binding.modelMappings) {
9949
- const source = mapping.source.trim();
9950
- if (source === "" || source.includes("*")) continue;
9951
- push(source, viaLabel(source, mapping.target));
10450
+ for (const [kind, ref] of Object.entries(binding.modelMap ?? {})) {
10451
+ if (typeof ref !== "string" || ref.trim() === "") continue;
10452
+ push(kind, viaLabel(kind, ref));
9952
10453
  }
9953
- continue;
9954
10454
  }
9955
- for (const [kind, ref] of Object.entries(binding.modelMap ?? {})) {
9956
- if (typeof ref !== "string" || ref.trim() === "") continue;
9957
- push(kind, viaLabel(kind, ref));
10455
+ }
10456
+ if (directTarget?.kind === "provider") {
10457
+ for (const id of await passthroughProviderModelIds(llmConfig, directTarget.providerId)) {
10458
+ push(id);
9958
10459
  }
9959
10460
  }
9960
10461
  const limited = limit !== void 0 ? entries2.slice(0, limit) : entries2;
@@ -9996,29 +10497,172 @@ function handleOauthUsageProxy(res, config, apiKeyId) {
9996
10497
  res.writeHead(200, { "Content-Type": "application/json" });
9997
10498
  res.end(JSON.stringify(body));
9998
10499
  }
9999
- async function writeModelsList(res, llmConfig, config, apiKeyId, allowedEndpoints, imageModels = []) {
10500
+ function directUpstreamRoot(baseUrl, format) {
10501
+ const base = baseUrl.trim().replace(/\/+$/, "");
10502
+ if (!base) return null;
10503
+ if (format === "google") return base.replace(/\/models$/, "");
10504
+ if (format === "anthropic") return base.replace(/\/messages$/, "");
10505
+ if (format === "openai-response") return base.replace(/\/responses$/, "");
10506
+ return base.replace(/\/chat\/completions$/, "");
10507
+ }
10508
+ function directUpstreamUrl(provider, requestUrl) {
10509
+ const root = directUpstreamRoot(
10510
+ provider.api_base_url ?? "",
10511
+ resolveApiFormat(provider)
10512
+ );
10513
+ if (!root) return null;
10514
+ const raw = requestUrl ?? "/";
10515
+ const queryIndex = raw.indexOf("?");
10516
+ const path = (queryIndex >= 0 ? raw.slice(0, queryIndex) : raw) || "/";
10517
+ const query = queryIndex >= 0 ? raw.slice(queryIndex) : "";
10518
+ const versionSegment = /\/v\d+[a-z]*$/.exec(root)?.[0];
10519
+ let tail = path;
10520
+ if (versionSegment) {
10521
+ if (path === versionSegment) tail = "";
10522
+ else if (path.startsWith(`${versionSegment}/`)) tail = path.slice(versionSegment.length);
10523
+ }
10524
+ return `${root}${tail}${query}`;
10525
+ }
10526
+ function directUpstreamBinding(target, apiKeyId, priority = 1e3) {
10527
+ return {
10528
+ id: `direct:${apiKeyId}`,
10529
+ name: "direct upstream",
10530
+ enabled: true,
10531
+ keyScope: "selected",
10532
+ apiKeyIds: [apiKeyId],
10533
+ endpoint: "messages",
10534
+ target,
10535
+ priority,
10536
+ fallback: "fail",
10537
+ modelMode: "passthrough"
10538
+ };
10539
+ }
10540
+ async function relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, preReadBody) {
10541
+ const direct = verified.boundUpstream;
10542
+ const providerId = direct?.kind === "provider" ? direct.providerId : "";
10543
+ const provider = await deps.llmConfig.getProvider(providerId);
10544
+ if (!provider) {
10545
+ writeJsonError(
10546
+ res,
10547
+ 503,
10548
+ `direct upstream provider '${providerId}' is not configured for this key`
10549
+ );
10550
+ return;
10551
+ }
10552
+ const url = directUpstreamUrl(provider, req.url);
10553
+ if (!url) {
10554
+ writeJsonError(res, 503, `direct upstream provider '${providerId}' has no base URL`);
10555
+ return;
10556
+ }
10557
+ const concurrencyLimit = verified.maxConcurrency;
10558
+ let releaseConcurrency = null;
10559
+ if (typeof concurrencyLimit === "number" && concurrencyLimit > 0) {
10560
+ const cq = config.concurrencyQueue ?? DEFAULT_CONCURRENCY_QUEUE;
10561
+ const acquisition = concurrencyGate.acquire(verified.id, concurrencyLimit, {
10562
+ maxQueueSizeFactor: cq.maxQueueSizeFactor,
10563
+ minQueueSize: cq.minQueueSize,
10564
+ waitTimeoutMs: cq.waitTimeoutMs
10565
+ });
10566
+ const cancelOnClose = () => acquisition.cancel();
10567
+ res.once("close", cancelOnClose);
10568
+ let slot;
10569
+ try {
10570
+ slot = await acquisition.granted;
10571
+ } catch (err) {
10572
+ res.removeListener("close", cancelOnClose);
10573
+ if (isConcurrencyRejection(err)) {
10574
+ writeJsonError(res, 429, "Concurrency limit exceeded", { "Retry-After": "5" });
10575
+ return;
10576
+ }
10577
+ throw err;
10578
+ }
10579
+ res.removeListener("close", cancelOnClose);
10580
+ const release2 = () => slot.release();
10581
+ releaseConcurrency = release2;
10582
+ res.once("close", release2);
10583
+ }
10584
+ try {
10585
+ const rawBody = preReadBody !== void 0 ? preReadBody : await readBody3(req);
10586
+ if (audit) {
10587
+ audit.setRequestBody(rawBody);
10588
+ try {
10589
+ audit.sessionKey = deriveAuditSessionKey(
10590
+ rawBody ? JSON.parse(rawBody) : {},
10591
+ req.headers,
10592
+ { fallbackKey: verified.id, endpoint: "direct" }
10593
+ );
10594
+ } catch {
10595
+ }
10596
+ }
10597
+ const headers = getProviderHeaders(provider, resolveProviderKeyLiteral(provider.api_key));
10598
+ delete headers["Content-Type"];
10599
+ const clientContentType = req.headers["content-type"];
10600
+ if (typeof clientContentType === "string" && clientContentType) {
10601
+ headers["Content-Type"] = clientContentType;
10602
+ }
10603
+ const clientAccept = req.headers["accept"];
10604
+ if (typeof clientAccept === "string" && clientAccept) headers["Accept"] = clientAccept;
10605
+ const hasRequestBody = req.method !== "GET" && req.method !== "HEAD";
10606
+ const upstream = await fetchUpstream(
10607
+ url,
10608
+ {
10609
+ method: req.method ?? "GET",
10610
+ headers,
10611
+ ...hasRequestBody ? { body: rawBody } : {},
10612
+ // Client hang-up aborts the upstream call (no egress after cancel).
10613
+ signal: requestLifecycleSignal(req, res)
10614
+ },
10615
+ { providerId: "byo" }
10616
+ );
10617
+ if (audit) audit.provider = provider.id;
10618
+ const responseHeaders = {};
10619
+ const contentType = upstream.headers.get("content-type");
10620
+ if (contentType) responseHeaders["Content-Type"] = contentType;
10621
+ res.writeHead(upstream.status, responseHeaders);
10622
+ if (!upstream.body) {
10623
+ res.end();
10624
+ return;
10625
+ }
10626
+ const bodyStream = Readable2.fromWeb(
10627
+ upstream.body
10628
+ );
10629
+ bodyStream.on("error", () => {
10630
+ res.destroy();
10631
+ });
10632
+ bodyStream.pipe(res);
10633
+ await new Promise((resolve) => {
10634
+ res.once("close", resolve);
10635
+ });
10636
+ } catch (err) {
10637
+ const message = serializeError(err);
10638
+ if (deps.logger) deps.logger.error("[OutboundApi] direct relay error:", message);
10639
+ else console.error("[OutboundApi] direct relay error:", message);
10640
+ emitWebhookEvent({ kind: "server.error", at: Date.now(), message });
10641
+ if (audit) audit.error = message;
10642
+ if (!res.headersSent) writeJsonError(res, 502, message);
10643
+ else res.destroy();
10644
+ }
10645
+ }
10646
+ async function writeModelsList(res, llmConfig, config, apiKeyId, allowedEndpoints, imageModels = [], directTarget) {
10000
10647
  const modelIds = [];
10001
- for (const endpoint of ["chat", "responses", "messages", "gemini"]) {
10648
+ for (const endpoint of MODEL_LIST_ENDPOINTS) {
10002
10649
  if (allowedEndpoints && !allowedEndpoints.includes(endpoint)) continue;
10003
10650
  for (const binding of candidateGatewayBindings(config.bindings, apiKeyId, endpoint)) {
10004
10651
  if (binding.modelMode === "passthrough") {
10005
- if (binding.target.kind === "provider") {
10006
- const provider = await llmConfig.getProvider(binding.target.providerId);
10007
- modelIds.push(...provider?.models ?? []);
10008
- } else if (Object.prototype.hasOwnProperty.call(
10009
- SUBSCRIPTION_MODEL_CATALOG,
10010
- binding.target.providerId
10011
- )) {
10012
- modelIds.push(
10013
- ...SUBSCRIPTION_MODEL_CATALOG[binding.target.providerId]
10014
- );
10015
- }
10652
+ await pushTargetCatalog(llmConfig, binding.target, (id) => modelIds.push(id));
10016
10653
  continue;
10017
10654
  }
10018
10655
  if (binding.modelMappings?.length) {
10019
10656
  modelIds.push(
10020
10657
  ...binding.modelMappings.map((mapping) => mapping.source.trim()).filter((source) => source !== "" && !source.includes("*"))
10021
10658
  );
10659
+ if (hasWildcardMapping(binding)) {
10660
+ await pushTargetCatalog(llmConfig, binding.target, (id) => modelIds.push(id));
10661
+ for (const mapping of binding.modelMappings ?? []) {
10662
+ if (!mapping.source.includes("*") || mapping.target.trim() === "") continue;
10663
+ modelIds.push(parseModelRef(mapping.target)?.modelId ?? mapping.target.trim());
10664
+ }
10665
+ }
10022
10666
  continue;
10023
10667
  }
10024
10668
  const endpointConfig = gatewayBindingToEndpointConfig(binding);
@@ -10035,6 +10679,9 @@ async function writeModelsList(res, llmConfig, config, apiKeyId, allowedEndpoint
10035
10679
  );
10036
10680
  }
10037
10681
  }
10682
+ if (directTarget?.kind === "provider") {
10683
+ modelIds.push(...await passthroughProviderModelIds(llmConfig, directTarget.providerId));
10684
+ }
10038
10685
  modelIds.push(...imageModels);
10039
10686
  const seen = /* @__PURE__ */ new Set();
10040
10687
  const data = modelIds.filter((modelId) => {
@@ -10129,6 +10776,16 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10129
10776
  });
10130
10777
  return;
10131
10778
  }
10779
+ const directProvider = verified.boundUpstream?.kind === "provider" ? verified.boundUpstream : null;
10780
+ if (verified.boundUpstream && !directProvider) {
10781
+ config = {
10782
+ ...config,
10783
+ bindings: [
10784
+ ...config.bindings ?? [],
10785
+ directUpstreamBinding(verified.boundUpstream, verified.id, 1e3)
10786
+ ]
10787
+ };
10788
+ }
10132
10789
  if (isOauthUsagePath) {
10133
10790
  if (req.method !== "GET") {
10134
10791
  writeJsonError(res, 404, `Unsupported: ${req.method} ${req.url}`);
@@ -10192,7 +10849,8 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10192
10849
  config,
10193
10850
  verified.id,
10194
10851
  verified.allowedEndpoints,
10195
- parseModelsLimit(req.url)
10852
+ parseModelsLimit(req.url),
10853
+ verified.boundUpstream
10196
10854
  );
10197
10855
  } else {
10198
10856
  let imageModels = [];
@@ -10208,21 +10866,26 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10208
10866
  config,
10209
10867
  verified.id,
10210
10868
  verified.allowedEndpoints,
10211
- imageModels
10869
+ imageModels,
10870
+ verified.boundUpstream
10212
10871
  );
10213
10872
  }
10214
10873
  return;
10215
10874
  }
10216
10875
  const endpoint = selectEndpoint(req.method, req.url);
10217
10876
  if (!endpoint) {
10877
+ if (directProvider) {
10878
+ await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit);
10879
+ return;
10880
+ }
10218
10881
  writeJsonError(res, 404, `Unsupported: ${req.method} ${req.url}`);
10219
10882
  return;
10220
10883
  }
10221
- if (verified.allowedEndpoints && !verified.allowedEndpoints.includes(endpoint)) {
10884
+ if (verified.allowedEndpoints && !verified.boundUpstream && !verified.allowedEndpoints.includes(endpoint)) {
10222
10885
  writeJsonError(res, 403, "API key is not allowed to access this endpoint");
10223
10886
  return;
10224
10887
  }
10225
- if (candidateGatewayBindings(config.bindings, verified.id, endpoint).length === 0) {
10888
+ if (candidateGatewayBindings(config.bindings, verified.id, endpoint).length === 0 && !directProvider) {
10226
10889
  writeJsonError(res, 503, `endpoint '${endpoint}' has no downstream route for this key`);
10227
10890
  return;
10228
10891
  }
@@ -10316,22 +10979,31 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10316
10979
  role
10317
10980
  });
10318
10981
  if (bindingResolution.source === "none") {
10982
+ if (directProvider) {
10983
+ await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, rawBody);
10984
+ return;
10985
+ }
10319
10986
  writeJsonError(res, 503, `endpoint '${endpoint}' has no downstream route for this key`);
10320
10987
  return;
10321
10988
  }
10322
10989
  const effectiveEndpointConfig = bindingResolution.config;
10323
10990
  const bindingAffinitySuffix = `:${bindingResolution.binding.id}`;
10324
10991
  const sessionId = deps.proxyDeps.apiKeyPool ? `outbound:${verified.id}${bindingAffinitySuffix}` : null;
10992
+ let replayBody = rawBody;
10993
+ if (!isCountTokens && effectiveEndpointConfig.reasoningEffort && injectMappingEffortDefault(endpoint, effectiveEndpointConfig.reasoningEffort, parsedBody)) {
10994
+ replayBody = JSON.stringify(parsedBody);
10995
+ }
10325
10996
  const hostedImageGenerationAllowed = verified.allowedEndpoints.includes("images");
10997
+ const listResolvedModel = endpoint === "chat" && bindingResolution.binding.modelMode === "mapped" ? resolveGatewayModelMappingRow(bindingResolution.binding.modelMappings, requestedModel)?.target ?? requestedModel : requestedModel;
10326
10998
  const resolved = isKindMappedEndpoint(endpoint) || endpoint === "chat" ? await resolveRoute({
10327
10999
  config: effectiveEndpointConfig,
10328
11000
  ingressFormat,
10329
11001
  llmConfig: deps.llmConfig,
10330
11002
  sessionId,
10331
- // Capture the ORIGINAL requested id BEFORE any downstream swap; for
10332
- // kind-mapped endpoints it selects the kind AND is stamped onto
10333
- // `route.requestedModel`; for chat it is matched against the list.
10334
- requestedModel,
11003
+ // The id the route resolves against: the client's ORIGINAL id for
11004
+ // kind-mapped endpoints (it selects the kind AND is stamped onto
11005
+ // `route.requestedModel`), the mapping-resolved target for chat.
11006
+ requestedModel: listResolvedModel,
10335
11007
  // Attribution: stamp the verified named-key id onto the route.
10336
11008
  apiKeyId: verified.id,
10337
11009
  hostedImageGenerationAllowed
@@ -10346,6 +11018,10 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10346
11018
  hostedImageGenerationAllowed
10347
11019
  });
10348
11020
  if (!resolved.ok) {
11021
+ if (directProvider) {
11022
+ await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, rawBody);
11023
+ return;
11024
+ }
10349
11025
  writeJsonError(res, resolved.error.status, resolved.error.message);
10350
11026
  return;
10351
11027
  }
@@ -10407,7 +11083,7 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10407
11083
  );
10408
11084
  try {
10409
11085
  shimAuthHeader(req, token);
10410
- const replay = makeReplayRequest(req, rawBody);
11086
+ const replay = makeReplayRequest(req, replayBody);
10411
11087
  await routeRequest(replay, res, routeMap, deps.proxyDeps);
10412
11088
  } catch (err) {
10413
11089
  if (isBoundAccountSelectionError(err)) {
@@ -13105,9 +13781,13 @@ export {
13105
13781
  projectUpstreamResponseHeaders,
13106
13782
  relayResponse,
13107
13783
  aggregateAnthropicSseToJsonBody,
13784
+ aggregateResponsesSseToJsonBody,
13785
+ aggregateOpenAIChatChunksToJsonBody,
13786
+ sniffIsSseResponse,
13108
13787
  writeError,
13109
13788
  writeBoundAccountError,
13110
13789
  resolvePoolBoundKey,
13790
+ buildByoRouteActivityMeta,
13111
13791
  DEFAULT_ROUTE_IDLE_MS,
13112
13792
  ProviderProxyRouteMap,
13113
13793
  previousResponseNotFound,
@@ -13187,6 +13867,7 @@ export {
13187
13867
  loadServerConfig,
13188
13868
  saveServerConfig,
13189
13869
  mergeServerConfig,
13870
+ injectMappingEffortDefault,
13190
13871
  computeKeyExpiry,
13191
13872
  checkKeyQuota,
13192
13873
  randomBase62,