@omnicross/core 0.1.7 → 0.1.8

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 (53) hide show
  1. package/dist/{ProviderProxy-C-xqrkKi.d.ts → ProviderProxy--ROec6VI.d.ts} +22 -3
  2. package/dist/{ProviderProxy-CnMQYN59.d.cts → ProviderProxy-QdwNV6l1.d.cts} +22 -3
  3. package/dist/{chunk-4BB343BI.cjs → chunk-26K6T2YS.cjs} +1209 -472
  4. package/dist/{chunk-OJGQKHN5.cjs → chunk-EPN67EGP.cjs} +3 -0
  5. package/dist/{chunk-E3COOKUP.js → chunk-QYC54PMG.js} +924 -187
  6. package/dist/{chunk-SOO562NM.js → chunk-UAPBLNN2.js} +3 -0
  7. package/dist/completion/ApiKeyPoolService.cjs +32 -1
  8. package/dist/completion/ApiKeyPoolService.d.cts +17 -1
  9. package/dist/completion/ApiKeyPoolService.d.ts +17 -1
  10. package/dist/completion/ApiKeyPoolService.js +31 -0
  11. package/dist/completion/CompletionService.cjs +3 -3
  12. package/dist/completion/CompletionService.js +2 -2
  13. package/dist/completion.cjs +3 -3
  14. package/dist/completion.js +2 -2
  15. package/dist/index.cjs +59 -3
  16. package/dist/index.d.cts +5 -5
  17. package/dist/index.d.ts +5 -5
  18. package/dist/index.js +58 -2
  19. package/dist/outbound-api/routeResolver.d.cts +3 -3
  20. package/dist/outbound-api/routeResolver.d.ts +3 -3
  21. package/dist/outbound-api/types.d.cts +2 -2
  22. package/dist/outbound-api/types.d.ts +2 -2
  23. package/dist/outbound-api.cjs +3 -3
  24. package/dist/outbound-api.d.cts +4 -4
  25. package/dist/outbound-api.d.ts +4 -4
  26. package/dist/outbound-api.js +2 -2
  27. package/dist/pipeline/AccountAllowanceScheduling.d.cts +2 -2
  28. package/dist/pipeline/AccountAllowanceScheduling.d.ts +2 -2
  29. package/dist/ports.d.cts +2 -2
  30. package/dist/ports.d.ts +2 -2
  31. package/dist/provider-proxy/ProviderProxy.cjs +3 -3
  32. package/dist/provider-proxy/ProviderProxy.d.cts +1 -1
  33. package/dist/provider-proxy/ProviderProxy.d.ts +1 -1
  34. package/dist/provider-proxy/ProviderProxy.js +2 -2
  35. package/dist/provider-proxy/ingress/providerProxyShared.cjs +3 -3
  36. package/dist/provider-proxy/ingress/providerProxyShared.js +2 -2
  37. package/dist/provider-proxy/types.d.cts +15 -1
  38. package/dist/provider-proxy/types.d.ts +15 -1
  39. package/dist/provider-proxy.cjs +59 -3
  40. package/dist/provider-proxy.d.cts +254 -8
  41. package/dist/provider-proxy.d.ts +254 -8
  42. package/dist/provider-proxy.js +60 -4
  43. package/dist/{routeResolver-BxOfunbm.d.ts → routeResolver-C0mkkZaM.d.ts} +1 -1
  44. package/dist/{routeResolver-D5OzD0iM.d.cts → routeResolver-CVFE8KKe.d.cts} +1 -1
  45. package/dist/{types-CqZD77rS.d.ts → types-BB8pxk_3.d.ts} +1 -1
  46. package/dist/{types-gjJ5lJ5n.d.cts → types-C2MwkLah.d.cts} +1 -1
  47. package/dist/usage/usage-recorder.cjs +2 -2
  48. package/dist/usage/usage-recorder.d.cts +6 -0
  49. package/dist/usage/usage-recorder.d.ts +6 -0
  50. package/dist/usage/usage-recorder.js +1 -1
  51. package/dist/usage.cjs +2 -2
  52. package/dist/usage.js +1 -1
  53. package/package.json +1 -1
@@ -94,6 +94,9 @@ import {
94
94
  resolvePrefixTarget,
95
95
  resolveRoute
96
96
  } from "./chunk-EBLFCNPY.js";
