@omnicross/core 0.4.3 → 0.4.5

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 (81) hide show
  1. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +4 -4
  2. package/dist/auth/GeminiCodeAssistProjectResolver.js +3 -3
  3. package/dist/{chunk-MPFOC7AT.js → chunk-24W3DVGI.js} +1 -1
  4. package/dist/{chunk-WYECWQ3J.cjs → chunk-2DMOOZ4F.cjs} +1 -1
  5. package/dist/{chunk-NKVZ6Z3Y.cjs → chunk-4HMAS74X.cjs} +8 -3
  6. package/dist/{chunk-M6PKXP6T.js → chunk-4LSZ7NFM.js} +14 -2
  7. package/dist/{chunk-VFFCV4J6.cjs → chunk-CXRJMNJJ.cjs} +12 -6
  8. package/dist/{chunk-E5RCCGE7.cjs → chunk-DF5JXK36.cjs} +2 -2
  9. package/dist/{chunk-CGEZQC2G.cjs → chunk-F3TT5XPV.cjs} +2 -2
  10. package/dist/{chunk-EE7VP4O3.js → chunk-HQ7C3VB6.js} +1 -1
  11. package/dist/{chunk-ZJJBITUN.js → chunk-IAWLTZUP.js} +2 -2
  12. package/dist/{chunk-SWY2LTMR.js → chunk-L4L77G3X.js} +574 -100
  13. package/dist/{chunk-NTF7TLT7.js → chunk-LBIXTHP7.js} +11 -5
  14. package/dist/{chunk-UOOLU64V.cjs → chunk-LVQOCIES.cjs} +2 -2
  15. package/dist/{chunk-J5XMB5KY.cjs → chunk-NCCEWVWY.cjs} +978 -504
  16. package/dist/{chunk-K6J2RIEX.js → chunk-NDJSLJIA.js} +1 -1
  17. package/dist/{chunk-AFH5CEMF.cjs → chunk-SQU2FGXI.cjs} +15 -3
  18. package/dist/{chunk-F6P4DIBL.js → chunk-TLYFFUW4.js} +1 -1
  19. package/dist/{chunk-GL6XM527.js → chunk-TX7ZM4OI.js} +8 -3
  20. package/dist/{chunk-HRBXQ5AC.js → chunk-WYUXJKMK.js} +1 -1
  21. package/dist/{chunk-NDIBVTBZ.cjs → chunk-XYFCNDJK.cjs} +2 -2
  22. package/dist/{chunk-X6GHTNDL.cjs → chunk-ZG57GAVU.cjs} +7 -7
  23. package/dist/completion/CompletionService.cjs +11 -11
  24. package/dist/completion/CompletionService.js +10 -10
  25. package/dist/completion.cjs +11 -11
  26. package/dist/completion.js +10 -10
  27. package/dist/index.cjs +13 -11
  28. package/dist/index.d.cts +3 -3
  29. package/dist/index.d.ts +3 -3
  30. package/dist/index.js +12 -10
  31. package/dist/outbound-api/routeResolver.d.cts +2 -2
  32. package/dist/outbound-api/routeResolver.d.ts +2 -2
  33. package/dist/outbound-api/types.d.cts +2 -2
  34. package/dist/outbound-api/types.d.ts +2 -2
  35. package/dist/outbound-api.cjs +13 -11
  36. package/dist/outbound-api.d.cts +46 -5
  37. package/dist/outbound-api.d.ts +46 -5
  38. package/dist/outbound-api.js +12 -10
  39. package/dist/pipeline/AccountAllowanceScheduling.d.cts +1 -1
  40. package/dist/pipeline/AccountAllowanceScheduling.d.ts +1 -1
  41. package/dist/pipeline/AccountRouteActivity.cjs +2 -2
  42. package/dist/pipeline/AccountRouteActivity.d.cts +33 -8
  43. package/dist/pipeline/AccountRouteActivity.d.ts +33 -8
  44. package/dist/pipeline/AccountRouteActivity.js +1 -1
  45. package/dist/pipeline/upstreamFetch.cjs +3 -3
  46. package/dist/pipeline/upstreamFetch.d.cts +15 -1
  47. package/dist/pipeline/upstreamFetch.d.ts +15 -1
  48. package/dist/pipeline/upstreamFetch.js +2 -2
  49. package/dist/ports.d.cts +1 -1
  50. package/dist/ports.d.ts +1 -1
  51. package/dist/provider-proxy/ProviderProxy.cjs +11 -11
  52. package/dist/provider-proxy/ProviderProxy.js +10 -10
  53. package/dist/provider-proxy/identity/codexCliHeaders.cjs +6 -2
  54. package/dist/provider-proxy/identity/codexCliHeaders.d.cts +42 -11
  55. package/dist/provider-proxy/identity/codexCliHeaders.d.ts +42 -11
  56. package/dist/provider-proxy/identity/codexCliHeaders.js +6 -2
  57. package/dist/provider-proxy/identity/openCodeGoHeaders.cjs +2 -2
  58. package/dist/provider-proxy/identity/openCodeGoHeaders.js +1 -1
  59. package/dist/provider-proxy/ingress/providerProxyShared.cjs +13 -11
  60. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +19 -1
  61. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +19 -1
  62. package/dist/provider-proxy/ingress/providerProxyShared.js +12 -10
  63. package/dist/provider-proxy.cjs +11 -11
  64. package/dist/provider-proxy.d.cts +1 -1
  65. package/dist/provider-proxy.d.ts +1 -1
  66. package/dist/provider-proxy.js +10 -10
  67. package/dist/{routeResolver-C2z0pp-q.d.cts → routeResolver-BdNJvTZP.d.cts} +1 -1
  68. package/dist/{routeResolver-BLTleO4H.d.ts → routeResolver-zVoCx9r3.d.ts} +1 -1
  69. package/dist/transformer/transformers/AntigravityTransformer.cjs +6 -6
  70. package/dist/transformer/transformers/AntigravityTransformer.js +5 -5
  71. package/dist/transformer/transformers/antigravityFailover.cjs +7 -7
  72. package/dist/transformer/transformers/antigravityFailover.js +6 -6
  73. package/dist/transformer/transformers/antigravityIdentity.cjs +4 -4
  74. package/dist/transformer/transformers/antigravityIdentity.js +3 -3
  75. package/dist/transformer/transformers.cjs +8 -8
  76. package/dist/transformer/transformers.js +7 -7
  77. package/dist/transformer.cjs +8 -8
  78. package/dist/transformer.js +7 -7
  79. package/dist/{types-C6m-Db26.d.cts → types-Dt1bgHPg.d.cts} +63 -2
  80. package/dist/{types-Da735j2d.d.ts → types-IRPv4i1Y.d.ts} +63 -2
  81. 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";
@@ -40,10 +40,10 @@ import {
40
40
  streamSSEResponse
41
41
  } from "./chunk-VVEHS2LI.js";