97
+ import {
98
+ getSubscriptionRegistryForOutbound
99
+ } from "./chunk-AQ5TNQB7.js";
97
100
  import {
98
101
  buildProviderApiUrl,
99
102
  resolveApiFormat,
@@ -432,12 +435,16 @@ var ProviderProxyRouteMap = class {
432
435
  * (`ANTHROPIC_AUTH_TOKEN` / `OPENAI_API_KEY`). Optionally override the idle
433
436
  * timeout (tests use a short one).
434
437
  */
435
- addRoute(context, idleMs) {
438
+ addRoute(context, idleMsOrOptions) {
439
+ const options = typeof idleMsOrOptions === "number" ? { idleMs: idleMsOrOptions } : idleMsOrOptions ?? {};
436
440
  const token = randomBytes(TOKEN_BYTES).toString("hex");
437
441
  const entry = {
438
442
  context,
439
443
  idleTimer: null,
440
- idleMs: idleMs ?? this.defaultIdleMs
444
+ idleMs: options.idleMs ?? this.defaultIdleMs,
445
+ onActivity: options.onActivity,
446
+ onEvicted: options.onEvicted,
447
+ evicted: false
441
448
  };
442
449
  this.routes.set(token, entry);
443
450
  this.armIdleTimer(token, entry);
@@ -452,6 +459,10 @@ var ProviderProxyRouteMap = class {
452
459
  if (!token) return void 0;
453
460
  const entry = this.routes.get(token);
454
461
  if (!entry) return void 0;
462
+ try {
463
+ entry.onActivity?.(Date.now());
464
+ } catch {
465
+ }
455
466
  this.armIdleTimer(token, entry);
456
467
  return entry.context;
457
468
  }
@@ -459,8 +470,16 @@ var ProviderProxyRouteMap = class {
459
470
  removeRoute(token) {
460
471
  const entry = this.routes.get(token);
461
472
  if (!entry) return false;
462
- this.clearIdleTimer(entry);
463
473
  this.routes.delete(token);
474
+ this.evict(entry, "removed");
475
+ return true;
476
+ }
477
+ /** Re-arm one existing route without rotating its token. */
478
+ renewRoute(token, idleMs) {
479
+ const entry = this.routes.get(token);
480
+ if (!entry) return false;
481
+ if (idleMs !== void 0) entry.idleMs = idleMs;
482
+ this.armIdleTimer(token, entry);
464
483
  return true;
465
484
  }
466
485
  /** Current live-route count (tests / diagnostics). */
@@ -473,10 +492,11 @@ var ProviderProxyRouteMap = class {
473
492
  }
474
493
  /** Tear down every route (proxy stop / app teardown). */
475
494
  clear() {
476
- for (const entry of this.routes.values()) {
477
- this.clearIdleTimer(entry);
478
- }
495
+ const entries2 = [...this.routes.values()];
479
496
  this.routes.clear();
497
+ for (const entry of entries2) {
498
+ this.evict(entry, "cleared");
499
+ }
480
500
  }
481
501
  // ---------------------------------------------------------------------------
482
502
  // Idle reaping (modeled on AcpSessionManager.armIdleTimer / clearIdleTimer)
@@ -484,7 +504,8 @@ var ProviderProxyRouteMap = class {
484
504
  armIdleTimer(token, entry) {
485
505
  this.clearIdleTimer(entry);
486
506
  entry.idleTimer = setTimeout(() => {
487
- this.routes.delete(token);
507
+ if (!this.routes.delete(token)) return;
508
+ this.evict(entry, "idle");
488
509
  }, entry.idleMs);
489
510
  entry.idleTimer.unref?.();
490
511
  }
@@ -494,6 +515,15 @@ var ProviderProxyRouteMap = class {
494
515
  entry.idleTimer = null;
495
516
  }
496
517
  }
518
+ evict(entry, reason) {
519
+ if (entry.evicted) return;
520
+ entry.evicted = true;
521
+ this.clearIdleTimer(entry);
522
+ try {
523
+ entry.onEvicted?.(reason);
524
+ } catch {
525
+ }
526
+ }
497
527
  };
498
528
 
499
529
  // src/provider-proxy/identity/claudeCodeHeaders.ts
@@ -583,6 +613,10 @@ function emitAnthropicUsageRecord(recorder, rawUsage, attribution) {
583
613
  providerId: attribution.providerId,
584
614
  model: attribution.model,
585
615
  apiKeyId: attribution.apiKeyId,
616
+ runId: attribution.routeLease?.runId ?? null,
617
+ routeLeaseId: attribution.routeLease?.leaseId ?? null,
618
+ routeLeaseConsumer: attribution.routeLease?.consumer ?? null,
619
+ routeLeaseStageId: attribution.routeLease?.stageId ?? null,
586
620
  auditResponse: attribution.auditResponse,
587
621
  engineOrigin: "codex-ingress",
588
622
  usage: tapped,
@@ -1142,7 +1176,7 @@ async function runSubscriptionSameFormatFetch(rawBody, plan, reportSelection, op
1142
1176
  } : void 0
1143
1177
  }
1144
1178
  );
1145
- return { response, rawStatus: response.status };
1179
+ return { response, rawStatus: response.status, actualModel: outboundModel };
1146
1180
  }
1147
1181
  function outboundRemapApplies(plan) {
1148
1182
  return plan.isSubscription && plan.transformerProvider.name === "claude";
@@ -1308,11 +1342,13 @@ async function handleAnthropicMessagesByo(res, rawBody, route, deps, options = {
1308
1342
  const upstreamSse = (upstreamResponse.headers.get("content-type") ?? "").includes(
1309
1343
  "text/event-stream"
1310
1344
  );
1345
+ const actualModel = "actualModel" in providerResponse && typeof providerResponse.actualModel === "string" ? providerResponse.actualModel : void 0;
1311
1346
  const usageAttribution = {
1312
1347
  sessionId: route.sessionId,
1313
1348
  providerId: route.providerId ?? "anthropic",
1314
- model: plan.resolvedModel,
1349
+ model: actualModel ?? plan.resolvedModel,
1315
1350
  apiKeyId: route.apiKeyId ?? null,
1351
+ routeLease: route.routeLease,
1316
1352
  // request-audit-log: correlate this request's tokens/cost to its audit record.
1317
1353
  auditResponse: res
1318
1354
  };
@@ -1571,6 +1607,10 @@ function emitGeminiUsageRecord(recorder, rawUsageMetadata, attribution) {
1571
1607
  providerId: attribution.providerId,
1572
1608
  model: attribution.model,
1573
1609
  apiKeyId: attribution.apiKeyId,
1610
+ runId: attribution.routeLease?.runId ?? null,
1611
+ routeLeaseId: attribution.routeLease?.leaseId ?? null,
1612
+ routeLeaseConsumer: attribution.routeLease?.consumer ?? null,
1613
+ routeLeaseStageId: attribution.routeLease?.stageId ?? null,
1574
1614
  auditResponse: attribution.auditResponse,
1575
1615
  engineOrigin: "codex-ingress",
1576
1616
  usage: tapped,
@@ -1636,6 +1676,7 @@ async function handleGeminiGenerateContentRequest(res, rawBody, url, route, deps
1636
1676
  providerId: route.providerId ?? "gemini",
1637
1677
  model: resolvedModel,
1638
1678
  apiKeyId: route.apiKeyId ?? null,
1679
+ routeLease: route.routeLease,
1639
1680
  // request-audit-log: correlate this request's tokens/cost to its audit record.
1640
1681
  auditResponse: res
1641
1682
  };
@@ -1792,6 +1833,10 @@ function emitChatCompletionsUsageRecord(recorder, rawUsage, attribution) {
1792
1833
  providerId: attribution.providerId,
1793
1834
  model: attribution.model,
1794
1835
  apiKeyId: attribution.apiKeyId,
1836
+ runId: attribution.routeLease?.runId ?? null,
1837
+ routeLeaseId: attribution.routeLease?.leaseId ?? null,
1838
+ routeLeaseConsumer: attribution.routeLease?.consumer ?? null,
1839
+ routeLeaseStageId: attribution.routeLease?.stageId ?? null,
1795
1840
  auditResponse: attribution.auditResponse,
1796
1841
  engineOrigin: "codex-ingress",
1797
1842
  usage: tapped,
@@ -1844,6 +1889,7 @@ async function handleOpenAIChatRequest(res, rawBody, route, deps) {
1844
1889
  providerId: route.providerId ?? "openai",
1845
1890
  model: resolvedModel,
1846
1891
  apiKeyId: route.apiKeyId ?? null,
1892
+ routeLease: route.routeLease,
1847
1893
  // request-audit-log: correlate this request's tokens/cost to its audit record.
1848
1894
  auditResponse: res
1849
1895
  };
@@ -2072,6 +2118,10 @@ function emitResponsesUsageRecord(recorder, rawUsage, attribution) {
2072
2118
  providerId: attribution.providerId,
2073
2119
  model: attribution.model,
2074
2120
  apiKeyId: attribution.apiKeyId,
2121
+ runId: attribution.routeLease?.runId ?? null,
2122
+ routeLeaseId: attribution.routeLease?.leaseId ?? null,
2123
+ routeLeaseConsumer: attribution.routeLease?.consumer ?? null,
2124
+ routeLeaseStageId: attribution.routeLease?.stageId ?? null,
2075
2125
  cacheKeySource: attribution.cacheKeySource,
2076
2126
  cacheKeyInjected: attribution.cacheKeyInjected,
2077
2127
  auditResponse: attribution.auditResponse,
@@ -2205,8 +2255,9 @@ async function handleOpenAIResponsesRequest(res, rawBody, route, deps, requestHe
2205
2255
  const usageAttribution = {
2206
2256
  sessionId: route.sessionId,
2207
2257
  providerId: route.providerId ?? "codex",
2208
- model: resolvedModel,
2258
+ model: providerResponse.actualModel ?? resolvedModel,
2209
2259
  apiKeyId: route.apiKeyId ?? null,
2260
+ routeLease: route.routeLease,
2210
2261
  cacheKeySource: cacheKeyAttribution.cacheKeySource,
2211
2262
  cacheKeyInjected: cacheKeyAttribution.cacheKeyInjected,
2212
2263
  // request-audit-log: correlate this request's tokens/cost to its audit record.
@@ -2391,6 +2442,7 @@ async function runPipeline4(responsesBody, plan) {
2391
2442
  const endpointTransformer = getResponsesEndpointTransformer();
2392
2443
  const { auth, sessionKey, chain, transformerProvider, resolvedModel, isStream, resolveUrl, upstreamUrl } = plan;
2393
2444
  let proxyAccountId;
2445
+ let actualModel = resolvedModel;
2394
2446
  let activityRecordId;
2395
2447
  const authHeaders = {};
2396
2448
  await auth.applyHeaders(authHeaders, {
@@ -2400,10 +2452,13 @@ async function runPipeline4(responsesBody, plan) {
2400
2452
  preferredAccountId: plan.preferredAccountId,
2401
2453
  preferredAccountGroup: plan.preferredAccountGroup,
2402
2454
  boundAccountFallbackPolicy: plan.boundAccountFallbackPolicy,
2403
- reportSelection: (accountId) => {
2455
+ reportSelection: (accountId, _isActive, remappedModel) => {
2404
2456
  proxyAccountId = accountId;
2457
+ if (remappedModel) actualModel = remappedModel;
2405
2458
  }
2406
2459
  });
2460
+ responsesBody.model = actualModel;
2461
+ transformerProvider.models = [actualModel];
2407
2462
  let rawStatus = null;
2408
2463
  const { response } = await executeProviderCall({
2409
2464
  executor,
@@ -2427,7 +2482,7 @@ async function runPipeline4(responsesBody, plan) {
2427
2482
  return headers;
2428
2483
  },
2429
2484
  fetchFn: (url, headers, body) => {
2430
- console.log(`[ProviderProxy:responses] -> ${url} model=${resolvedModel} stream=${isStream}`);
2485
+ console.log(`[ProviderProxy:responses] -> ${url} model=${actualModel} stream=${isStream}`);
2431
2486
  return fetchUpstream(
2432
2487
  url,
2433
2488
  { method: "POST", headers, body: JSON.stringify(body) },
@@ -2438,7 +2493,7 @@ async function runPipeline4(responsesBody, plan) {
2438
2493
  endpoint: "responses",
2439
2494
  sessionKey,
2440
2495
  sessionSource: plan.sessionSource ?? "none",
2441
- model: resolvedModel,
2496
+ model: actualModel,
2442
2497
  onRecorded: (record) => {
2443
2498
  activityRecordId = record.id;
2444
2499
  }
@@ -2451,7 +2506,7 @@ async function runPipeline4(responsesBody, plan) {
2451
2506
  },
2452
2507
  runResponseChain: true
2453
2508
  });
2454
- return { response, rawStatus, accountId: proxyAccountId, activityRecordId };
2509
+ return { response, rawStatus, accountId: proxyAccountId, actualModel, activityRecordId };
2455
2510
  }
2456
2511
  async function runPipelineWithPoolReporting4(responsesBody, plan) {
2457
2512
  const first = await runPipeline4(responsesBody, plan);
@@ -2555,6 +2610,7 @@ var ProviderProxy = class {
2555
2610
  server = null;
2556
2611
  port = 0;
2557
2612
  routes;
2613
+ beforeStop = /* @__PURE__ */ new Set();
2558
2614
  /**
2559
2615
  * Start the resident listener on a stable port on 127.0.0.1. Idempotent —
2560
2616
  * a second `start()` returns the already-bound port.
@@ -2595,6 +2651,12 @@ var ProviderProxy = class {
2595
2651
  }
2596
2652
  /** Stop the listener, clear all routes, and release the port. */
2597
2653
  async stop() {
2654
+ for (const cleanup of [...this.beforeStop]) {
2655
+ try {
2656
+ await cleanup();
2657
+ } catch {
2658
+ }
2659
+ }
2598
2660
  this.routes.clear();
2599
2661
  const server = this.server;
2600
2662
  if (!server) return;
@@ -2611,6 +2673,15 @@ var ProviderProxy = class {
2611
2673
  getBaseUrl() {
2612
2674
  return `http://127.0.0.1:${this.port}`;
2613
2675
  }
2676
+ /** Register daemon-owned cleanup that must run before route-map teardown. */
2677
+ registerBeforeStop(cleanup) {
2678
+ this.beforeStop.add(cleanup);
2679
+ return () => this.beforeStop.delete(cleanup);
2680
+ }
2681
+ /** Whether the resident listener has completed binding. */
2682
+ isReady() {
2683
+ return this.server !== null && this.port > 0;
2684
+ }
2614
2685
  /**
2615
2686
  * The SHARED route map. Exposed so the outbound API server
2616
2687
  * (`outbound-api-server`) can mint per-request routes on the SAME map and
@@ -2628,8 +2699,12 @@ var ProviderProxy = class {
2628
2699
  return this.deps;
2629
2700
  }
2630
2701
  /** Register a route for one run; returns the crypto route token (task 2.2). */
2631
- addRoute(context, idleMs) {
2632
- return this.routes.addRoute(context, idleMs);
2702
+ addRoute(context, idleMsOrOptions) {
2703
+ return this.routes.addRoute(context, idleMsOrOptions);
2704
+ }
2705
+ /** Re-arm one route while retaining the exact same bearer token. */
2706
+ renewRoute(token, idleMs) {
2707
+ return this.routes.renewRoute(token, idleMs);
2633
2708
  }
2634
2709
  /** Remove a route at run end. Returns true if an entry existed. */
2635
2710
  removeRoute(token) {
@@ -2641,6 +2716,802 @@ var ProviderProxy = class {
2641
2716
  }
2642
2717
  };
2643
2718
 
2719
+ // src/provider-proxy/RouteLeaseManager.ts
2720
+ import { randomBytes as randomBytes3, randomUUID } from "crypto";
2721
+
2722
+ // src/provider-proxy/routeLeaseSchema.ts
2723
+ import { createHash, createHmac, randomBytes as randomBytes2 } from "crypto";
2724
+ var ROUTE_LEASE_REQUEST_SCHEMA = "omnicross.route-lease.request/1";
2725
+ var ROUTE_LEASE_RESULT_SCHEMA = "omnicross.route-lease/1";
2726
+ var ROUTE_LEASE_CAPABILITIES_SCHEMA = "omnicross.route-lease.capabilities/1";
2727
+ var ROUTE_LEASE_API_VERSION = 1;
2728
+ var ROUTE_LEASE_DEFAULT_TTL_SECONDS = 600;
2729
+ var ROUTE_LEASE_MAX_TTL_SECONDS = 3600;
2730
+ var ROUTE_LEASE_MAX_IDEMPOTENCY_BYTES = 256;
2731
+ var ROUTE_LEASE_MAX_CONSUMER_BYTES = 64;
2732
+ var ROUTE_LEASE_MAX_EXECUTION_ID_BYTES = 128;
2733
+ var ROUTE_LEASE_MAX_SESSION_ID_BYTES = 512;
2734
+ var ROUTE_LEASE_MAX_MODEL_BYTES = 256;
2735
+ var ROUTE_LEASE_SESSION_HASH_DOMAIN = "omnicross.route-lease.session/v1";
2736
+ var ROUTE_LEASE_CODEX_TOKEN_ENV = "OMNICROSS_CODEX_ROUTE_TOKEN";
2737
+ var ROUTE_LEASE_RUNTIMES = ["claude", "codex"];
2738
+ var ROUTE_LEASE_CAPABILITIES = Object.freeze({
2739
+ schemaVersion: ROUTE_LEASE_CAPABILITIES_SCHEMA,
2740
+ runtimes: Object.freeze(["claude", "codex"]),
2741
+ upstreamKinds: Object.freeze(["provider", "account", "account-group", "account-pool"]),
2742
+ leaseApiVersion: ROUTE_LEASE_API_VERSION,
2743
+ codexAuthMode: "env_key",
2744
+ maxTtlSeconds: ROUTE_LEASE_MAX_TTL_SECONDS
2745
+ });
2746
+ var ROUTE_LEASE_RUNTIME_TABLE = Object.freeze({
2747
+ claude: Object.freeze({ endpoint: "messages", ingressFormat: "anthropic-messages", wirePath: "/v1/messages" }),
2748
+ codex: Object.freeze({ endpoint: "responses", ingressFormat: "openai-responses", wirePath: "/openai/responses" })
2749
+ });
2750
+ var ERROR_DEFAULTS = {
2751
+ invalid_request: { status: 400, retryable: false },
2752
+ runtime_unsupported: { status: 400, retryable: false },
2753
+ model_not_configured: { status: 400, retryable: false },
2754
+ format_unsupported: { status: 400, retryable: false },
2755
+ control_unauthorized: { status: 403, retryable: false },
2756
+ upstream_not_found: { status: 404, retryable: false },
2757
+ lease_not_found: { status: 404, retryable: false },
2758
+ idempotency_conflict: { status: 409, retryable: false },
2759
+ upstream_unavailable: { status: 409, retryable: true },
2760
+ lease_expired: { status: 410, retryable: false },
2761
+ upstream_exhausted: { status: 429, retryable: true },
2762
+ daemon_not_ready: { status: 503, retryable: true }
2763
+ };
2764
+ var RouteLeaseError = class extends Error {
2765
+ constructor(code, message, options = {}) {
2766
+ super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
2767
+ this.code = code;
2768
+ this.name = "RouteLeaseError";
2769
+ const defaults = ERROR_DEFAULTS[code];
2770
+ this.status = options.status ?? defaults.status;
2771
+ this.retryable = options.retryable ?? defaults.retryable;
2772
+ if (options.retryAfterSeconds !== void 0) {
2773
+ this.retryAfterSeconds = Math.max(1, Math.min(3600, Math.floor(options.retryAfterSeconds)));
2774
+ }
2775
+ }
2776
+ code;
2777
+ type = "route_lease_error";
2778
+ status;
2779
+ retryable;
2780
+ retryAfterSeconds;
2781
+ toResponse() {
2782
+ return { error: { type: this.type, code: this.code, message: this.message, retryable: this.retryable } };
2783
+ }
2784
+ };
2785
+ var IDEMPOTENCY_RE = /^[A-Za-z0-9._:-]+$/u;
2786
+ var CONTROL_RE = /[\u0000-\u001f\u007f-\u009f]/u;
2787
+ var PROCESS_SESSION_HMAC_KEY = randomBytes2(32);
2788
+ function isRecord(value) {
2789
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2790
+ }
2791
+ function byteLength(value) {
2792
+ return Buffer.byteLength(value, "utf8");
2793
+ }
2794
+ function normalizedString(value, field, maxBytes, options = {}) {
2795
+ if (typeof value !== "string") throw invalid(`${field} must be a string`);
2796
+ const normalized = value.trim();
2797
+ if (!normalized || byteLength(normalized) > maxBytes || CONTROL_RE.test(normalized)) {
2798
+ throw invalid(`${field} is invalid`);
2799
+ }
2800
+ if (options.asciiPattern && !options.asciiPattern.test(normalized)) {
2801
+ throw invalid(`${field} is invalid`);
2802
+ }
2803
+ return normalized;
2804
+ }
2805
+ function optionalExecutionId(value, field) {
2806
+ return value === void 0 ? void 0 : normalizedString(value, field, ROUTE_LEASE_MAX_EXECUTION_ID_BYTES);
2807
+ }
2808
+ function invalid(message) {
2809
+ return new RouteLeaseError("invalid_request", message);
2810
+ }
2811
+ function normalizeRouteLeaseIdempotencyKey(value) {
2812
+ return normalizedString(value, "Idempotency-Key", ROUTE_LEASE_MAX_IDEMPOTENCY_BYTES, {
2813
+ asciiPattern: IDEMPOTENCY_RE
2814
+ });
2815
+ }
2816
+ function normalizeRouteLeaseTtl(value) {
2817
+ if (value === void 0) return ROUTE_LEASE_DEFAULT_TTL_SECONDS;
2818
+ if (!Number.isInteger(value) || value < 1 || value > ROUTE_LEASE_MAX_TTL_SECONDS) {
2819
+ throw invalid(`ttlSeconds must be an integer from 1 to ${ROUTE_LEASE_MAX_TTL_SECONDS}`);
2820
+ }
2821
+ return value;
2822
+ }
2823
+ function normalizeUpstream(value) {
2824
+ if (!isRecord(value) || typeof value.kind !== "string") throw invalid("upstream is invalid");
2825
+ const providerId = normalizedString(value.providerId, "upstream.providerId", 128);
2826
+ switch (value.kind) {
2827
+ case "provider": {
2828
+ const keyId = value.keyId === void 0 ? void 0 : normalizedString(value.keyId, "upstream.keyId", 128);
2829
+ return keyId ? { kind: "provider", providerId, keyId } : { kind: "provider", providerId };
2830
+ }
2831
+ case "account":
2832
+ return {
2833
+ kind: "account",
2834
+ providerId,
2835
+ accountId: normalizedString(value.accountId, "upstream.accountId", 128)
2836
+ };
2837
+ case "account-group":
2838
+ return {
2839
+ kind: "account-group",
2840
+ providerId,
2841
+ group: normalizedString(value.group, "upstream.group", 128)
2842
+ };
2843
+ case "account-pool":
2844
+ return { kind: "account-pool", providerId };
2845
+ default:
2846
+ throw invalid("upstream kind is unsupported");
2847
+ }
2848
+ }
2849
+ function hashRouteLeaseSessionId(sessionId, hmacKey = PROCESS_SESSION_HMAC_KEY) {
2850
+ return createHmac("sha256", hmacKey).update(ROUTE_LEASE_SESSION_HASH_DOMAIN, "utf8").update("\0", "utf8").update(sessionId, "utf8").digest("hex");
2851
+ }
2852
+ function canonicalizeRouteLeasePayload(value) {
2853
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
2854
+ if (Array.isArray(value)) {
2855
+ return `[${value.map((entry) => canonicalizeRouteLeasePayload(entry)).join(",")}]`;
2856
+ }
2857
+ const record = value;
2858
+ const fields = Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalizeRouteLeasePayload(record[key])}`);
2859
+ return `{${fields.join(",")}}`;
2860
+ }
2861
+ function hashRouteLeasePayload(value) {
2862
+ return createHash("sha256").update(canonicalizeRouteLeasePayload(value), "utf8").digest("hex");
2863
+ }
2864
+ function parseRouteLeaseCreate(raw, idempotencyHeader, hmacKey = PROCESS_SESSION_HMAC_KEY) {
2865
+ if (!isRecord(raw)) throw invalid("request body must be a JSON object");
2866
+ if (raw.schemaVersion !== ROUTE_LEASE_REQUEST_SCHEMA) {
2867
+ throw invalid(`schemaVersion must be ${ROUTE_LEASE_REQUEST_SCHEMA}`);
2868
+ }
2869
+ const consumer = normalizedString(raw.consumer, "consumer", ROUTE_LEASE_MAX_CONSUMER_BYTES);
2870
+ const runtime = raw.runtime;
2871
+ if (runtime !== "claude" && runtime !== "codex") {
2872
+ throw new RouteLeaseError("runtime_unsupported", "runtime is unsupported");
2873
+ }
2874
+ const upstream = normalizeUpstream(raw.upstream);
2875
+ if (typeof raw.model !== "string" || raw.model.trim().length === 0) {
2876
+ throw new RouteLeaseError("model_not_configured", "model is not configured");
2877
+ }
2878
+ const model = normalizedString(raw.model, "model", ROUTE_LEASE_MAX_MODEL_BYTES);
2879
+ const ttlSeconds = normalizeRouteLeaseTtl(raw.ttlSeconds);
2880
+ const idempotencyKey = normalizeRouteLeaseIdempotencyKey(idempotencyHeader);
2881
+ let execution;
2882
+ let semanticExecution;
2883
+ if (raw.execution !== void 0) {
2884
+ if (!isRecord(raw.execution)) throw invalid("execution must be a JSON object");
2885
+ const runId = optionalExecutionId(raw.execution.runId, "execution.runId");
2886
+ const stageId = optionalExecutionId(raw.execution.stageId, "execution.stageId");
2887
+ let attempt;
2888
+ if (raw.execution.attempt !== void 0) {
2889
+ if (!Number.isSafeInteger(raw.execution.attempt) || raw.execution.attempt < 1) {
2890
+ throw invalid("execution.attempt must be a positive integer");
2891
+ }
2892
+ attempt = raw.execution.attempt;
2893
+ }
2894
+ let sessionId;
2895
+ let sessionIdHash;
2896
+ if (raw.execution.sessionId !== void 0) {
2897
+ sessionId = normalizedString(raw.execution.sessionId, "execution.sessionId", ROUTE_LEASE_MAX_SESSION_ID_BYTES);
2898
+ sessionIdHash = hashRouteLeaseSessionId(sessionId, hmacKey);
2899
+ }
2900
+ if (runId !== void 0 || stageId !== void 0 || attempt !== void 0 || sessionIdHash !== void 0) {
2901
+ execution = { runId, stageId, attempt, sessionIdHash };
2902
+ semanticExecution = { runId, stageId, attempt, sessionId };
2903
+ }
2904
+ }
2905
+ const request = {
2906
+ schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA,
2907
+ consumer,
2908
+ runtime,
2909
+ upstream,
2910
+ model,
2911
+ ...execution ? { execution } : {},
2912
+ ttlSeconds
2913
+ };
2914
+ const semanticPayload = {
2915
+ schemaVersion: request.schemaVersion,
2916
+ consumer,
2917
+ runtime,
2918
+ upstream,
2919
+ model,
2920
+ ...semanticExecution ? { execution: semanticExecution } : {},
2921
+ ttlSeconds
2922
+ };
2923
+ return { request, idempotencyKey, payloadHash: hashRouteLeasePayload(semanticPayload) };
2924
+ }
2925
+ function routeLeaseRuntime(runtime) {
2926
+ switch (runtime) {
2927
+ case "claude":
2928
+ return ROUTE_LEASE_RUNTIME_TABLE.claude;
2929
+ case "codex":
2930
+ return ROUTE_LEASE_RUNTIME_TABLE.codex;
2931
+ default: {
2932
+ const exhaustive = runtime;
2933
+ throw new RouteLeaseError("runtime_unsupported", `runtime is unsupported: ${String(exhaustive)}`);
2934
+ }
2935
+ }
2936
+ }
2937
+
2938
+ // src/provider-proxy/RouteLeaseManager.ts
2939
+ var SYSTEM_ROUTE_LEASE_CLOCK = {
2940
+ now: () => Date.now(),
2941
+ setTimeout(callback, delayMs) {
2942
+ const timer = setTimeout(callback, delayMs);
2943
+ timer.unref?.();
2944
+ return timer;
2945
+ },
2946
+ clearTimeout(handle) {
2947
+ clearTimeout(handle);
2948
+ }
2949
+ };
2950
+ function cloneUpstream(upstream) {
2951
+ return { ...upstream };
2952
+ }
2953
+ function metadataCopy(metadata) {
2954
+ return {
2955
+ ...metadata,
2956
+ upstream: cloneUpstream(metadata.upstream),
2957
+ ...metadata.execution ? { execution: { ...metadata.execution } } : {}
2958
+ };
2959
+ }
2960
+ var RouteLeaseManager = class {
2961
+ constructor(routePort, targetResolver, descriptors, options = {}) {
2962
+ this.routePort = routePort;
2963
+ this.targetResolver = targetResolver;
2964
+ this.descriptors = descriptors;
2965
+ this.clock = options.clock ?? SYSTEM_ROUTE_LEASE_CLOCK;
2966
+ this.hmacKey = options.hmacKey ?? randomBytes3(32);
2967
+ this.tombstoneMaxCount = Math.max(1, options.tombstoneMaxCount ?? 512);
2968
+ this.tombstoneMaxAgeMs = Math.max(1, options.tombstoneMaxAgeMs ?? 60 * 60 * 1e3);
2969
+ this.logger = options.logger;
2970
+ this.afterPublish = options.afterPublish;
2971
+ }
2972
+ routePort;
2973
+ targetResolver;
2974
+ descriptors;
2975
+ active = /* @__PURE__ */ new Map();
2976
+ idempotency = /* @__PURE__ */ new Map();
2977
+ pendingCreates = /* @__PURE__ */ new Map();
2978
+ tombstones = /* @__PURE__ */ new Map();
2979
+ clock;
2980
+ hmacKey;
2981
+ tombstoneMaxCount;
2982
+ tombstoneMaxAgeMs;
2983
+ logger;
2984
+ afterPublish;
2985
+ closed = false;
2986
+ parse(raw, idempotencyKey) {
2987
+ return parseRouteLeaseCreate(raw, idempotencyKey, this.hmacKey);
2988
+ }
2989
+ async createFromRequest(raw, idempotencyKey) {
2990
+ return this.create(this.parse(raw, idempotencyKey));
2991
+ }
2992
+ async create(input) {
2993
+ if (this.closed) throw new RouteLeaseError("daemon_not_ready", "route lease manager is shutting down");
2994
+ const request = input.request;
2995
+ const scope = `${request.consumer}\0${input.idempotencyKey}`;
2996
+ const existing = this.idempotency.get(scope);
2997
+ if (existing) {
2998
+ const lease = this.active.get(existing.leaseId);
2999
+ if (!lease) {
3000
+ this.idempotency.delete(scope);
3001
+ } else if (existing.payloadHash !== input.payloadHash) {
3002
+ throw new RouteLeaseError("idempotency_conflict", "idempotency key is already used for a different live request");
3003
+ } else {
3004
+ return { created: false, result: lease.result };
3005
+ }
3006
+ }
3007
+ const pending = this.pendingCreates.get(scope);
3008
+ if (pending) {
3009
+ if (pending.payloadHash !== input.payloadHash) {
3010
+ throw new RouteLeaseError("idempotency_conflict", "idempotency key is already used for a different live request");
3011
+ }
3012
+ const outcome2 = await pending.outcome;
3013
+ return { created: false, result: outcome2.result };
3014
+ }
3015
+ const outcome = this.createNew(input, scope);
3016
+ const pendingCreate = { payloadHash: input.payloadHash, outcome };
3017
+ this.pendingCreates.set(scope, pendingCreate);
3018
+ try {
3019
+ return await outcome;
3020
+ } finally {
3021
+ if (this.pendingCreates.get(scope) === pendingCreate) this.pendingCreates.delete(scope);
3022
+ }
3023
+ }
3024
+ async createNew(input, scope) {
3025
+ const request = input.request;
3026
+ if (!this.routePort.isReady()) {
3027
+ throw new RouteLeaseError("daemon_not_ready", "resident provider proxy is not ready");
3028
+ }
3029
+ if (!this.descriptors.has(request.runtime)) {
3030
+ throw new RouteLeaseError("runtime_unsupported", "runtime launch adapter is unavailable");
3031
+ }
3032
+ const leaseId = randomUUID();
3033
+ const resolved = await this.targetResolver.resolve(request);
3034
+ this.assertOpen();
3035
+ const route = {
3036
+ ...resolved,
3037
+ sessionId: request.execution?.sessionIdHash ?? null,
3038
+ routeLease: {
3039
+ leaseId,
3040
+ consumer: request.consumer,
3041
+ ...request.execution?.runId ? { runId: request.execution.runId } : {},
3042
+ ...request.execution?.stageId ? { stageId: request.execution.stageId } : {}
3043
+ }
3044
+ };
3045
+ const now = this.clock.now();
3046
+ const ttlMs = request.ttlSeconds * 1e3;
3047
+ const expiresAtMs = now + ttlMs;
3048
+ let routeToken;
3049
+ let published = false;
3050
+ let activeLease;
3051
+ try {
3052
+ routeToken = this.routePort.addRoute(route, {
3053
+ idleMs: ttlMs,
3054
+ onActivity: (at) => this.noteActivity(leaseId, at),
3055
+ onEvicted: () => this.onRouteEvicted(leaseId)
3056
+ });
3057
+ const launch = this.descriptors.build(request.runtime, {
3058
+ proxyBaseUrl: this.routePort.getBaseUrl(),
3059
+ model: request.model,
3060
+ routeToken
3061
+ });
3062
+ this.assertOpen();
3063
+ const metadata = {
3064
+ leaseId,
3065
+ consumer: request.consumer,
3066
+ runtime: request.runtime,
3067
+ upstream: cloneUpstream(request.upstream),
3068
+ model: request.model,
3069
+ createdAt: new Date(now).toISOString(),
3070
+ expiresAt: new Date(expiresAtMs).toISOString(),
3071
+ status: "active",
3072
+ ...request.execution ? { execution: { ...request.execution } } : {}
3073
+ };
3074
+ const result = {
3075
+ schemaVersion: ROUTE_LEASE_RESULT_SCHEMA,
3076
+ ...metadata,
3077
+ status: "active",
3078
+ launch: { env: { ...launch.env }, extraArgs: [...launch.extraArgs] }
3079
+ };
3080
+ const timer = this.clock.setTimeout(() => this.expire(leaseId), ttlMs);
3081
+ activeLease = {
3082
+ leaseId,
3083
+ routeToken,
3084
+ idempotencyScope: scope,
3085
+ payloadHash: input.payloadHash,
3086
+ request,
3087
+ route,
3088
+ result,
3089
+ metadata,
3090
+ expiresAtMs,
3091
+ timer
3092
+ };
3093
+ this.assertOpen();
3094
+ this.active.set(leaseId, activeLease);
3095
+ this.idempotency.set(scope, { leaseId, payloadHash: input.payloadHash });
3096
+ published = true;
3097
+ this.afterPublish?.();
3098
+ if (this.closed) {
3099
+ this.finalize(activeLease, "released");
3100
+ throw new RouteLeaseError("daemon_not_ready", "route lease manager is shutting down");
3101
+ }
3102
+ return { created: true, result };
3103
+ } catch (error) {
3104
+ if (published && activeLease) {
3105
+ this.active.delete(leaseId);
3106
+ this.idempotency.delete(scope);
3107
+ this.clock.clearTimeout(activeLease.timer);
3108
+ }
3109
+ if (routeToken) this.safeRemoveRoute(routeToken, leaseId);
3110
+ if (error instanceof RouteLeaseError) throw error;
3111
+ throw new RouteLeaseError("upstream_unavailable", "route lease creation failed safely", { cause: error });
3112
+ }
3113
+ }
3114
+ list() {
3115
+ this.pruneTombstones();
3116
+ return [
3117
+ ...[...this.active.values()].map((lease) => metadataCopy(lease.metadata)),
3118
+ ...[...this.tombstones.values()].map((entry) => metadataCopy(entry.metadata))
3119
+ ].sort((left, right) => left.createdAt.localeCompare(right.createdAt));
3120
+ }
3121
+ get(leaseId) {
3122
+ const active = this.active.get(leaseId);
3123
+ if (active) return metadataCopy(active.metadata);
3124
+ this.pruneTombstones();
3125
+ const tombstone = this.tombstones.get(leaseId);
3126
+ if (tombstone) return metadataCopy(tombstone.metadata);
3127
+ throw new RouteLeaseError("lease_not_found", "route lease was not found");
3128
+ }
3129
+ renew(leaseId, ttlSeconds) {
3130
+ const lease = this.active.get(leaseId);
3131
+ if (!lease) {
3132
+ this.pruneTombstones();
3133
+ const known = this.tombstones.get(leaseId);
3134
+ if (known?.metadata.status === "expired") {
3135
+ throw new RouteLeaseError("lease_expired", "route lease has expired");
3136
+ }
3137
+ throw new RouteLeaseError("lease_not_found", "route lease was not found");
3138
+ }
3139
+ const now = this.clock.now();
3140
+ if (now >= lease.expiresAtMs) {
3141
+ this.finalize(lease, "expired");
3142
+ throw new RouteLeaseError("lease_expired", "route lease has expired");
3143
+ }
3144
+ const ttlMs = ttlSeconds * 1e3;
3145
+ const expiresAtMs = now + ttlMs;
3146
+ const nextTimer = this.clock.setTimeout(() => this.expire(leaseId), ttlMs);
3147
+ const previousTimer = lease.timer;
3148
+ lease.timer = nextTimer;
3149
+ lease.expiresAtMs = expiresAtMs;
3150
+ lease.metadata = { ...lease.metadata, expiresAt: new Date(expiresAtMs).toISOString() };
3151
+ this.clock.clearTimeout(previousTimer);
3152
+ if (!this.routePort.renewRoute(lease.routeToken, ttlMs)) {
3153
+ this.finalize(lease, "expired", true);
3154
+ throw new RouteLeaseError("lease_expired", "route lease route has expired");
3155
+ }
3156
+ return { leaseId, expiresAt: lease.metadata.expiresAt, status: "active" };
3157
+ }
3158
+ release(leaseId) {
3159
+ const lease = this.active.get(leaseId);
3160
+ if (!lease) return { leaseId, released: false };
3161
+ this.finalize(lease, "released");
3162
+ return { leaseId, released: true };
3163
+ }
3164
+ shutdown() {
3165
+ if (this.closed) return;
3166
+ this.closed = true;
3167
+ for (const lease of [...this.active.values()]) {
3168
+ try {
3169
+ this.finalize(lease, "released");
3170
+ } catch (error) {
3171
+ this.logger?.warn("route lease cleanup failed during shutdown", { leaseId: lease.leaseId, error: safeErrorName(error) });
3172
+ }
3173
+ }
3174
+ }
3175
+ activeCount() {
3176
+ return this.active.size;
3177
+ }
3178
+ assertOpen() {
3179
+ if (this.closed) {
3180
+ throw new RouteLeaseError("daemon_not_ready", "route lease manager is shutting down");
3181
+ }
3182
+ }
3183
+ noteActivity(leaseId, at) {
3184
+ const lease = this.active.get(leaseId);
3185
+ if (!lease || at >= lease.expiresAtMs) return;
3186
+ lease.metadata = { ...lease.metadata, lastActivityAt: new Date(at).toISOString() };
3187
+ }
3188
+ onRouteEvicted(leaseId) {
3189
+ const lease = this.active.get(leaseId);
3190
+ if (lease) this.finalize(lease, "expired", true);
3191
+ }
3192
+ expire(leaseId) {
3193
+ const lease = this.active.get(leaseId);
3194
+ if (!lease) return;
3195
+ const remaining = lease.expiresAtMs - this.clock.now();
3196
+ if (remaining > 0) {
3197
+ lease.timer = this.clock.setTimeout(() => this.expire(leaseId), remaining);
3198
+ return;
3199
+ }
3200
+ this.finalize(lease, "expired");
3201
+ }
3202
+ finalize(lease, status, routeAlreadyGone = false) {
3203
+ if (this.active.get(lease.leaseId) !== lease) return;
3204
+ this.active.delete(lease.leaseId);
3205
+ this.idempotency.delete(lease.idempotencyScope);
3206
+ this.clock.clearTimeout(lease.timer);
3207
+ if (!routeAlreadyGone) this.safeRemoveRoute(lease.routeToken, lease.leaseId);
3208
+ const metadata = { ...lease.metadata, status };
3209
+ this.tombstones.delete(lease.leaseId);
3210
+ this.tombstones.set(lease.leaseId, { metadata, terminalAtMs: this.clock.now() });
3211
+ this.pruneTombstones();
3212
+ }
3213
+ safeRemoveRoute(routeToken, leaseId) {
3214
+ try {
3215
+ this.routePort.removeRoute(routeToken);
3216
+ } catch (error) {
3217
+ this.logger?.warn("route lease route cleanup failed", { leaseId, error: safeErrorName(error) });
3218
+ }
3219
+ }
3220
+ pruneTombstones() {
3221
+ const cutoff = this.clock.now() - this.tombstoneMaxAgeMs;
3222
+ for (const [leaseId, tombstone] of this.tombstones) {
3223
+ if (tombstone.terminalAtMs < cutoff) this.tombstones.delete(leaseId);
3224
+ }
3225
+ while (this.tombstones.size > this.tombstoneMaxCount) {
3226
+ const oldest = this.tombstones.keys().next().value;
3227
+ if (!oldest) break;
3228
+ this.tombstones.delete(oldest);
3229
+ }
3230
+ }
3231
+ };
3232
+ function safeErrorName(error) {
3233
+ return error instanceof Error ? error.name : "unknown";
3234
+ }
3235
+
3236
+ // src/outbound-api/gatewayBindingResolver.ts
3237
+ var MESSAGE_FALLBACK_KINDS = ["sonnet", "opus", "haiku", "fable"];
3238
+ function nonBlank(value) {
3239
+ return typeof value === "string" && value.trim() !== "";
3240
+ }
3241
+ function gatewayBindingAllowsKey(binding, apiKeyId) {
3242
+ const scope = binding.keyScope ?? (binding.apiKeyIds?.length ? "selected" : "all");
3243
+ return scope === "all" || Boolean(binding.apiKeyIds?.includes(apiKeyId));
3244
+ }
3245
+ function wildcardMatches(pattern, value) {
3246
+ const escaped = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join(".*");
3247
+ return new RegExp(`^${escaped}$`, "iu").test(value);
3248
+ }
3249
+ function resolveGatewayModelMapping(mappings, requestedModel) {
3250
+ if (!nonBlank(requestedModel)) return void 0;
3251
+ const wanted = requestedModel.trim();
3252
+ const exact = (mappings ?? []).find(
3253
+ (mapping) => nonBlank(mapping.source) && !mapping.source.includes("*") && mapping.source.trim().toLocaleLowerCase() === wanted.toLocaleLowerCase()
3254
+ );
3255
+ if (exact && nonBlank(exact.target)) return exact.target.trim();
3256
+ const wildcard = (mappings ?? []).find(
3257
+ (mapping) => nonBlank(mapping.source) && mapping.source.includes("*") && wildcardMatches(mapping.source.trim(), wanted)
3258
+ );
3259
+ return wildcard && nonBlank(wildcard.target) ? wildcard.target.trim() : void 0;
3260
+ }
3261
+ function routeCanServe(binding, requestedModel, role) {
3262
+ if (binding.modelMode === "passthrough") return nonBlank(requestedModel);
3263
+ if (binding.modelMappings?.length) {
3264
+ return nonBlank(resolveGatewayModelMapping(binding.modelMappings, requestedModel));
3265
+ }
3266
+ if (isKindMappedEndpoint(binding.endpoint)) {
3267
+ const map = binding.modelMap ?? {};
3268
+ const kind = detectModelKind(binding.endpoint, requestedModel);
3269
+ if (kind && nonBlank(map[kind])) return true;
3270
+ if (binding.endpoint === "messages") {
3271
+ return MESSAGE_FALLBACK_KINDS.some((candidate) => nonBlank(map[candidate]));
3272
+ }
3273
+ return nonBlank(map.codex);
3274
+ }
3275
+ if (binding.endpoint === "chat" && binding.dispatchMode === "prefix") {
3276
+ return resolvePrefixTarget(binding.prefixTargets, requestedModel) !== null;
3277
+ }
3278
+ if (binding.endpoint === "chat") {
3279
+ if (!requestedModel) return false;
3280
+ const wanted = requestedModel.trim().toLowerCase();
3281
+ return (binding.models ?? []).some((ref) => modelIdOf(ref)?.toLowerCase() === wanted);
3282
+ }
3283
+ return role === "background" ? nonBlank(binding.backgroundModel) : nonBlank(binding.defaultModel);
3284
+ }
3285
+ function modelIdOf(ref) {
3286
+ if (!nonBlank(ref)) return void 0;
3287
+ const trimmed = ref.trim();
3288
+ const comma = trimmed.indexOf(",");
3289
+ const modelId = comma >= 0 ? trimmed.slice(comma + 1).trim() : trimmed;
3290
+ return modelId || void 0;
3291
+ }
3292
+ function targetProviderId(target) {
3293
+ return target.providerId.trim();
3294
+ }
3295
+ function targetRef(target, ref) {
3296
+ const modelId = modelIdOf(ref) ?? "";
3297
+ return modelId ? `${targetProviderId(target)},${modelId}` : "";
3298
+ }
3299
+ function targetRefs(target, refs) {
3300
+ return (refs ?? []).map((ref) => targetRef(target, ref)).filter(nonBlank);
3301
+ }
3302
+ function targetPrefixRefs(target, refs) {
3303
+ if (!refs) return void 0;
3304
+ const mapped = {};
3305
+ for (const kind of ["claude", "gpt", "gemini"]) {
3306
+ const ref = targetRef(target, refs[kind]);
3307
+ if (ref) mapped[kind] = ref;
3308
+ }
3309
+ return mapped.claude || mapped.gpt || mapped.gemini ? mapped : void 0;
3310
+ }
3311
+ function applySingleModel(config, binding, targetModel) {
3312
+ const ref = targetRef(binding.target, targetModel);
3313
+ if (!ref) return;
3314
+ if (isKindMappedEndpoint(binding.endpoint)) {
3315
+ config.modelMap = Object.fromEntries(
3316
+ modelKindsForEndpoint(binding.endpoint).map((kind) => [kind, ref])
3317
+ );
3318
+ } else if (binding.endpoint === "chat") {
3319
+ config.models = [ref];
3320
+ config.dispatchMode = "list";
3321
+ } else {
3322
+ config.defaultModel = ref;
3323
+ config.backgroundModel = ref;
3324
+ }
3325
+ }
3326
+ function gatewayBindingToEndpointConfig(binding, requestedModel) {
3327
+ const target = binding.target;
3328
+ const config = {
3329
+ endpoint: binding.endpoint,
3330
+ useSubscription: target.kind !== "provider"
3331
+ };
3332
+ const usesGenericModelHandling = binding.modelMode === "passthrough" || Boolean(binding.modelMappings?.length);
3333
+ const dynamicModel = binding.modelMode === "passthrough" ? requestedModel : resolveGatewayModelMapping(binding.modelMappings, requestedModel);
3334
+ if (usesGenericModelHandling) {
3335
+ if (nonBlank(dynamicModel)) applySingleModel(config, binding, dynamicModel);
3336
+ } else if (isKindMappedEndpoint(binding.endpoint)) {
3337
+ config.modelMap = Object.fromEntries(
3338
+ Object.entries(binding.modelMap ?? {}).map(([kind, ref]) => [kind, targetRef(target, ref)])
3339
+ );
3340
+ } else if (binding.endpoint === "chat") {
3341
+ config.models = targetRefs(target, binding.models);
3342
+ if (binding.dispatchMode === "prefix") {
3343
+ config.dispatchMode = "prefix";
3344
+ config.prefixTargets = targetPrefixRefs(target, binding.prefixTargets);
3345
+ }
3346
+ } else {
3347
+ config.defaultModel = targetRef(target, binding.defaultModel);
3348
+ config.backgroundModel = targetRef(target, binding.backgroundModel);
3349
+ if (binding.backgroundModelIds) config.backgroundModelIds = [...binding.backgroundModelIds];
3350
+ }
3351
+ if (target.kind === "account") {
3352
+ config.boundAccountId = target.accountId;
3353
+ config.boundAccountFallbackPolicy = binding.fallback === "next" ? "pool" : "strict";
3354
+ } else if (target.kind === "account-group") {
3355
+ config.boundAccountGroup = target.group;
3356
+ config.boundAccountFallbackPolicy = binding.fallback === "next" ? "pool" : "strict";
3357
+ } else if (target.kind === "provider" && target.keyId) {
3358
+ config.boundKeyId = target.keyId;
3359
+ config.boundKeyFallbackPolicy = binding.fallback === "next" ? "pool" : "strict";
3360
+ }
3361
+ return config;
3362
+ }
3363
+ function candidateGatewayBindings(bindings, apiKeyId, endpoint) {
3364
+ const candidates = (bindings ?? []).filter(
3365
+ (binding) => binding.enabled && binding.endpoint === endpoint && gatewayBindingAllowsKey(binding, apiKeyId)
3366
+ );
3367
+ const scopeOf = (binding) => binding.keyScope ?? (binding.apiKeyIds?.length ? "selected" : "all");
3368
+ const scoped = candidates.filter((binding) => scopeOf(binding) === "selected");
3369
+ const pool = scoped.length > 0 ? scoped : candidates.filter((binding) => scopeOf(binding) === "all");
3370
+ return pool.sort(
3371
+ (left, right) => (left.priority ?? 100) - (right.priority ?? 100) || left.id.localeCompare(right.id)
3372
+ );
3373
+ }
3374
+ function candidateBackgroundModelIds(bindings, apiKeyId, endpoint) {
3375
+ const ids = [
3376
+ ...new Set(
3377
+ candidateGatewayBindings(bindings, apiKeyId, endpoint).flatMap(
3378
+ (binding) => binding.backgroundModelIds ?? []
3379
+ )
3380
+ )
3381
+ ];
3382
+ return ids.length > 0 ? ids : void 0;
3383
+ }
3384
+ function resolveGatewayBinding(input) {
3385
+ const candidates = candidateGatewayBindings(input.bindings, input.apiKeyId, input.endpoint);
3386
+ const binding = candidates.find(
3387
+ (candidate) => candidate.fallback === "fail" || routeCanServe(candidate, input.requestedModel, input.role)
3388
+ ) ?? candidates[0];
3389
+ if (!binding) return { source: "none" };
3390
+ return {
3391
+ source: "binding",
3392
+ binding,
3393
+ config: gatewayBindingToEndpointConfig(binding, input.requestedModel)
3394
+ };
3395
+ }
3396
+
3397
+ // src/provider-proxy/RouteLeaseTargetResolver.ts
3398
+ function resolveEnvKey(raw) {
3399
+ if (!raw) return "";
3400
+ return raw.startsWith("$") ? process.env[raw.slice(1)] ?? "" : raw;
3401
+ }
3402
+ function providerSupportsModel(provider, model) {
3403
+ const flat = provider.models ?? [];
3404
+ const configured = provider.modelConfigs ?? [];
3405
+ if (flat.length === 0 && configured.length === 0) return false;
3406
+ return flat.includes(model) || configured.some((entry) => entry.id === model && entry.enabled !== false);
3407
+ }
3408
+ function syntheticBinding(request) {
3409
+ const runtime = routeLeaseRuntime(request.runtime);
3410
+ return {
3411
+ id: "route-lease-ephemeral",
3412
+ name: "Route Lease ephemeral target",
3413
+ enabled: true,
3414
+ endpoint: runtime.endpoint,
3415
+ target: request.upstream,
3416
+ fallback: "fail",
3417
+ modelMode: "passthrough"
3418
+ };
3419
+ }
3420
+ function transformerRegistered(llmConfig, name) {
3421
+ return Boolean(llmConfig.getTransformerService()?.getTransformer(name));
3422
+ }
3423
+ var RouteLeaseTargetResolver = class {
3424
+ constructor(llmConfig, options = {}) {
3425
+ this.llmConfig = llmConfig;
3426
+ this.options = options;
3427
+ }
3428
+ llmConfig;
3429
+ options;
3430
+ async resolve(request) {
3431
+ const runtime = routeLeaseRuntime(request.runtime);
3432
+ await this.preflightTarget(request);
3433
+ const binding = syntheticBinding(request);
3434
+ const config = gatewayBindingToEndpointConfig(binding, request.model);
3435
+ const result = await resolveRoute({
3436
+ config,
3437
+ ingressFormat: runtime.ingressFormat,
3438
+ llmConfig: this.llmConfig,
3439
+ requestedModel: request.model,
3440
+ sessionId: request.execution?.sessionIdHash ?? null
3441
+ });
3442
+ if (!result.ok) {
3443
+ throw new RouteLeaseError(
3444
+ result.error.status === 404 ? "upstream_not_found" : "upstream_unavailable",
3445
+ "the selected upstream could not be resolved"
3446
+ );
3447
+ }
3448
+ return { ...result.route, requestedModel: void 0 };
3449
+ }
3450
+ async preflightTarget(request) {
3451
+ const target = request.upstream;
3452
+ if (target.kind !== "provider") {
3453
+ await this.preflightSubscription(target, request.model);
3454
+ return;
3455
+ }
3456
+ const provider = await this.llmConfig.getProvider(target.providerId);
3457
+ if (!provider) throw new RouteLeaseError("upstream_not_found", "provider was not found");
3458
+ if (provider.enabled === false) throw new RouteLeaseError("upstream_unavailable", "provider is disabled");
3459
+ if (!providerSupportsModel(provider, request.model)) {
3460
+ throw new RouteLeaseError("model_not_configured", "model is not configured for the provider");
3461
+ }
3462
+ if (target.keyId) {
3463
+ const availability = await this.options.providerKeys?.getKeyAvailability?.(
3464
+ target.providerId,
3465
+ target.keyId
3466
+ );
3467
+ if (availability === "not-found") {
3468
+ throw new RouteLeaseError("upstream_not_found", "the selected provider key was not found");
3469
+ }
3470
+ const usable = availability === "usable" || availability === void 0 && Boolean(this.options.providerKeys) && await this.options.providerKeys.hasUsableKey(target.providerId, target.keyId);
3471
+ if (!usable) {
3472
+ throw new RouteLeaseError("upstream_unavailable", "the selected provider key is unavailable");
3473
+ }
3474
+ } else {
3475
+ const directKey = resolveEnvKey(provider.api_key);
3476
+ const poolAvailability = await this.options.providerKeys?.getPoolAvailability?.(target.providerId);
3477
+ const poolReady = poolAvailability ? poolAvailability.outcome === "usable" : this.options.providerKeys ? await this.options.providerKeys.hasUsableKeys(target.providerId) : false;
3478
+ if (!directKey && poolAvailability?.outcome === "exhausted") {
3479
+ throw new RouteLeaseError("upstream_exhausted", "provider key pool is temporarily exhausted", {
3480
+ retryAfterSeconds: poolAvailability.retryAfterSeconds
3481
+ });
3482
+ }
3483
+ if (!directKey && !poolReady) {
3484
+ throw new RouteLeaseError("upstream_unavailable", "provider has no usable credential");
3485
+ }
3486
+ }
3487
+ const sameFormat = request.runtime === "claude" && provider.apiFormat === "anthropic" || request.runtime === "codex" && provider.apiFormat === "openai-response";
3488
+ if (!sameFormat) {
3489
+ try {
3490
+ const chain = await resolveProviderChain(this.llmConfig, provider.id, request.model);
3491
+ if (!chain.hasTransformers) {
3492
+ throw new RouteLeaseError("format_unsupported", "no transformer chain supports this runtime and upstream");
3493
+ }
3494
+ } catch (error) {
3495
+ if (error instanceof RouteLeaseError) throw error;
3496
+ throw new RouteLeaseError("format_unsupported", "the transformer chain could not be resolved", { cause: error });
3497
+ }
3498
+ }
3499
+ }
3500
+ async preflightSubscription(target, model) {
3501
+ const registry = getSubscriptionRegistryForOutbound();
3502
+ const profile = registry?.getProfile(target.providerId) ?? null;
3503
+ if (!profile) throw new RouteLeaseError("upstream_not_found", "subscription provider was not found");
3504
+ await this.options.subscriptions?.assertAvailable(target, model);
3505
+ const names = profile.resolveProviderTransformerNames?.(model) ?? profile.providerTransformerNames ?? [];
3506
+ if (names.some((name) => !transformerRegistered(this.llmConfig, name))) {
3507
+ throw new RouteLeaseError("format_unsupported", "a required transformer is unavailable");
3508
+ }
3509
+ if (profile.modelTransformerNames?.some((name) => !transformerRegistered(this.llmConfig, name))) {
3510
+ throw new RouteLeaseError("format_unsupported", "a required model transformer is unavailable");
3511
+ }
3512
+ }
3513
+ };
3514
+
2644
3515
  // src/provider-proxy/index.ts
2645
3516
  var instance = null;
2646
3517
  function getProviderProxy(deps) {
@@ -3255,169 +4126,8 @@ function mergeServerConfig(current, patch) {
3255
4126
  });
3256
4127
  }
3257
4128
 
3258
- // src/outbound-api/gatewayBindingResolver.ts
3259
- var MESSAGE_FALLBACK_KINDS = ["sonnet", "opus", "haiku", "fable"];
3260
- function nonBlank(value) {
3261
- return typeof value === "string" && value.trim() !== "";
3262
- }
3263
- function gatewayBindingAllowsKey(binding, apiKeyId) {
3264
- const scope = binding.keyScope ?? (binding.apiKeyIds?.length ? "selected" : "all");
3265
- return scope === "all" || Boolean(binding.apiKeyIds?.includes(apiKeyId));
3266
- }
3267
- function wildcardMatches(pattern, value) {
3268
- const escaped = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join(".*");
3269
- return new RegExp(`^${escaped}$`, "iu").test(value);
3270
- }
3271
- function resolveGatewayModelMapping(mappings, requestedModel) {
3272
- if (!nonBlank(requestedModel)) return void 0;
3273
- const wanted = requestedModel.trim();
3274
- const exact = (mappings ?? []).find(
3275
- (mapping) => nonBlank(mapping.source) && !mapping.source.includes("*") && mapping.source.trim().toLocaleLowerCase() === wanted.toLocaleLowerCase()
3276
- );
3277
- if (exact && nonBlank(exact.target)) return exact.target.trim();
3278
- const wildcard = (mappings ?? []).find(
3279
- (mapping) => nonBlank(mapping.source) && mapping.source.includes("*") && wildcardMatches(mapping.source.trim(), wanted)
3280
- );
3281
- return wildcard && nonBlank(wildcard.target) ? wildcard.target.trim() : void 0;
3282
- }
3283
- function routeCanServe(binding, requestedModel, role) {
3284
- if (binding.modelMode === "passthrough") return nonBlank(requestedModel);
3285
- if (binding.modelMappings?.length) {
3286
- return nonBlank(resolveGatewayModelMapping(binding.modelMappings, requestedModel));
3287
- }
3288
- if (isKindMappedEndpoint(binding.endpoint)) {
3289
- const map = binding.modelMap ?? {};
3290
- const kind = detectModelKind(binding.endpoint, requestedModel);
3291
- if (kind && nonBlank(map[kind])) return true;
3292
- if (binding.endpoint === "messages") {
3293
- return MESSAGE_FALLBACK_KINDS.some((candidate) => nonBlank(map[candidate]));
3294
- }
3295
- return nonBlank(map.codex);
3296
- }
3297
- if (binding.endpoint === "chat" && binding.dispatchMode === "prefix") {
3298
- return resolvePrefixTarget(binding.prefixTargets, requestedModel) !== null;
3299
- }
3300
- if (binding.endpoint === "chat") {
3301
- if (!requestedModel) return false;
3302
- const wanted = requestedModel.trim().toLowerCase();
3303
- return (binding.models ?? []).some((ref) => modelIdOf(ref)?.toLowerCase() === wanted);
3304
- }
3305
- return role === "background" ? nonBlank(binding.backgroundModel) : nonBlank(binding.defaultModel);
3306
- }
3307
- function modelIdOf(ref) {
3308
- if (!nonBlank(ref)) return void 0;
3309
- const trimmed = ref.trim();
3310
- const comma = trimmed.indexOf(",");
3311
- const modelId = comma >= 0 ? trimmed.slice(comma + 1).trim() : trimmed;
3312
- return modelId || void 0;
3313
- }
3314
- function targetProviderId(target) {
3315
- return target.providerId.trim();
3316
- }
3317
- function targetRef(target, ref) {
3318
- const modelId = modelIdOf(ref) ?? "";
3319
- return modelId ? `${targetProviderId(target)},${modelId}` : "";
3320
- }
3321
- function targetRefs(target, refs) {
3322
- return (refs ?? []).map((ref) => targetRef(target, ref)).filter(nonBlank);
3323
- }
3324
- function targetPrefixRefs(target, refs) {
3325
- if (!refs) return void 0;
3326
- const mapped = {};
3327
- for (const kind of ["claude", "gpt", "gemini"]) {
3328
- const ref = targetRef(target, refs[kind]);
3329
- if (ref) mapped[kind] = ref;
3330
- }
3331
- return mapped.claude || mapped.gpt || mapped.gemini ? mapped : void 0;
3332
- }
3333
- function applySingleModel(config, binding, targetModel) {
3334
- const ref = targetRef(binding.target, targetModel);
3335
- if (!ref) return;
3336
- if (isKindMappedEndpoint(binding.endpoint)) {
3337
- config.modelMap = Object.fromEntries(
3338
- modelKindsForEndpoint(binding.endpoint).map((kind) => [kind, ref])
3339
- );
3340
- } else if (binding.endpoint === "chat") {
3341
- config.models = [ref];
3342
- config.dispatchMode = "list";
3343
- } else {
3344
- config.defaultModel = ref;
3345
- config.backgroundModel = ref;
3346
- }
3347
- }
3348
- function gatewayBindingToEndpointConfig(binding, requestedModel) {
3349
- const target = binding.target;
3350
- const config = {
3351
- endpoint: binding.endpoint,
3352
- useSubscription: target.kind !== "provider"
3353
- };
3354
- const usesGenericModelHandling = binding.modelMode === "passthrough" || Boolean(binding.modelMappings?.length);
3355
- const dynamicModel = binding.modelMode === "passthrough" ? requestedModel : resolveGatewayModelMapping(binding.modelMappings, requestedModel);
3356
- if (usesGenericModelHandling) {
3357
- if (nonBlank(dynamicModel)) applySingleModel(config, binding, dynamicModel);
3358
- } else if (isKindMappedEndpoint(binding.endpoint)) {
3359
- config.modelMap = Object.fromEntries(
3360
- Object.entries(binding.modelMap ?? {}).map(([kind, ref]) => [kind, targetRef(target, ref)])
3361
- );
3362
- } else if (binding.endpoint === "chat") {
3363
- config.models = targetRefs(target, binding.models);
3364
- if (binding.dispatchMode === "prefix") {
3365
- config.dispatchMode = "prefix";
3366
- config.prefixTargets = targetPrefixRefs(target, binding.prefixTargets);
3367
- }
3368
- } else {
3369
- config.defaultModel = targetRef(target, binding.defaultModel);
3370
- config.backgroundModel = targetRef(target, binding.backgroundModel);
3371
- if (binding.backgroundModelIds) config.backgroundModelIds = [...binding.backgroundModelIds];
3372
- }
3373
- if (target.kind === "account") {
3374
- config.boundAccountId = target.accountId;
3375
- config.boundAccountFallbackPolicy = binding.fallback === "next" ? "pool" : "strict";
3376
- } else if (target.kind === "account-group") {
3377
- config.boundAccountGroup = target.group;
3378
- config.boundAccountFallbackPolicy = binding.fallback === "next" ? "pool" : "strict";
3379
- } else if (target.kind === "provider" && target.keyId) {
3380
- config.boundKeyId = target.keyId;
3381
- config.boundKeyFallbackPolicy = binding.fallback === "next" ? "pool" : "strict";
3382
- }
3383
- return config;
3384
- }
3385
- function candidateGatewayBindings(bindings, apiKeyId, endpoint) {
3386
- const candidates = (bindings ?? []).filter(
3387
- (binding) => binding.enabled && binding.endpoint === endpoint && gatewayBindingAllowsKey(binding, apiKeyId)
3388
- );
3389
- const scopeOf = (binding) => binding.keyScope ?? (binding.apiKeyIds?.length ? "selected" : "all");
3390
- const scoped = candidates.filter((binding) => scopeOf(binding) === "selected");
3391
- const pool = scoped.length > 0 ? scoped : candidates.filter((binding) => scopeOf(binding) === "all");
3392
- return pool.sort(
3393
- (left, right) => (left.priority ?? 100) - (right.priority ?? 100) || left.id.localeCompare(right.id)
3394
- );
3395
- }
3396
- function candidateBackgroundModelIds(bindings, apiKeyId, endpoint) {
3397
- const ids = [
3398
- ...new Set(
3399
- candidateGatewayBindings(bindings, apiKeyId, endpoint).flatMap(
3400
- (binding) => binding.backgroundModelIds ?? []
3401
- )
3402
- )
3403
- ];
3404
- return ids.length > 0 ? ids : void 0;
3405
- }
3406
- function resolveGatewayBinding(input) {
3407
- const candidates = candidateGatewayBindings(input.bindings, input.apiKeyId, input.endpoint);
3408
- const binding = candidates.find(
3409
- (candidate) => candidate.fallback === "fail" || routeCanServe(candidate, input.requestedModel, input.role)
3410
- ) ?? candidates[0];
3411
- if (!binding) return { source: "none" };
3412
- return {
3413
- source: "binding",
3414
- binding,
3415
- config: gatewayBindingToEndpointConfig(binding, input.requestedModel)
3416
- };
3417
- }
3418
-
3419
4129
  // src/outbound-api/outboundApiKeyAuth.ts
3420
- import { createHash, randomBytes as randomBytes2 } from "crypto";
4130
+ import { createHash as createHash2, randomBytes as randomBytes4 } from "crypto";
3421
4131
 
3422
4132
  // src/outbound-api/keyPolicy.ts
3423
4133
  var DAY_MS = 864e5;
@@ -3469,7 +4179,7 @@ var BASE62_REJECT_THRESHOLD = 248;
3469
4179
  function randomBase62(count) {
3470
4180
  let out = "";
3471
4181
  while (out.length < count) {
3472
- const buf = randomBytes2(count - out.length + 8);
4182
+ const buf = randomBytes4(count - out.length + 8);
3473
4183
  for (const byte of buf) {
3474
4184
  if (out.length >= count) break;
3475
4185
  if (byte >= BASE62_REJECT_THRESHOLD) continue;
@@ -3482,13 +4192,13 @@ function generateSecret() {
3482
4192
  return KEY_PREFIX + randomBase62(SECRET_BYTES);
3483
4193
  }
3484
4194
  function hashKey(secret) {
3485
- return createHash("sha256").update(secret, "utf8").digest("hex");
4195
+ return createHash2("sha256").update(secret, "utf8").digest("hex");
3486
4196
  }
3487
4197
  function keyPrefix(secret) {
3488
4198
  return secret.slice(0, PREFIX_LEN);
3489
4199
  }
3490
4200
  function newKeyId() {
3491
- return `oak_${Date.now()}_${randomBytes2(6).toString("hex")}`;
4201
+ return `oak_${Date.now()}_${randomBytes4(6).toString("hex")}`;
3492
4202
  }
3493
4203
  async function createNamedKey(db, name) {
3494
4204
  const secret = generateSecret();
@@ -4008,7 +4718,7 @@ var UserMessageSerialQueue = class {
4008
4718
  };
4009
4719
 
4010
4720
  // src/outbound-api/voucher.ts
4011
- import { createHash as createHash2 } from "crypto";
4721
+ import { createHash as createHash3 } from "crypto";
4012
4722
  var CODE_ENTROPY_CHARS = 32;
4013
4723
  var CODE_PREFIX = "CC_";
4014
4724
  var DISPLAY_PREFIX_LEN = 8;
@@ -4017,7 +4727,7 @@ function generateVoucherCode() {
4017
4727
  return CODE_PREFIX + randomBase62(CODE_ENTROPY_CHARS);
4018
4728
  }
4019
4729
  function hashVoucherCode(code) {
4020
- return createHash2("sha256").update(code, "utf8").digest("hex");
4730
+ return createHash3("sha256").update(code, "utf8").digest("hex");
4021
4731
  }
4022
4732
  function voucherCodePrefix(code) {
4023
4733
  return code.slice(0, DISPLAY_PREFIX_LEN);
@@ -6985,6 +7695,37 @@ export {
6985
7695
  extractRouteToken,
6986
7696
  isLoopbackAddress,
6987
7697
  ProviderProxy,
7698
+ ROUTE_LEASE_REQUEST_SCHEMA,
7699
+ ROUTE_LEASE_RESULT_SCHEMA,
7700
+ ROUTE_LEASE_CAPABILITIES_SCHEMA,
7701
+ ROUTE_LEASE_API_VERSION,
7702
+ ROUTE_LEASE_DEFAULT_TTL_SECONDS,
7703
+ ROUTE_LEASE_MAX_TTL_SECONDS,
7704
+ ROUTE_LEASE_MAX_IDEMPOTENCY_BYTES,
7705
+ ROUTE_LEASE_MAX_CONSUMER_BYTES,
7706
+ ROUTE_LEASE_MAX_EXECUTION_ID_BYTES,
7707
+ ROUTE_LEASE_MAX_SESSION_ID_BYTES,
7708
+ ROUTE_LEASE_MAX_MODEL_BYTES,
7709
+ ROUTE_LEASE_SESSION_HASH_DOMAIN,
7710
+ ROUTE_LEASE_CODEX_TOKEN_ENV,
7711
+ ROUTE_LEASE_RUNTIMES,
7712
+ ROUTE_LEASE_CAPABILITIES,
7713
+ ROUTE_LEASE_RUNTIME_TABLE,
7714
+ RouteLeaseError,
7715
+ normalizeRouteLeaseIdempotencyKey,
7716
+ normalizeRouteLeaseTtl,
7717
+ hashRouteLeaseSessionId,
7718
+ canonicalizeRouteLeasePayload,
7719
+ hashRouteLeasePayload,
7720
+ parseRouteLeaseCreate,
7721
+ routeLeaseRuntime,
7722
+ SYSTEM_ROUTE_LEASE_CLOCK,
7723
+ RouteLeaseManager,
7724
+ gatewayBindingToEndpointConfig,
7725
+ candidateGatewayBindings,
7726
+ candidateBackgroundModelIds,
7727
+ resolveGatewayBinding,
7728
+ RouteLeaseTargetResolver,
6988
7729
  getProviderProxy,
6989
7730
  __resetProviderProxyForTests,
6990
7731
  KeyedMutex,
@@ -7013,10 +7754,6 @@ export {
7013
7754
  loadServerConfig,
7014
7755
  saveServerConfig,
7015
7756
  mergeServerConfig,
7016
- gatewayBindingToEndpointConfig,
7017
- candidateGatewayBindings,
7018
- candidateBackgroundModelIds,
7019
- resolveGatewayBinding,
7020
7757
  computeKeyExpiry,
7021
7758
  checkKeyQuota,
7022
7759
  randomBase62,