42
42
  import {
43
- DEFAULT_CODEX_CLI_HEADERS,
44
43
  codexAcceptHeader,
45
- extractCodexClientHeaders
46
- } from "./chunk-M6PKXP6T.js";
44
+ extractCodexClientHeaders,
45
+ fillMissingCodexCliIdentity
46
+ } from "./chunk-4LSZ7NFM.js";
47
47
  import {
48
48
  getSharedIdentityStore
49
49
  } from "./chunk-XTEI64OU.js";
@@ -53,7 +53,7 @@ import {
53
53
  } from "./chunk-XX6NQJMA.js";
54
54
  import {
55
55
  extractOpenCodeSessionHeader
56
- } from "./chunk-HRBXQ5AC.js";
56
+ } from "./chunk-WYUXJKMK.js";
57
57
  import {
58
58
  NEVER_FORWARD_HEADERS,
59
59
  fillMissingHeaders,
@@ -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,
@@ -953,6 +954,21 @@ async function resolvePoolBoundKey(deps, providerId, provider, sessionId, prefer
953
954
  }
954
955
  return resolveApiKey(resolveProviderEndpoint(provider).apiKey);
955
956
  }
957
+ function buildByoRouteActivityMeta(deps, providerId, sessionId) {
958
+ return {
959
+ providerId,
960
+ resolveKeyId: () => {
961
+ if (!sessionId) return void 0;
962
+ const pool = deps.apiKeyPool;
963
+ if (!pool || typeof pool.getKeyIdForSession !== "function") return void 0;
964
+ try {
965
+ return pool.getKeyIdForSession(providerId, sessionId) ?? void 0;
966
+ } catch {
967
+ return void 0;
968
+ }
969
+ }
970
+ };
971
+ }
956
972
 
957
973
  // src/provider-proxy/providerProxyRouteMap.ts
958
974
  import { randomBytes } from "crypto";
@@ -1666,6 +1682,31 @@ function collectText(value) {
1666
1682
  function proxyProviderId(plan) {
1667
1683
  return plan.isSubscription ? plan.transformerProvider.name : "byo";
1668
1684
  }
1685
+ function buildAnthropicRouteActivity(plan, proxyAccountId, model, onRecorded) {
1686
+ if (!plan.isSubscription) {
1687
+ const routeSessionId = plan.byoSessionId || void 0;
1688
+ return {
1689
+ providerId: plan.byoActivity?.providerId ?? plan.transformerProvider.name,
1690
+ credentialKind: "provider-key",
1691
+ keyId: plan.byoActivity?.resolveKeyId(),
1692
+ endpoint: "messages",
1693
+ sessionKey: routeSessionId,
1694
+ sessionSource: routeSessionId ? "route-session-id" : "none",
1695
+ model,
1696
+ onRecorded
1697
+ };
1698
+ }
1699
+ return {
1700
+ providerId: proxyProviderId(plan),
1701
+ credentialKind: "subscription-account",
1702
+ accountId: proxyAccountId,
1703
+ endpoint: "messages",
1704
+ sessionKey: plan.sessionKey,
1705
+ sessionSource: plan.sessionKey ? "content-fingerprint" : "none",
1706
+ model,
1707
+ onRecorded
1708
+ };
1709
+ }
1669
1710
  async function runPipeline(anthropicBody, plan, reportSelection, signal, callerOpenCodeSession) {
1670
1711
  const executor = getSharedExecutor();
1671
1712
  const endpointTransformer = getAnthropicEndpointTransformer();
@@ -1715,15 +1756,14 @@ async function runPipeline(anthropicBody, plan, reportSelection, signal, callerO
1715
1756
  {
1716
1757
  providerId: proxyProviderId(plan),
1717
1758
  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) => {
1759
+ routeActivity: buildAnthropicRouteActivity(
1760
+ plan,
1761
+ proxyAccountId,
1762
+ resolvedModel,
1763
+ (record5) => {
1724
1764
  activityRecordId = record5.id;
1725
1765
  }
1726
- } : void 0
1766
+ )
1727
1767
  }
1728
1768
  ).then((r) => {
1729
1769
  rawStatus = r.status;
@@ -1786,15 +1826,14 @@ async function runSubscriptionSameFormatFetch(rawBody, plan, reportSelection, op
1786
1826
  {
1787
1827
  providerId: proxyProviderId(plan),
1788
1828
  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) => {
1829
+ routeActivity: buildAnthropicRouteActivity(
1830
+ plan,
1831
+ proxyAccountId,
1832
+ outboundModel,
1833
+ (record5) => {
1795
1834
  activityRecordId = record5.id;
1796
1835
  }
1797
- } : void 0
1836
+ )
1798
1837
  }
1799
1838
  );
1800
1839
  return {
@@ -2818,7 +2857,7 @@ async function resolveResponsesRouteProfile(route, deps, resolvedModel) {
2818
2857
  }
2819
2858
  async function buildResponsesCallPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, requestHeaders, affinity) {
2820
2859
  if (route.authMode === "byo") {
2821
- return buildByoPlan(route, deps, resolved, resolvedModel, isStream, affinity);
2860
+ return buildByoPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, affinity);
2822
2861
  }
2823
2862
  return buildSubscriptionPlan2(
2824
2863
  route,
@@ -2832,7 +2871,7 @@ async function buildResponsesCallPlan(route, deps, resolved, resolvedModel, isSt
2832
2871
  affinity
2833
2872
  );
2834
2873
  }
2835
- async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, affinity) {
2874
+ async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, affinity) {
2836
2875
  const providerId = route.providerId;
2837
2876
  const provider = resolved.provider;
2838
2877
  const affinityKeyId = affinity?.credential.kind === "byo-key" ? affinity.credential.id : void 0;
@@ -2883,7 +2922,11 @@ async function buildByoPlan(route, deps, resolved, resolvedModel, isStream, affi
2883
2922
  resolveCredential: () => {
2884
2923
  const keyId = route.sessionId && typeof deps.apiKeyPool?.getKeyIdForSession === "function" ? deps.apiKeyPool.getKeyIdForSession(providerId, route.sessionId) : null;
2885
2924
  return keyId ? { kind: "byo-key", id: keyId } : credential;
2886
- }
2925
+ },
2926
+ byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
2927
+ byoSessionId: route.sessionId ?? null,
2928
+ sessionKey,
2929
+ sessionSource
2887
2930
  };
2888
2931
  }
2889
2932
  async function buildSubscriptionPlan2(route, deps, resolved, resolvedModel, isStream, sessionKey, sessionSource, requestHeaders, affinity) {
@@ -2985,6 +3028,32 @@ async function executeResponsesUpstream(body, plan, operation, signal) {
2985
3028
  }
2986
3029
  return result;
2987
3030
  }
3031
+ function buildResponsesRouteActivity(plan, accountId, actualModel, onRecorded) {
3032
+ if (plan.proxyProviderId === "byo") {
3033
+ const contentKey = plan.sessionKey?.trim() || void 0;
3034
+ const routeSessionId = plan.byoSessionId || void 0;
3035
+ return {
3036
+ providerId: plan.byoActivity?.providerId ?? plan.providerIdentity,
3037
+ credentialKind: "provider-key",
3038
+ keyId: plan.byoActivity?.resolveKeyId(),
3039
+ endpoint: "responses",
3040
+ sessionKey: contentKey ?? routeSessionId,
3041
+ sessionSource: contentKey ? plan.sessionSource ?? "none" : routeSessionId ? "route-session-id" : "none",
3042
+ model: actualModel,
3043
+ onRecorded
3044
+ };
3045
+ }
3046
+ return {
3047
+ providerId: plan.proxyProviderId,
3048
+ credentialKind: "subscription-account",
3049
+ accountId,
3050
+ endpoint: "responses",
3051
+ sessionKey: plan.sessionKey,
3052
+ sessionSource: plan.sessionSource ?? "none",
3053
+ model: actualModel,
3054
+ onRecorded
3055
+ };
3056
+ }
2988
3057
  async function applyPlanAuth(body, plan) {
2989
3058
  let accountId;
2990
3059
  let actualModel = plan.resolvedModel;
@@ -3004,13 +3073,14 @@ async function applyPlanAuth(body, plan) {
3004
3073
  });
3005
3074
  body.model = actualModel;
3006
3075
  plan.transformerProvider.models = [actualModel];
3076
+ if (plan.proxyProviderId === "codex") body.store = false;
3007
3077
  return { headers, accountId, actualModel };
3008
3078
  }
3009
3079
  function decorateCodexHeaders(headers, plan) {
3010
3080
  if (plan.proxyProviderId !== "codex") return;
3011
3081
  fillMissingHeaders(headers, plan.callerClientHeaders ?? {});
3012
- fillMissingHeaders(headers, DEFAULT_CODEX_CLI_HEADERS);
3013
3082
  fillMissingHeaders(headers, { accept: codexAcceptHeader(plan.isStream) });
3083
+ fillMissingCodexCliIdentity(headers);
3014
3084
  }
3015
3085
  async function runNative(body, plan, operation, signal) {
3016
3086
  throwIfResponsesAborted(signal);
@@ -3025,15 +3095,9 @@ async function runNative(body, plan, operation, signal) {
3025
3095
  {
3026
3096
  providerId: plan.proxyProviderId,
3027
3097
  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
- }
3098
+ routeActivity: buildResponsesRouteActivity(plan, accountId, actualModel, (record5) => {
3099
+ activityRecordId = record5.id;
3100
+ })
3037
3101
  }
3038
3102
  );
3039
3103
  return {
@@ -3078,15 +3142,9 @@ async function runReduced(body, plan, signal) {
3078
3142
  {
3079
3143
  providerId: plan.proxyProviderId,
3080
3144
  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
- }
3145
+ routeActivity: buildResponsesRouteActivity(plan, accountId, actualModel, (record5) => {
3146
+ activityRecordId = record5.id;
3147
+ })
3090
3148
  }
3091
3149
  );
3092
3150
  const upstream = plan.proxyProviderId === "antigravity" ? await fetchWithAntigravityFailover(url, send, signal) : await send(url);
@@ -4329,7 +4387,9 @@ async function buildByoPlan2(res, route, deps, resolvedModel, isStream) {
4329
4387
  isSubscription: false,
4330
4388
  provider,
4331
4389
  apiKey,
4332
- extendedContextEnabled: route.anthropicSdkHints?.extendedContext?.enabled ?? false
4390
+ extendedContextEnabled: route.anthropicSdkHints?.extendedContext?.enabled ?? false,
4391
+ byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
4392
+ byoSessionId: route.sessionId ?? null
4333
4393
  };
4334
4394
  }
4335
4395
  async function runSameFormatFetch(bodyToSend, plan, options, keyOverride, urlOverride) {
@@ -4355,12 +4415,30 @@ async function runSameFormatFetch(bodyToSend, plan, options, keyOverride, urlOve
4355
4415
  headers["anthropic-version"] = callerVersion || DEFAULT_ANTHROPIC_VERSION;
4356
4416
  const url = urlOverride ?? buildProviderApiUrl(provider, { model: resolvedModel, stream: isStream });
4357
4417
  console.info(`[ProviderProxy:anthropic] (same-format) -> ${url} model=${resolvedModel} stream=${isStream}`);
4418
+ let activityRecordId;
4358
4419
  const response = await fetchUpstream(
4359
4420
  url,
4360
4421
  { method: "POST", headers, body: bodyToSend, signal: options.signal },
4361
- { providerId: "byo" }
4422
+ {
4423
+ providerId: "byo",
4424
+ routeActivity: {
4425
+ providerId: plan.byoActivity?.providerId ?? provider.name,
4426
+ credentialKind: "provider-key",
4427
+ // LIVE resolution: a pool rebind retry re-invokes this fetch, and the
4428
+ // binding has already rotated, so the retried attempt attributes the
4429
+ // NEW key. `keyOverride` is a key STRING (never recorded).
4430
+ keyId: plan.byoActivity?.resolveKeyId(),
4431
+ endpoint: "messages",
4432
+ sessionKey: plan.byoSessionId || void 0,
4433
+ sessionSource: plan.byoSessionId ? "route-session-id" : "none",
4434
+ model: resolvedModel,
4435
+ onRecorded: (record5) => {
4436
+ activityRecordId = record5.id;
4437
+ }
4438
+ }
4439
+ }
4362
4440
  );
4363
- return { response, rawStatus: response.status };
4441
+ return { response, rawStatus: response.status, activityRecordId };
4364
4442
  }
4365
4443
  function resolveSameFormatBody(anthropicBody, rawBody, plan) {
4366
4444
  let originalModel;
@@ -4903,7 +4981,9 @@ async function buildByoPlan3(res, route, deps, resolvedModel, isStream) {
4903
4981
  resolvedModel,
4904
4982
  isStream,
4905
4983
  resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : byoUrl,
4906
- upstreamUrl: byoUrl
4984
+ upstreamUrl: byoUrl,
4985
+ byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
4986
+ byoSessionId: route.sessionId ?? null
4907
4987
  };
4908
4988
  }
4909
4989
  async function runPipeline2(geminiBody, plan) {
@@ -4934,7 +5014,18 @@ async function runPipeline2(geminiBody, plan) {
4934
5014
  return fetchUpstream(
4935
5015
  url,
4936
5016
  { method: "POST", headers, body: JSON.stringify(body) },
4937
- { providerId: "byo" }
5017
+ {
5018
+ providerId: "byo",
5019
+ routeActivity: {
5020
+ providerId: plan.byoActivity?.providerId ?? transformerProvider.name,
5021
+ credentialKind: "provider-key",
5022
+ keyId: plan.byoActivity?.resolveKeyId(),
5023
+ endpoint: "generateContent",
5024
+ sessionKey: plan.byoSessionId || void 0,
5025
+ sessionSource: plan.byoSessionId ? "route-session-id" : "none",
5026
+ model: resolvedModel
5027
+ }
5028
+ }
4938
5029
  ).then((r) => {
4939
5030
  rawStatus = r.status;
4940
5031
  return r;
@@ -5118,7 +5209,9 @@ async function buildByoPlan4(res, route, deps, resolvedModel, isStream) {
5118
5209
  resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : byoUrl,
5119
5210
  upstreamUrl: byoUrl,
5120
5211
  proxyProviderId: "byo",
5121
- isSubscription: false
5212
+ isSubscription: false,
5213
+ byoActivity: buildByoRouteActivityMeta(deps, providerId, route.sessionId),
5214
+ routeSessionId: route.sessionId ?? null
5122
5215
  };
5123
5216
  }
5124
5217
  async function buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream) {
@@ -5165,7 +5258,8 @@ async function buildSubscriptionPlan3(res, route, deps, resolvedModel, isStream)
5165
5258
  resolveUrl: (config) => config.url instanceof URL ? config.url.toString() : typeof config.url === "string" ? config.url : upstreamUrl,
5166
5259
  upstreamUrl,
5167
5260
  proxyProviderId: providerId,
5168
- isSubscription: true
5261
+ isSubscription: true,
5262
+ routeSessionId: route.sessionId ?? null
5169
5263
  };
5170
5264
  }
5171
5265
  async function runPipeline3(chatBody, plan) {
@@ -5201,10 +5295,31 @@ async function runPipeline3(chatBody, plan) {
5201
5295
  },
5202
5296
  fetchFn: (url, headers, body) => {
5203
5297
  console.log(`[ProviderProxy:chat] -> ${url} model=${resolvedModel} stream=${isStream}`);
5298
+ const sessionKey = plan.routeSessionId || void 0;
5204
5299
  return fetchUpstream(
5205
5300
  url,
5206
5301
  { method: "POST", headers, body: JSON.stringify(body) },
5207
- { providerId: plan.proxyProviderId, accountId: proxyAccountId }
5302
+ {
5303
+ providerId: plan.proxyProviderId,
5304
+ accountId: proxyAccountId,
5305
+ routeActivity: plan.isSubscription ? {
5306
+ providerId: plan.proxyProviderId,
5307
+ credentialKind: "subscription-account",
5308
+ accountId: proxyAccountId,
5309
+ endpoint: "chat",
5310
+ sessionKey,
5311
+ sessionSource: sessionKey ? "route-session-id" : "none",
5312
+ model: resolvedModel
5313
+ } : {
5314
+ providerId: plan.byoActivity?.providerId ?? transformerProvider.name,
5315
+ credentialKind: "provider-key",
5316
+ keyId: plan.byoActivity?.resolveKeyId(),
5317
+ endpoint: "chat",
5318
+ sessionKey,
5319
+ sessionSource: sessionKey ? "route-session-id" : "none",
5320
+ model: resolvedModel
5321
+ }
5322
+ }
5208
5323
  ).then((r) => {
5209
5324
  rawStatus = r.status;
5210
5325
  return r;
@@ -5983,17 +6098,22 @@ function wildcardMatches(pattern, value) {
5983
6098
  const escaped = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join(".*");
5984
6099
  return new RegExp(`^${escaped}$`, "iu").test(value);
5985
6100
  }
5986
- function resolveGatewayModelMapping(mappings, requestedModel) {
6101
+ function resolveGatewayModelMappingRow(mappings, requestedModel) {
5987
6102
  if (!nonBlank(requestedModel)) return void 0;
5988
6103
  const wanted = requestedModel.trim();
5989
6104
  const exact = (mappings ?? []).find(
5990
6105
  (mapping) => nonBlank(mapping.source) && !mapping.source.includes("*") && mapping.source.trim().toLocaleLowerCase() === wanted.toLocaleLowerCase()
5991
6106
  );
5992
- if (exact && nonBlank(exact.target)) return exact.target.trim();
6107
+ if (exact && nonBlank(exact.target)) {
6108
+ return { target: exact.target.trim(), effort: exact.effort };
6109
+ }
5993
6110
  const wildcard = (mappings ?? []).find(
5994
6111
  (mapping) => nonBlank(mapping.source) && mapping.source.includes("*") && wildcardMatches(mapping.source.trim(), wanted)
5995
6112
  );
5996
- return wildcard && nonBlank(wildcard.target) ? wildcard.target.trim() : void 0;
6113
+ return wildcard && nonBlank(wildcard.target) ? { target: wildcard.target.trim(), effort: wildcard.effort } : void 0;
6114
+ }
6115
+ function resolveGatewayModelMapping(mappings, requestedModel) {
6116
+ return resolveGatewayModelMappingRow(mappings, requestedModel)?.target;
5997
6117
  }
5998
6118
  function routeCanServe(binding, requestedModel, role) {
5999
6119
  if (binding.modelMode === "passthrough") return nonBlank(requestedModel);
@@ -6067,9 +6187,11 @@ function gatewayBindingToEndpointConfig(binding, requestedModel) {
6067
6187
  useSubscription: target.kind !== "provider"
6068
6188
  };
6069
6189
  const usesGenericModelHandling = binding.modelMode === "passthrough" || Boolean(binding.modelMappings?.length);
6070
- const dynamicModel = binding.modelMode === "passthrough" ? requestedModel : resolveGatewayModelMapping(binding.modelMappings, requestedModel);
6190
+ const winningMapping = binding.modelMode === "passthrough" ? void 0 : resolveGatewayModelMappingRow(binding.modelMappings, requestedModel);
6191
+ const dynamicModel = binding.modelMode === "passthrough" ? requestedModel : winningMapping?.target;
6071
6192
  if (usesGenericModelHandling) {
6072
6193
  if (nonBlank(dynamicModel)) applySingleModel(config, binding, dynamicModel);
6194
+ if (winningMapping?.effort) config.reasoningEffort = winningMapping.effort;
6073
6195
  } else if (isKindMappedEndpoint(binding.endpoint)) {
6074
6196
  config.modelMap = Object.fromEntries(
6075
6197
  Object.entries(binding.modelMap ?? {}).map(([kind, ref]) => [kind, targetRef(target, ref)])
@@ -8494,6 +8616,7 @@ function normalizeBindingTarget(raw) {
8494
8616
  }
8495
8617
  return null;
8496
8618
  }
8619
+ var MAPPING_EFFORT_MAX_LENGTH = 32;
8497
8620
  function normalizeGatewayModelMappings(raw) {
8498
8621
  if (!Array.isArray(raw)) return [];
8499
8622
  const mappings = [];
@@ -8503,7 +8626,9 @@ function normalizeGatewayModelMappings(raw) {
8503
8626
  const source = typeof value.source === "string" ? value.source.trim() : "";
8504
8627
  const target = typeof value.target === "string" ? value.target.trim() : "";
8505
8628
  if (!source || !target) continue;
8506
- mappings.push({ source, target });
8629
+ const rawEffort = typeof value.effort === "string" ? value.effort.trim() : "";
8630
+ const effort = rawEffort && rawEffort.length <= MAPPING_EFFORT_MAX_LENGTH ? rawEffort : void 0;
8631
+ mappings.push(effort ? { source, target, effort } : { source, target });
8507
8632
  }
8508
8633
  return mappings;
8509
8634
  }
@@ -8795,6 +8920,61 @@ function deriveAuditSessionKey(body, headers = {}, options = {}) {
8795
8920
  return gateway.key;
8796
8921
  }
8797
8922
 
8923
+ // src/outbound-api/mappingEffortInjection.ts
8924
+ function isPlainObject(value) {
8925
+ return !!value && typeof value === "object" && !Array.isArray(value);
8926
+ }
8927
+ function hasOpenAiWireReasoningIntent(body) {
8928
+ return extractReasoningIntent({
8929
+ model: typeof body["model"] === "string" ? body["model"] : "",
8930
+ reasoning: isPlainObject(body["reasoning"]) ? body["reasoning"] : void 0,
8931
+ reasoning_effort: body["reasoning_effort"]
8932
+ }) !== void 0;
8933
+ }
8934
+ function injectMappingEffortDefault(endpoint, effort, body) {
8935
+ if (!effort) return false;
8936
+ switch (endpoint) {
8937
+ case "chat":
8938
+ if (hasOpenAiWireReasoningIntent(body)) {
8939
+ return false;
8940
+ }
8941
+ body["reasoning_effort"] = effort;
8942
+ return true;
8943
+ case "responses": {
8944
+ if (hasOpenAiWireReasoningIntent(body)) {
8945
+ return false;
8946
+ }
8947
+ const reasoning = body["reasoning"];
8948
+ body["reasoning"] = {
8949
+ ...isPlainObject(reasoning) ? reasoning : {},
8950
+ effort
8951
+ };
8952
+ return true;
8953
+ }
8954
+ case "messages": {
8955
+ if (body["thinking"] !== void 0) return false;
8956
+ if (effort === "none") return false;
8957
+ body["thinking"] = { type: "adaptive" };
8958
+ const outputConfig = body["output_config"];
8959
+ body["output_config"] = {
8960
+ ...isPlainObject(outputConfig) ? outputConfig : {},
8961
+ effort
8962
+ };
8963
+ return true;
8964
+ }
8965
+ case "gemini": {
8966
+ const generationConfig = body["generationConfig"];
8967
+ const config = isPlainObject(generationConfig) ? generationConfig : {};
8968
+ if (config["thinkingConfig"] !== void 0) return false;
8969
+ body["generationConfig"] = {
8970
+ ...config,
8971
+ thinkingConfig: { thinkingLevel: effort }
8972
+ };
8973
+ return true;
8974
+ }
8975
+ }
8976
+ }
8977
+
8798
8978
  // src/outbound-api/outboundApiKeyAuth.ts
8799
8979
  import { createHash as createHash3, randomBytes as randomBytes4 } from "crypto";
8800
8980
 
@@ -8981,8 +9161,23 @@ function toVerifiedKey(row) {
8981
9161
  if (rateLimit) key.rateLimit = rateLimit;
8982
9162
  const modelRestriction = extractModelRestriction(row);
8983
9163
  if (modelRestriction) key.modelRestriction = modelRestriction;
9164
+ const directTarget = extractBoundUpstream(row);
9165
+ if (directTarget) key.boundUpstream = directTarget;
8984
9166
  return key;
8985
9167
  }
9168
+ function extractBoundUpstream(row) {
9169
+ if (row.boundUpstream && typeof row.boundUpstream === "object") {
9170
+ const target = row.boundUpstream;
9171
+ 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) {
9172
+ return target;
9173
+ }
9174
+ return void 0;
9175
+ }
9176
+ if (typeof row.boundUpstreamProviderId === "string" && row.boundUpstreamProviderId) {
9177
+ return { kind: "provider", providerId: row.boundUpstreamProviderId };
9178
+ }
9179
+ return void 0;
9180
+ }
8986
9181
  async function verifyKey(db, presentedKey, now = Date.now()) {
8987
9182
  if (!presentedKey) return { status: "invalid" };
8988
9183
  const trimmed = presentedKey.trim();
@@ -9908,7 +10103,102 @@ function resolveModelsShape(modelsShape, allowedEndpoints) {
9908
10103
  if (!allowedEndpoints || allowedEndpoints.length === 0) return "anthropic";
9909
10104
  return allowedEndpoints.includes("messages") ? "anthropic" : "openai";
9910
10105
  }
10106
+ var UPSTREAM_MODELS_TTL_MS = 6e4;
10107
+ var UPSTREAM_MODELS_NEGATIVE_TTL_MS = 5e3;
10108
+ var UPSTREAM_MODELS_TIMEOUT_MS = 1e4;
10109
+ var upstreamModelsCache = /* @__PURE__ */ new Map();
10110
+ var upstreamModelsInFlight = /* @__PURE__ */ new Map();
10111
+ function resolveProviderKeyLiteral(apiKey) {
10112
+ if (!apiKey) return "";
10113
+ if (apiKey.startsWith("$")) return process.env[apiKey.slice(1)] || "";
10114
+ return apiKey;
10115
+ }
10116
+ function buildProviderModelsUrl(baseUrl, format) {
10117
+ const base = baseUrl.trim().replace(/\/+$/, "");
10118
+ if (!base) return null;
10119
+ if (format === "google") {
10120
+ return /\/models$/.test(base) ? base : `${base}/models`;
10121
+ }
10122
+ if (format === "anthropic") {
10123
+ if (base.endsWith("/messages")) return `${base.slice(0, -"/messages".length)}/models`;
10124
+ if (/\/v\d+$/.test(base)) return `${base}/models`;
10125
+ return `${base}/v1/models`;
10126
+ }
10127
+ const completionSuffix = format === "openai-response" ? "/responses" : "/chat/completions";
10128
+ const root = base.endsWith(completionSuffix) ? base.slice(0, -completionSuffix.length) : base;
10129
+ if (root !== base || /\/v\d+$/.test(base)) return `${root}/models`;
10130
+ return `${base}/v1/models`;
10131
+ }
10132
+ async function fetchUpstreamModelsUncached(provider) {
10133
+ const format = resolveApiFormat(provider);
10134
+ const url = buildProviderModelsUrl(provider.api_base_url ?? "", format);
10135
+ if (!url) return [];
10136
+ const headers = getProviderHeaders(provider, resolveProviderKeyLiteral(provider.api_key));
10137
+ delete headers["Content-Type"];
10138
+ headers["Accept"] = "application/json";
10139
+ const response = await fetchUpstream(
10140
+ url,
10141
+ { method: "GET", headers, signal: AbortSignal.timeout(UPSTREAM_MODELS_TIMEOUT_MS) },
10142
+ { providerId: "byo" }
10143
+ );
10144
+ if (!response.ok) return [];
10145
+ const body = await response.json();
10146
+ const fromData = Array.isArray(body?.data) ? body.data.map((entry) => typeof entry?.id === "string" ? entry.id.trim() : "").filter((id) => id !== "") : [];
10147
+ if (fromData.length > 0) return fromData;
10148
+ return Array.isArray(body?.models) ? body.models.map(
10149
+ (entry) => typeof entry?.name === "string" ? entry.name.replace(/^models\//, "").trim() : ""
10150
+ ).filter((id) => id !== "") : [];
10151
+ }
10152
+ async function discoverUpstreamModels(provider) {
10153
+ const cached = upstreamModelsCache.get(provider.id);
10154
+ if (cached) {
10155
+ const ttl = cached.ok ? UPSTREAM_MODELS_TTL_MS : UPSTREAM_MODELS_NEGATIVE_TTL_MS;
10156
+ if (Date.now() - cached.at < ttl) return cached.ids;
10157
+ upstreamModelsCache.delete(provider.id);
10158
+ }
10159
+ const existing = upstreamModelsInFlight.get(provider.id);
10160
+ if (existing) return existing;
10161
+ const pending = (async () => {
10162
+ let ids = [];
10163
+ try {
10164
+ ids = await fetchUpstreamModelsUncached(provider);
10165
+ } catch {
10166
+ ids = [];
10167
+ }
10168
+ upstreamModelsCache.set(provider.id, { at: Date.now(), ids, ok: ids.length > 0 });
10169
+ return ids;
10170
+ })();
10171
+ upstreamModelsInFlight.set(provider.id, pending);
10172
+ try {
10173
+ return await pending;
10174
+ } finally {
10175
+ upstreamModelsInFlight.delete(provider.id);
10176
+ }
10177
+ }
10178
+ async function passthroughProviderModelIds(llmConfig, providerId) {
10179
+ const provider = await llmConfig.getProvider(providerId);
10180
+ if (!provider) return [];
10181
+ const configured = provider.models ?? [];
10182
+ return configured.length > 0 ? configured : await discoverUpstreamModels(provider);
10183
+ }
9911
10184
  var ANTHROPIC_MODELS_CREATED_AT = (/* @__PURE__ */ new Date()).toISOString();
10185
+ function hasWildcardMapping(binding) {
10186
+ return (binding.modelMappings ?? []).some(
10187
+ (mapping) => mapping.source.includes("*") && mapping.target.trim() !== ""
10188
+ );
10189
+ }
10190
+ async function pushTargetCatalog(llmConfig, target, push) {
10191
+ if (target.kind === "provider") {
10192
+ for (const id of await passthroughProviderModelIds(llmConfig, target.providerId)) {
10193
+ push(id);
10194
+ }
10195
+ } else if (Object.prototype.hasOwnProperty.call(SUBSCRIPTION_MODEL_CATALOG, target.providerId)) {
10196
+ for (const id of SUBSCRIPTION_MODEL_CATALOG[target.providerId]) {
10197
+ push(id);
10198
+ }
10199
+ }
10200
+ }
10201
+ var MODEL_LIST_ENDPOINTS = ["chat", "responses", "messages", "gemini"];
9912
10202
  function parseModelsLimit(url) {
9913
10203
  const query = url?.split("?")[1];
9914
10204
  if (!query) return void 0;
@@ -9917,8 +10207,7 @@ function parseModelsLimit(url) {
9917
10207
  const n = Number.parseInt(raw, 10);
9918
10208
  return Number.isFinite(n) && n > 0 ? n : void 0;
9919
10209
  }
9920
- async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowedEndpoints, limit) {
9921
- void allowedEndpoints;
10210
+ async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowedEndpoints, limit, directTarget) {
9922
10211
  const entries2 = [];
9923
10212
  const seen = /* @__PURE__ */ new Set();
9924
10213
  const push = (id, displayName) => {
@@ -9932,29 +10221,37 @@ async function writeModelsListAnthropic(res, llmConfig, config, apiKeyId, allowe
9932
10221
  const modelId = ref ? parseModelRef(ref)?.modelId : void 0;
9933
10222
  return modelId ? `${name} (via ${modelId})` : void 0;
9934
10223
  };
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);
10224
+ for (const endpoint of MODEL_LIST_ENDPOINTS) {
10225
+ if (allowedEndpoints && !allowedEndpoints.includes(endpoint)) continue;
10226
+ for (const binding of candidateGatewayBindings(config.bindings, apiKeyId, endpoint)) {
10227
+ if (binding.modelMode === "passthrough") {
10228
+ await pushTargetCatalog(llmConfig, binding.target, push);
10229
+ continue;
10230
+ }
10231
+ if (binding.modelMappings?.length) {
10232
+ for (const mapping of binding.modelMappings) {
10233
+ const source = mapping.source.trim();
10234
+ if (source === "" || source.includes("*")) continue;
10235
+ push(source, viaLabel(source, mapping.target));
10236
+ }
10237
+ if (hasWildcardMapping(binding)) {
10238
+ await pushTargetCatalog(llmConfig, binding.target, push);
10239
+ for (const mapping of binding.modelMappings ?? []) {
10240
+ if (!mapping.source.includes("*") || mapping.target.trim() === "") continue;
10241
+ push(parseModelRef(mapping.target)?.modelId ?? mapping.target.trim());
10242
+ }
9943
10243
  }
10244
+ continue;
9944
10245
  }
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));
10246
+ for (const [kind, ref] of Object.entries(binding.modelMap ?? {})) {
10247
+ if (typeof ref !== "string" || ref.trim() === "") continue;
10248
+ push(kind, viaLabel(kind, ref));
9952
10249
  }
9953
- continue;
9954
10250
  }
9955
- for (const [kind, ref] of Object.entries(binding.modelMap ?? {})) {
9956
- if (typeof ref !== "string" || ref.trim() === "") continue;
9957
- push(kind, viaLabel(kind, ref));
10251
+ }
10252
+ if (directTarget?.kind === "provider") {
10253
+ for (const id of await passthroughProviderModelIds(llmConfig, directTarget.providerId)) {
10254
+ push(id);
9958
10255
  }
9959
10256
  }
9960
10257
  const limited = limit !== void 0 ? entries2.slice(0, limit) : entries2;
@@ -9996,29 +10293,172 @@ function handleOauthUsageProxy(res, config, apiKeyId) {
9996
10293
  res.writeHead(200, { "Content-Type": "application/json" });
9997
10294
  res.end(JSON.stringify(body));
9998
10295
  }
9999
- async function writeModelsList(res, llmConfig, config, apiKeyId, allowedEndpoints, imageModels = []) {
10296
+ function directUpstreamRoot(baseUrl, format) {
10297
+ const base = baseUrl.trim().replace(/\/+$/, "");
10298
+ if (!base) return null;
10299
+ if (format === "google") return base.replace(/\/models$/, "");
10300
+ if (format === "anthropic") return base.replace(/\/messages$/, "");
10301
+ if (format === "openai-response") return base.replace(/\/responses$/, "");
10302
+ return base.replace(/\/chat\/completions$/, "");
10303
+ }
10304
+ function directUpstreamUrl(provider, requestUrl) {
10305
+ const root = directUpstreamRoot(
10306
+ provider.api_base_url ?? "",
10307
+ resolveApiFormat(provider)
10308
+ );
10309
+ if (!root) return null;
10310
+ const raw = requestUrl ?? "/";
10311
+ const queryIndex = raw.indexOf("?");
10312
+ const path = (queryIndex >= 0 ? raw.slice(0, queryIndex) : raw) || "/";
10313
+ const query = queryIndex >= 0 ? raw.slice(queryIndex) : "";
10314
+ const versionSegment = /\/v\d+[a-z]*$/.exec(root)?.[0];
10315
+ let tail = path;
10316
+ if (versionSegment) {
10317
+ if (path === versionSegment) tail = "";
10318
+ else if (path.startsWith(`${versionSegment}/`)) tail = path.slice(versionSegment.length);
10319
+ }
10320
+ return `${root}${tail}${query}`;
10321
+ }
10322
+ function directUpstreamBinding(target, apiKeyId, priority = 1e3) {
10323
+ return {
10324
+ id: `direct:${apiKeyId}`,
10325
+ name: "direct upstream",
10326
+ enabled: true,
10327
+ keyScope: "selected",
10328
+ apiKeyIds: [apiKeyId],
10329
+ endpoint: "messages",
10330
+ target,
10331
+ priority,
10332
+ fallback: "fail",
10333
+ modelMode: "passthrough"
10334
+ };
10335
+ }
10336
+ async function relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, preReadBody) {
10337
+ const direct = verified.boundUpstream;
10338
+ const providerId = direct?.kind === "provider" ? direct.providerId : "";
10339
+ const provider = await deps.llmConfig.getProvider(providerId);
10340
+ if (!provider) {
10341
+ writeJsonError(
10342
+ res,
10343
+ 503,
10344
+ `direct upstream provider '${providerId}' is not configured for this key`
10345
+ );
10346
+ return;
10347
+ }
10348
+ const url = directUpstreamUrl(provider, req.url);
10349
+ if (!url) {
10350
+ writeJsonError(res, 503, `direct upstream provider '${providerId}' has no base URL`);
10351
+ return;
10352
+ }
10353
+ const concurrencyLimit = verified.maxConcurrency;
10354
+ let releaseConcurrency = null;
10355
+ if (typeof concurrencyLimit === "number" && concurrencyLimit > 0) {
10356
+ const cq = config.concurrencyQueue ?? DEFAULT_CONCURRENCY_QUEUE;
10357
+ const acquisition = concurrencyGate.acquire(verified.id, concurrencyLimit, {
10358
+ maxQueueSizeFactor: cq.maxQueueSizeFactor,
10359
+ minQueueSize: cq.minQueueSize,
10360
+ waitTimeoutMs: cq.waitTimeoutMs
10361
+ });
10362
+ const cancelOnClose = () => acquisition.cancel();
10363
+ res.once("close", cancelOnClose);
10364
+ let slot;
10365
+ try {
10366
+ slot = await acquisition.granted;
10367
+ } catch (err) {
10368
+ res.removeListener("close", cancelOnClose);
10369
+ if (isConcurrencyRejection(err)) {
10370
+ writeJsonError(res, 429, "Concurrency limit exceeded", { "Retry-After": "5" });
10371
+ return;
10372
+ }
10373
+ throw err;
10374
+ }
10375
+ res.removeListener("close", cancelOnClose);
10376
+ const release2 = () => slot.release();
10377
+ releaseConcurrency = release2;
10378
+ res.once("close", release2);
10379
+ }
10380
+ try {
10381
+ const rawBody = preReadBody !== void 0 ? preReadBody : await readBody3(req);
10382
+ if (audit) {
10383
+ audit.setRequestBody(rawBody);
10384
+ try {
10385
+ audit.sessionKey = deriveAuditSessionKey(
10386
+ rawBody ? JSON.parse(rawBody) : {},
10387
+ req.headers,
10388
+ { fallbackKey: verified.id, endpoint: "direct" }
10389
+ );
10390
+ } catch {
10391
+ }
10392
+ }
10393
+ const headers = getProviderHeaders(provider, resolveProviderKeyLiteral(provider.api_key));
10394
+ delete headers["Content-Type"];
10395
+ const clientContentType = req.headers["content-type"];
10396
+ if (typeof clientContentType === "string" && clientContentType) {
10397
+ headers["Content-Type"] = clientContentType;
10398
+ }
10399
+ const clientAccept = req.headers["accept"];
10400
+ if (typeof clientAccept === "string" && clientAccept) headers["Accept"] = clientAccept;
10401
+ const hasRequestBody = req.method !== "GET" && req.method !== "HEAD";
10402
+ const upstream = await fetchUpstream(
10403
+ url,
10404
+ {
10405
+ method: req.method ?? "GET",
10406
+ headers,
10407
+ ...hasRequestBody ? { body: rawBody } : {},
10408
+ // Client hang-up aborts the upstream call (no egress after cancel).
10409
+ signal: requestLifecycleSignal(req, res)
10410
+ },
10411
+ { providerId: "byo" }
10412
+ );
10413
+ if (audit) audit.provider = provider.id;
10414
+ const responseHeaders = {};
10415
+ const contentType = upstream.headers.get("content-type");
10416
+ if (contentType) responseHeaders["Content-Type"] = contentType;
10417
+ res.writeHead(upstream.status, responseHeaders);
10418
+ if (!upstream.body) {
10419
+ res.end();
10420
+ return;
10421
+ }
10422
+ const bodyStream = Readable2.fromWeb(
10423
+ upstream.body
10424
+ );
10425
+ bodyStream.on("error", () => {
10426
+ res.destroy();
10427
+ });
10428
+ bodyStream.pipe(res);
10429
+ await new Promise((resolve) => {
10430
+ res.once("close", resolve);
10431
+ });
10432
+ } catch (err) {
10433
+ const message = serializeError(err);
10434
+ if (deps.logger) deps.logger.error("[OutboundApi] direct relay error:", message);
10435
+ else console.error("[OutboundApi] direct relay error:", message);
10436
+ emitWebhookEvent({ kind: "server.error", at: Date.now(), message });
10437
+ if (audit) audit.error = message;
10438
+ if (!res.headersSent) writeJsonError(res, 502, message);
10439
+ else res.destroy();
10440
+ }
10441
+ }
10442
+ async function writeModelsList(res, llmConfig, config, apiKeyId, allowedEndpoints, imageModels = [], directTarget) {
10000
10443
  const modelIds = [];
10001
- for (const endpoint of ["chat", "responses", "messages", "gemini"]) {
10444
+ for (const endpoint of MODEL_LIST_ENDPOINTS) {
10002
10445
  if (allowedEndpoints && !allowedEndpoints.includes(endpoint)) continue;
10003
10446
  for (const binding of candidateGatewayBindings(config.bindings, apiKeyId, endpoint)) {
10004
10447
  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
- }
10448
+ await pushTargetCatalog(llmConfig, binding.target, (id) => modelIds.push(id));
10016
10449
  continue;
10017
10450
  }
10018
10451
  if (binding.modelMappings?.length) {
10019
10452
  modelIds.push(
10020
10453
  ...binding.modelMappings.map((mapping) => mapping.source.trim()).filter((source) => source !== "" && !source.includes("*"))
10021
10454
  );
10455
+ if (hasWildcardMapping(binding)) {
10456
+ await pushTargetCatalog(llmConfig, binding.target, (id) => modelIds.push(id));
10457
+ for (const mapping of binding.modelMappings ?? []) {
10458
+ if (!mapping.source.includes("*") || mapping.target.trim() === "") continue;
10459
+ modelIds.push(parseModelRef(mapping.target)?.modelId ?? mapping.target.trim());
10460
+ }
10461
+ }
10022
10462
  continue;
10023
10463
  }
10024
10464
  const endpointConfig = gatewayBindingToEndpointConfig(binding);
@@ -10035,6 +10475,9 @@ async function writeModelsList(res, llmConfig, config, apiKeyId, allowedEndpoint
10035
10475
  );
10036
10476
  }
10037
10477
  }
10478
+ if (directTarget?.kind === "provider") {
10479
+ modelIds.push(...await passthroughProviderModelIds(llmConfig, directTarget.providerId));
10480
+ }
10038
10481
  modelIds.push(...imageModels);
10039
10482
  const seen = /* @__PURE__ */ new Set();
10040
10483
  const data = modelIds.filter((modelId) => {
@@ -10129,6 +10572,16 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10129
10572
  });
10130
10573
  return;
10131
10574
  }
10575
+ const directProvider = verified.boundUpstream?.kind === "provider" ? verified.boundUpstream : null;
10576
+ if (verified.boundUpstream && !directProvider) {
10577
+ config = {
10578
+ ...config,
10579
+ bindings: [
10580
+ ...config.bindings ?? [],
10581
+ directUpstreamBinding(verified.boundUpstream, verified.id, 1e3)
10582
+ ]
10583
+ };
10584
+ }
10132
10585
  if (isOauthUsagePath) {
10133
10586
  if (req.method !== "GET") {
10134
10587
  writeJsonError(res, 404, `Unsupported: ${req.method} ${req.url}`);
@@ -10192,7 +10645,8 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10192
10645
  config,
10193
10646
  verified.id,
10194
10647
  verified.allowedEndpoints,
10195
- parseModelsLimit(req.url)
10648
+ parseModelsLimit(req.url),
10649
+ verified.boundUpstream
10196
10650
  );
10197
10651
  } else {
10198
10652
  let imageModels = [];
@@ -10208,21 +10662,26 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10208
10662
  config,
10209
10663
  verified.id,
10210
10664
  verified.allowedEndpoints,
10211
- imageModels
10665
+ imageModels,
10666
+ verified.boundUpstream
10212
10667
  );
10213
10668
  }
10214
10669
  return;
10215
10670
  }
10216
10671
  const endpoint = selectEndpoint(req.method, req.url);
10217
10672
  if (!endpoint) {
10673
+ if (directProvider) {
10674
+ await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit);
10675
+ return;
10676
+ }
10218
10677
  writeJsonError(res, 404, `Unsupported: ${req.method} ${req.url}`);
10219
10678
  return;
10220
10679
  }
10221
- if (verified.allowedEndpoints && !verified.allowedEndpoints.includes(endpoint)) {
10680
+ if (verified.allowedEndpoints && !verified.boundUpstream && !verified.allowedEndpoints.includes(endpoint)) {
10222
10681
  writeJsonError(res, 403, "API key is not allowed to access this endpoint");
10223
10682
  return;
10224
10683
  }
10225
- if (candidateGatewayBindings(config.bindings, verified.id, endpoint).length === 0) {
10684
+ if (candidateGatewayBindings(config.bindings, verified.id, endpoint).length === 0 && !directProvider) {
10226
10685
  writeJsonError(res, 503, `endpoint '${endpoint}' has no downstream route for this key`);
10227
10686
  return;
10228
10687
  }
@@ -10316,22 +10775,31 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10316
10775
  role
10317
10776
  });
10318
10777
  if (bindingResolution.source === "none") {
10778
+ if (directProvider) {
10779
+ await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, rawBody);
10780
+ return;
10781
+ }
10319
10782
  writeJsonError(res, 503, `endpoint '${endpoint}' has no downstream route for this key`);
10320
10783
  return;
10321
10784
  }
10322
10785
  const effectiveEndpointConfig = bindingResolution.config;
10323
10786
  const bindingAffinitySuffix = `:${bindingResolution.binding.id}`;
10324
10787
  const sessionId = deps.proxyDeps.apiKeyPool ? `outbound:${verified.id}${bindingAffinitySuffix}` : null;
10788
+ let replayBody = rawBody;
10789
+ if (!isCountTokens && effectiveEndpointConfig.reasoningEffort && injectMappingEffortDefault(endpoint, effectiveEndpointConfig.reasoningEffort, parsedBody)) {
10790
+ replayBody = JSON.stringify(parsedBody);
10791
+ }
10325
10792
  const hostedImageGenerationAllowed = verified.allowedEndpoints.includes("images");
10793
+ const listResolvedModel = endpoint === "chat" && bindingResolution.binding.modelMode === "mapped" ? resolveGatewayModelMappingRow(bindingResolution.binding.modelMappings, requestedModel)?.target ?? requestedModel : requestedModel;
10326
10794
  const resolved = isKindMappedEndpoint(endpoint) || endpoint === "chat" ? await resolveRoute({
10327
10795
  config: effectiveEndpointConfig,
10328
10796
  ingressFormat,
10329
10797
  llmConfig: deps.llmConfig,
10330
10798
  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,
10799
+ // The id the route resolves against: the client's ORIGINAL id for
10800
+ // kind-mapped endpoints (it selects the kind AND is stamped onto
10801
+ // `route.requestedModel`), the mapping-resolved target for chat.
10802
+ requestedModel: listResolvedModel,
10335
10803
  // Attribution: stamp the verified named-key id onto the route.
10336
10804
  apiKeyId: verified.id,
10337
10805
  hostedImageGenerationAllowed
@@ -10346,6 +10814,10 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10346
10814
  hostedImageGenerationAllowed
10347
10815
  });
10348
10816
  if (!resolved.ok) {
10817
+ if (directProvider) {
10818
+ await relayDirectUpstream(req, res, deps, verified, config, concurrencyGate, audit, rawBody);
10819
+ return;
10820
+ }
10349
10821
  writeJsonError(res, resolved.error.status, resolved.error.message);
10350
10822
  return;
10351
10823
  }
@@ -10407,7 +10879,7 @@ async function handleOutboundRequest(req, res, deps, config, rateLimiter, serial
10407
10879
  );
10408
10880
  try {
10409
10881
  shimAuthHeader(req, token);
10410
- const replay = makeReplayRequest(req, rawBody);
10882
+ const replay = makeReplayRequest(req, replayBody);
10411
10883
  await routeRequest(replay, res, routeMap, deps.proxyDeps);
10412
10884
  } catch (err) {
10413
10885
  if (isBoundAccountSelectionError(err)) {
@@ -13108,6 +13580,7 @@ export {
13108
13580
  writeError,
13109
13581
  writeBoundAccountError,
13110
13582
  resolvePoolBoundKey,
13583
+ buildByoRouteActivityMeta,
13111
13584
  DEFAULT_ROUTE_IDLE_MS,
13112
13585
  ProviderProxyRouteMap,
13113
13586
  previousResponseNotFound,
@@ -13187,6 +13660,7 @@ export {
13187
13660
  loadServerConfig,
13188
13661
  saveServerConfig,
13189
13662
  mergeServerConfig,
13663
+ injectMappingEffortDefault,
13190
13664
  computeKeyExpiry,
13191
13665
  checkKeyQuota,
13192
13666
  randomBase62,