@omnicross/daemon 0.1.7 → 0.1.9

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.
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  normalizeServerConfig
13
13
  } from "@omnicross/core/outbound-api";
14
14
  import { setSubscriptionRegistryForOutbound } from "@omnicross/core/outbound-api/subscriptionRegistryPort";
15
- import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
15
+ import { getSharedAccountHealth as getSharedAccountHealth4 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
16
16
  import {
17
17
  __resetSharedAccountAllowanceStoreForTests,
18
18
  AccountAllowanceStore as AccountAllowanceStore3,
@@ -20,15 +20,18 @@ import {
20
20
  } from "@omnicross/core/pipeline/AccountAllowanceStore";
21
21
  import {
22
22
  __resetSharedAccountAllowanceSchedulingForTests,
23
- getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4
23
+ getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling5
24
24
  } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
25
25
  import { fetchUpstream as fetchUpstream7, setUpstreamProxyResolver } from "@omnicross/core/pipeline/upstreamFetch";
26
26
  import { __resetSharedIdentityStoreForTests } from "@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore";
27
27
  import { setGeminiCodeAssistResolver } from "@omnicross/core/ports/gemini-code-assist-resolver";
28
28
  import {
29
29
  __resetProviderProxyForTests,
30
- getProviderProxy
30
+ getProviderProxy,
31
+ RouteLeaseManager,
32
+ RouteLeaseTargetResolver
31
33
  } from "@omnicross/core/provider-proxy";
34
+ import { routeLeaseDescriptorPort } from "@omnicross/cli-launcher";
32
35
  import { KeySpendTracker } from "@omnicross/core/outbound-api";
33
36
  import { PricingEngine, UsageRecorder } from "@omnicross/core/usage";
34
37
  import {
@@ -741,6 +744,93 @@ async function handleWebhookTest(req, res) {
741
744
  res.end(JSON.stringify({ result }));
742
745
  }
743
746
 
747
+ // src/admin/routeLeaseApi.ts
748
+ import {
749
+ isLoopbackAddress,
750
+ normalizeRouteLeaseTtl,
751
+ ROUTE_LEASE_CAPABILITIES,
752
+ RouteLeaseError
753
+ } from "@omnicross/core/provider-proxy";
754
+ var MAX_BODY_BYTES = 64 * 1024;
755
+ var SAFE_LEASE_ID = /^[A-Za-z0-9-]{1,128}$/u;
756
+ async function readJson(req) {
757
+ const chunks = [];
758
+ let bytes = 0;
759
+ for await (const chunk of req) {
760
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
761
+ bytes += buffer.length;
762
+ if (bytes > MAX_BODY_BYTES) throw new RouteLeaseError("invalid_request", "request body is too large");
763
+ chunks.push(buffer);
764
+ }
765
+ if (chunks.length === 0) return {};
766
+ try {
767
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
768
+ } catch {
769
+ throw new RouteLeaseError("invalid_request", "request body is not valid JSON");
770
+ }
771
+ }
772
+ function json(res, status, body, noStore = false) {
773
+ res.statusCode = status;
774
+ res.setHeader("Content-Type", "application/json");
775
+ if (noStore) res.setHeader("Cache-Control", "no-store");
776
+ res.end(JSON.stringify(body));
777
+ }
778
+ function leaseId(value) {
779
+ if (!value || !SAFE_LEASE_ID.test(value)) throw new RouteLeaseError("invalid_request", "lease id is invalid");
780
+ return value;
781
+ }
782
+ function header(req, name) {
783
+ const value = req.headers[name.toLowerCase()];
784
+ return Array.isArray(value) ? value[0] : value;
785
+ }
786
+ function writeError(res, error, noStore) {
787
+ const safe = error instanceof RouteLeaseError ? error : new RouteLeaseError("upstream_unavailable", "route lease operation failed safely");
788
+ if (safe.retryAfterSeconds !== void 0) res.setHeader("Retry-After", String(safe.retryAfterSeconds));
789
+ json(res, safe.status, safe.toResponse(), noStore);
790
+ }
791
+ async function handleRouteLeaseApi(req, res, path2, deps) {
792
+ const method = (req.method ?? "GET").toUpperCase();
793
+ const noStore = method === "POST" && (path2 === "/admin/api/route-leases" || path2.endsWith("/renew"));
794
+ try {
795
+ if (!isLoopbackAddress(req.socket.remoteAddress)) {
796
+ throw new RouteLeaseError("control_unauthorized", "route lease control plane is loopback only");
797
+ }
798
+ const manager = deps.routeLeaseManager;
799
+ if (!manager) throw new RouteLeaseError("daemon_not_ready", "route lease manager is unavailable");
800
+ const base = "/admin/api/route-leases";
801
+ const suffix = path2.slice(base.length).replace(/^\/+|\/+$/gu, "");
802
+ const segments = suffix ? suffix.split("/") : [];
803
+ if (segments.length === 1 && segments[0] === "capabilities") {
804
+ if (method !== "GET" && method !== "HEAD") throw new RouteLeaseError("invalid_request", "method is not allowed");
805
+ return json(res, 200, ROUTE_LEASE_CAPABILITIES);
806
+ }
807
+ if (segments.length === 0) {
808
+ if (method === "GET") return json(res, 200, { leases: manager.list() });
809
+ if (method === "POST") {
810
+ const outcome = await manager.createFromRequest(await readJson(req), header(req, "idempotency-key"));
811
+ return json(res, outcome.created ? 201 : 200, outcome.result, true);
812
+ }
813
+ throw new RouteLeaseError("invalid_request", "method is not allowed");
814
+ }
815
+ const id = leaseId(segments[0]);
816
+ if (segments.length === 1) {
817
+ if (method === "GET") return json(res, 200, manager.get(id));
818
+ if (method === "DELETE") return json(res, 200, manager.release(id));
819
+ throw new RouteLeaseError("invalid_request", "method is not allowed");
820
+ }
821
+ if (segments.length === 2 && segments[1] === "renew" && method === "POST") {
822
+ const body = await readJson(req);
823
+ const ttl = normalizeRouteLeaseTtl(
824
+ body && typeof body === "object" && !Array.isArray(body) ? body.ttlSeconds : void 0
825
+ );
826
+ return json(res, 200, manager.renew(id, ttl), true);
827
+ }
828
+ throw new RouteLeaseError("lease_not_found", "route lease endpoint was not found");
829
+ } catch (error) {
830
+ writeError(res, error, noStore);
831
+ }
832
+ }
833
+
744
834
  // src/admin/adminApi.ts
745
835
  import http from "http";
746
836
  import {
@@ -1174,6 +1264,35 @@ function validateApiKeys(raw) {
1174
1264
  }
1175
1265
  return out.length > 0 ? out : void 0;
1176
1266
  }
1267
+ var THINK_LEVELS = /* @__PURE__ */ new Set([
1268
+ "none",
1269
+ "minimal",
1270
+ "low",
1271
+ "medium",
1272
+ "high",
1273
+ "xhigh",
1274
+ "max"
1275
+ ]);
1276
+ function validateThinkingLevels(raw) {
1277
+ if (!Array.isArray(raw)) return void 0;
1278
+ if (!raw.every((level) => typeof level === "string" && THINK_LEVELS.has(level))) {
1279
+ return void 0;
1280
+ }
1281
+ return [...raw];
1282
+ }
1283
+ function validateThinkingTokenLimit(raw) {
1284
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
1285
+ const bounds = raw;
1286
+ const min = bounds["min"];
1287
+ const max = bounds["max"];
1288
+ if (typeof min !== "number" || !Number.isFinite(min) || !Number.isInteger(min) || min < 0) {
1289
+ return void 0;
1290
+ }
1291
+ if (typeof max !== "number" || !Number.isFinite(max) || !Number.isInteger(max) || max < min) {
1292
+ return void 0;
1293
+ }
1294
+ return { min, max };
1295
+ }
1177
1296
  function validateModelConfigs(raw) {
1178
1297
  if (!Array.isArray(raw)) return void 0;
1179
1298
  const out = [];
@@ -1188,6 +1307,10 @@ function validateModelConfigs(raw) {
1188
1307
  if (typeof m["enabled"] === "boolean") entry.enabled = m["enabled"];
1189
1308
  if (typeof m["vision"] === "boolean") entry.vision = m["vision"];
1190
1309
  if (typeof m["reasoning"] === "boolean") entry.reasoning = m["reasoning"];
1310
+ const thinkingLevels = validateThinkingLevels(m["thinkingLevels"]);
1311
+ if (thinkingLevels) entry.thinkingLevels = thinkingLevels;
1312
+ const thinkingTokenLimit = validateThinkingTokenLimit(m["thinkingTokenLimit"]);
1313
+ if (thinkingTokenLimit) entry.thinkingTokenLimit = thinkingTokenLimit;
1191
1314
  out.push(entry);
1192
1315
  }
1193
1316
  return out.length > 0 ? out : void 0;
@@ -2613,7 +2736,9 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
2613
2736
  // src/admin/cliLaunch.ts
2614
2737
  import { exec, spawn } from "child_process";
2615
2738
  import { randomUUID as randomUUID2 } from "crypto";
2616
- import { existsSync as existsSync5 } from "fs";
2739
+ import { chmodSync as chmodSync3, existsSync as existsSync5, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
2740
+ import { createServer } from "net";
2741
+ import { tmpdir } from "os";
2617
2742
  import { delimiter, join as join3 } from "path";
2618
2743
  import {
2619
2744
  buildChatCliLaunchConfig,
@@ -2621,6 +2746,33 @@ import {
2621
2746
  buildCodexLaunchConfig,
2622
2747
  buildGeminiCliLaunchConfig
2623
2748
  } from "@omnicross/cli-launcher";
2749
+ import {
2750
+ ROUTE_LEASE_REQUEST_SCHEMA,
2751
+ RouteLeaseError as RouteLeaseError2
2752
+ } from "@omnicross/core/provider-proxy";
2753
+
2754
+ // src/routeLeaseRenewal.ts
2755
+ var TERMINAL_LEASE_TTL_SECONDS = 600;
2756
+ var TERMINAL_LEASE_RENEW_INTERVAL_MS = 5 * 60 * 1e3;
2757
+ var TERMINAL_LEASE_MAX_LIFETIME_MS = 24 * 60 * 60 * 1e3;
2758
+ function startTerminalLeaseRenewal(manager, leaseId2) {
2759
+ const stopAt = Date.now() + TERMINAL_LEASE_MAX_LIFETIME_MS;
2760
+ const timer = setInterval(() => {
2761
+ if (Date.now() >= stopAt) {
2762
+ clearInterval(timer);
2763
+ return;
2764
+ }
2765
+ try {
2766
+ manager.renew(leaseId2, TERMINAL_LEASE_TTL_SECONDS);
2767
+ } catch {
2768
+ clearInterval(timer);
2769
+ }
2770
+ }, TERMINAL_LEASE_RENEW_INTERVAL_MS);
2771
+ timer.unref?.();
2772
+ return () => clearInterval(timer);
2773
+ }
2774
+
2775
+ // src/admin/cliLaunch.ts
2624
2776
  var LAUNCHABLE_CLIS = [
2625
2777
  { id: "claude", displayName: "Claude Code", command: "claude" },
2626
2778
  { id: "codex", displayName: "Codex CLI", command: "codex" },
@@ -2701,34 +2853,183 @@ async function buildLaunchEnv(cli, llmConfig, target) {
2701
2853
  function shq(s) {
2702
2854
  return `'${s.replace(/'/g, `'\\''`)}'`;
2703
2855
  }
2704
- var defaultTerminalOpener = ({ cli, command, extraArgs, env, cwd, platform }) => {
2856
+ var MAC_TERMINAL_BOOTSTRAP_SOURCE = `
2857
+ 'use strict';
2858
+ const fs = require('node:fs');
2859
+ const net = require('node:net');
2860
+ const { spawn } = require('node:child_process');
2861
+ const [socketPath, launchDir, cwd, command, ...args] = process.argv.slice(2);
2862
+ let payload = '';
2863
+ const socket = net.createConnection(socketPath);
2864
+ socket.setEncoding('utf8');
2865
+ socket.on('data', (chunk) => { payload += chunk; });
2866
+ socket.on('end', () => {
2867
+ const descriptor = JSON.parse(payload);
2868
+ if (!descriptor || Array.isArray(descriptor) || Object.values(descriptor).some((value) => typeof value !== 'string')) {
2869
+ throw new Error('invalid terminal launch descriptor');
2870
+ }
2871
+ try { fs.rmSync(launchDir, { recursive: true, force: true }); } catch {}
2872
+ const child = spawn(command, args, {
2873
+ cwd: cwd || undefined,
2874
+ env: { ...process.env, ...descriptor },
2875
+ stdio: 'inherit',
2876
+ });
2877
+ child.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
2878
+ child.on('exit', (code, signal) => {
2879
+ if (signal) process.kill(process.pid, signal);
2880
+ else process.exitCode = code == null ? 1 : code;
2881
+ });
2882
+ });
2883
+ socket.on('error', (error) => { console.error(error.message); process.exitCode = 1; });
2884
+ `;
2885
+ var MAC_TERMINAL_IPC_TIMEOUT_MS = 12e4;
2886
+ function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }, spawnProcess = spawn, macIpc = {}) {
2705
2887
  const childEnv = { ...process.env, ...env };
2706
2888
  if (platform === "win32") {
2707
2889
  const args = ["/c", "start", `"omnicross ${cli}"`];
2708
2890
  if (cwd) args.push("/D", `"${cwd}"`);
2709
2891
  args.push("cmd", "/k", command, ...extraArgs);
2710
- spawn(process.env["ComSpec"] || "cmd.exe", args, {
2892
+ spawnProcess(process.env["ComSpec"] || "cmd.exe", args, {
2711
2893
  env: childEnv,
2712
2894
  windowsVerbatimArguments: true,
2713
2895
  detached: true,
2714
2896
  stdio: "ignore"
2715
2897
  }).unref();
2716
- return;
2898
+ return () => {
2899
+ };
2717
2900
  }
2718
- const exportLine = Object.entries(env).map(([k, v]) => `export ${k}=${shq(v)}`).join("; ");
2719
2901
  const runLine = [command, ...extraArgs].map(shq).join(" ");
2720
- const script = `${exportLine}; ${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
2902
+ const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
2721
2903
  if (platform === "darwin") {
2722
- const osa = `tell application "Terminal" to do script ${JSON.stringify(script)}`;
2723
- spawn("osascript", ["-e", osa], { detached: true, stdio: "ignore" }).unref();
2724
- return;
2904
+ const launchDir = mkdtempSync(join3(tmpdir(), "omnicross-terminal-"));
2905
+ const commandFile = join3(launchDir, "launch.command");
2906
+ const bootstrapFile = join3(launchDir, "bootstrap.cjs");
2907
+ const socketPath = macIpc.socketPath ?? join3(launchDir, "descriptor.sock");
2908
+ const openerEnv = { ...process.env };
2909
+ for (const key of Object.keys(env)) delete openerEnv[key];
2910
+ let claimed = false;
2911
+ let cleaned = false;
2912
+ let failureNotified = false;
2913
+ let timer;
2914
+ const notifyFailure = () => {
2915
+ cleanup();
2916
+ if (failureNotified) return;
2917
+ failureNotified = true;
2918
+ try {
2919
+ onFailure?.();
2920
+ } catch {
2921
+ }
2922
+ };
2923
+ const handleLaunchFailure = () => {
2924
+ if (claimed) cleanup();
2925
+ else notifyFailure();
2926
+ };
2927
+ const sockets = /* @__PURE__ */ new Set();
2928
+ const server = createServer((socket) => {
2929
+ socket.unref();
2930
+ sockets.add(socket);
2931
+ socket.once("close", () => sockets.delete(socket));
2932
+ try {
2933
+ macIpc.onAccepted?.(socket);
2934
+ } catch {
2935
+ cleanup();
2936
+ return;
2937
+ }
2938
+ if (claimed || cleaned) {
2939
+ socket.destroy();
2940
+ return;
2941
+ }
2942
+ claimed = true;
2943
+ try {
2944
+ macIpc.onClaimed?.();
2945
+ if (cleaned) return;
2946
+ socket.end(JSON.stringify(env), cleanup);
2947
+ } catch {
2948
+ cleanup();
2949
+ }
2950
+ });
2951
+ const cleanup = () => {
2952
+ if (!cleaned) {
2953
+ cleaned = true;
2954
+ if (timer) clearTimeout(timer);
2955
+ for (const socket of sockets) socket.destroy();
2956
+ sockets.clear();
2957
+ try {
2958
+ server.close();
2959
+ } catch {
2960
+ }
2961
+ }
2962
+ try {
2963
+ if (macIpc.removeArtifacts) {
2964
+ macIpc.removeArtifacts(launchDir);
2965
+ } else {
2966
+ rmSync2(launchDir, {
2967
+ recursive: true,
2968
+ force: true,
2969
+ maxRetries: 3,
2970
+ retryDelay: 20
2971
+ });
2972
+ }
2973
+ } catch {
2974
+ }
2975
+ };
2976
+ try {
2977
+ writeFileSync5(bootstrapFile, MAC_TERMINAL_BOOTSTRAP_SOURCE, { encoding: "utf8", mode: 448 });
2978
+ writeFileSync5(commandFile, `#!/bin/bash
2979
+ rm -f -- "$0"
2980
+ exec ${shq(process.execPath)} ${shq(bootstrapFile)} ${shq(socketPath)} ${shq(launchDir)} ${shq(cwd ?? "")} ${runLine}
2981
+ `, {
2982
+ encoding: "utf8",
2983
+ mode: 448
2984
+ });
2985
+ chmodSync3(commandFile, 448);
2986
+ chmodSync3(bootstrapFile, 448);
2987
+ server.once("error", handleLaunchFailure);
2988
+ server.listen(socketPath, () => {
2989
+ if (cleaned) return;
2990
+ try {
2991
+ macIpc.onListening?.();
2992
+ if (cleaned) return;
2993
+ if (process.platform !== "win32") chmodSync3(socketPath, 384);
2994
+ const opener = spawnProcess("open", ["-n", "-a", "Terminal", commandFile], {
2995
+ env: openerEnv,
2996
+ detached: true,
2997
+ stdio: "ignore"
2998
+ });
2999
+ opener.once("error", handleLaunchFailure);
3000
+ opener.unref();
3001
+ server.unref();
3002
+ } catch {
3003
+ handleLaunchFailure();
3004
+ }
3005
+ });
3006
+ timer = setTimeout(handleLaunchFailure, macIpc.timeoutMs ?? MAC_TERMINAL_IPC_TIMEOUT_MS);
3007
+ timer.unref?.();
3008
+ return cleanup;
3009
+ } catch (error) {
3010
+ cleanup();
3011
+ throw error;
3012
+ }
2725
3013
  }
2726
- spawn("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
3014
+ spawnProcess("x-terminal-emulator", ["-e", "bash", "-lc", `${script}; exec bash`], {
3015
+ env: childEnv,
2727
3016
  detached: true,
2728
3017
  stdio: "ignore"
2729
3018
  }).unref();
2730
- };
3019
+ return () => {
3020
+ };
3021
+ }
3022
+ var defaultTerminalOpener = (input) => openTerminal(input);
2731
3023
  var sessions = /* @__PURE__ */ new Map();
3024
+ function resetCliSessions() {
3025
+ for (const s of sessions.values()) {
3026
+ try {
3027
+ s.onSessionEnd();
3028
+ } catch {
3029
+ }
3030
+ }
3031
+ sessions.clear();
3032
+ }
2732
3033
  function errBody(message) {
2733
3034
  return { error: { type: "admin_api_error", message } };
2734
3035
  }
@@ -2783,29 +3084,81 @@ async function handleCliLaunch(cli, body, ctx) {
2783
3084
  } catch (err5) {
2784
3085
  return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "no launch target") };
2785
3086
  }
3087
+ const id = randomUUID2();
3088
+ let leaseId2;
2786
3089
  let launch;
2787
3090
  try {
2788
- launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
3091
+ if ((cli === "claude" || cli === "codex") && ctx.routeLeaseManager) {
3092
+ const outcome = await ctx.routeLeaseManager.createFromRequest({
3093
+ schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA,
3094
+ consumer: "omnicross-terminal",
3095
+ runtime: cli,
3096
+ upstream: { kind: "provider", providerId: target.providerId },
3097
+ model: target.model,
3098
+ execution: { sessionId: id }
3099
+ }, `omnicross-terminal:${id}`);
3100
+ leaseId2 = outcome.result.leaseId;
3101
+ const stopRenewal = startTerminalLeaseRenewal(ctx.routeLeaseManager, leaseId2);
3102
+ launch = {
3103
+ env: outcome.result.launch.env,
3104
+ extraArgs: outcome.result.launch.extraArgs,
3105
+ onSessionEnd: () => {
3106
+ stopRenewal();
3107
+ ctx.routeLeaseManager?.release(outcome.result.leaseId);
3108
+ }
3109
+ };
3110
+ } else {
3111
+ launch = await buildLaunchEnv(cli, ctx.llmConfig, target);
3112
+ }
2789
3113
  } catch (err5) {
2790
- return { status: 400, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
3114
+ const status = err5 instanceof RouteLeaseError2 ? err5.status : 400;
3115
+ return { status, body: errBody(err5 instanceof Error ? err5.message : "failed to build launch env") };
2791
3116
  }
2792
3117
  const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
2793
3118
  const opener = ctx.opener ?? defaultTerminalOpener;
3119
+ let openerCleanup;
3120
+ let ended = false;
3121
+ let published = false;
3122
+ const onSessionEnd = () => {
3123
+ if (ended) return;
3124
+ ended = true;
3125
+ if (published) sessions.delete(id);
3126
+ try {
3127
+ openerCleanup?.();
3128
+ } finally {
3129
+ launch.onSessionEnd();
3130
+ }
3131
+ };
2794
3132
  try {
2795
- opener({ cli, command: meta.command, extraArgs: launch.extraArgs ?? [], env: launch.env, cwd, platform });
3133
+ const cleanup = opener({
3134
+ cli,
3135
+ command: meta.command,
3136
+ extraArgs: launch.extraArgs ?? [],
3137
+ env: launch.env,
3138
+ cwd,
3139
+ platform,
3140
+ onFailure: onSessionEnd
3141
+ });
3142
+ if (cleanup) openerCleanup = cleanup;
2796
3143
  } catch (err5) {
2797
- launch.onSessionEnd();
3144
+ onSessionEnd();
2798
3145
  return { status: 500, body: errBody(err5 instanceof Error ? err5.message : "failed to open terminal") };
2799
3146
  }
2800
- const id = randomUUID2();
3147
+ if (ended) {
3148
+ openerCleanup?.();
3149
+ return { status: 500, body: errBody("failed to open terminal") };
3150
+ }
2801
3151
  sessions.set(id, {
2802
3152
  id,
2803
3153
  cli,
2804
3154
  providerId: target.providerId,
2805
3155
  model: target.model,
3156
+ ...leaseId2 ? { leaseId: leaseId2 } : {},
2806
3157
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2807
- onSessionEnd: launch.onSessionEnd
3158
+ onSessionEnd
2808
3159
  });
3160
+ published = true;
3161
+ if (ended) sessions.delete(id);
2809
3162
  return { status: 200, body: { sessionId: id, providerId: target.providerId, model: target.model } };
2810
3163
  }
2811
3164
 
@@ -3707,7 +4060,7 @@ function sealPack(bundleJson, passphrase) {
3707
4060
  cipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
3708
4061
  const ciphertext = Buffer.concat([cipher.update(bundleJson, "utf8"), cipher.final()]);
3709
4062
  const tag = cipher.getAuthTag();
3710
- const header = {
4063
+ const header2 = {
3711
4064
  magic: PACK_MAGIC,
3712
4065
  v: PACK_VERSION,
3713
4066
  kdf: KDF_ALGORITHM,
@@ -3718,7 +4071,7 @@ function sealPack(bundleJson, passphrase) {
3718
4071
  iv: iv.toString("base64"),
3719
4072
  tag: tag.toString("base64")
3720
4073
  };
3721
- return `${PACK_PREFIX}${toB64Url(JSON.stringify(header))}.${ciphertext.toString("base64")}`;
4074
+ return `${PACK_PREFIX}${toB64Url(JSON.stringify(header2))}.${ciphertext.toString("base64")}`;
3722
4075
  }
3723
4076
  function parsePack(packString) {
3724
4077
  if (typeof packString !== "string" || !packString.startsWith(PACK_PREFIX)) {
@@ -3729,28 +4082,28 @@ function parsePack(packString) {
3729
4082
  if (dot < 0) throw new PackAuthError("migration pack is malformed (missing body)");
3730
4083
  const headerB64Url = rest.slice(0, dot);
3731
4084
  const ctB64 = rest.slice(dot + 1);
3732
- let header;
4085
+ let header2;
3733
4086
  try {
3734
- header = JSON.parse(fromB64Url(headerB64Url));
4087
+ header2 = JSON.parse(fromB64Url(headerB64Url));
3735
4088
  } catch {
3736
4089
  throw new PackAuthError("migration pack is malformed (unreadable header)");
3737
4090
  }
3738
- if (!header || header.magic !== PACK_MAGIC || header.v !== PACK_VERSION || header.kdf !== KDF_ALGORITHM || typeof header.salt !== "string" || typeof header.iv !== "string" || typeof header.tag !== "string" || typeof header.N !== "number" || typeof header.r !== "number" || typeof header.p !== "number") {
4091
+ if (!header2 || header2.magic !== PACK_MAGIC || header2.v !== PACK_VERSION || header2.kdf !== KDF_ALGORITHM || typeof header2.salt !== "string" || typeof header2.iv !== "string" || typeof header2.tag !== "string" || typeof header2.N !== "number" || typeof header2.r !== "number" || typeof header2.p !== "number") {
3739
4092
  throw new PackAuthError("migration pack is malformed (unsupported header)");
3740
4093
  }
3741
4094
  const ciphertext = Buffer.from(ctB64, "base64");
3742
- return { header, ciphertext };
4095
+ return { header: header2, ciphertext };
3743
4096
  }
3744
4097
  function openPack(packString, passphrase) {
3745
4098
  assertPassphraseStrength(passphrase);
3746
- const { header, ciphertext } = parsePack(packString);
3747
- const salt = Buffer.from(header.salt, "base64");
3748
- const iv = Buffer.from(header.iv, "base64");
3749
- const tag = Buffer.from(header.tag, "base64");
4099
+ const { header: header2, ciphertext } = parsePack(packString);
4100
+ const salt = Buffer.from(header2.salt, "base64");
4101
+ const iv = Buffer.from(header2.iv, "base64");
4102
+ const tag = Buffer.from(header2.tag, "base64");
3750
4103
  if (iv.length !== IV_BYTES2 || tag.length !== TAG_BYTES2) {
3751
4104
  throw new PackAuthError("migration pack is malformed (invalid iv/tag length)");
3752
4105
  }
3753
- const key = deriveKey(passphrase, salt, header.N, header.r, header.p);
4106
+ const key = deriveKey(passphrase, salt, header2.N, header2.r, header2.p);
3754
4107
  const decipher = createDecipheriv2("aes-256-gcm", key, iv);
3755
4108
  decipher.setAAD(aadFor(PACK_MAGIC, PACK_VERSION, KDF_ALGORITHM));
3756
4109
  decipher.setAuthTag(tag);
@@ -4086,10 +4439,10 @@ function writeJson2(res, status, body) {
4086
4439
  res.writeHead(status, { "Content-Type": "application/json" });
4087
4440
  res.end(JSON.stringify(body));
4088
4441
  }
4089
- function writeError(res, status, message) {
4442
+ function writeError2(res, status, message) {
4090
4443
  writeJson2(res, status, { error: { type: "account_allowance_error", message } });
4091
4444
  }
4092
- function readJson(req) {
4445
+ function readJson2(req) {
4093
4446
  return new Promise((resolve2, reject) => {
4094
4447
  const chunks = [];
4095
4448
  req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
@@ -4115,10 +4468,10 @@ function allowanceProvider(value) {
4115
4468
  return value === "claude" || value === "codex" ? value : null;
4116
4469
  }
4117
4470
  async function handleAccountAllowanceApi(req, res, method, rest, service) {
4118
- if (!service) return writeError(res, 501, "account allowance service is not available");
4471
+ if (!service) return writeError2(res, 501, "account allowance service is not available");
4119
4472
  if (method === "GET" && rest.length === 1 && rest[0] === "scheduling") {
4120
4473
  if (!service.getSchedulingStatus) {
4121
- return writeError(res, 501, "allowance scheduling diagnostics are not available");
4474
+ return writeError2(res, 501, "allowance scheduling diagnostics are not available");
4122
4475
  }
4123
4476
  return writeJson2(res, 200, { scheduling: service.getSchedulingStatus() });
4124
4477
  }
@@ -4126,27 +4479,27 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
4126
4479
  const params = query(req);
4127
4480
  const pathProvider = rest.length >= 2 ? rest[0] : null;
4128
4481
  const providerId = allowanceProvider(pathProvider ?? params.get("providerId") ?? params.get("provider"));
4129
- if (providerId === null) return writeError(res, 400, "providerId must be claude or codex");
4482
+ if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
4130
4483
  const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
4131
4484
  const allowances = await service.list({ providerId, accountId });
4132
4485
  return writeJson2(res, 200, { allowances });
4133
4486
  }
4134
4487
  if (method === "POST" && rest[0] === "refresh") {
4135
- const body = await readJson(req);
4488
+ const body = await readJson2(req);
4136
4489
  const requestedProvider = allowanceProvider(
4137
4490
  typeof body["providerId"] === "string" ? body["providerId"] : "claude"
4138
4491
  );
4139
4492
  if (requestedProvider !== "claude") {
4140
- return writeError(res, 400, "only Claude allowances support explicit refresh");
4493
+ return writeError2(res, 400, "only Claude allowances support explicit refresh");
4141
4494
  }
4142
4495
  const accountId = typeof body["accountId"] === "string" && body["accountId"].trim() ? body["accountId"].trim() : void 0;
4143
4496
  const allowances = await service.refreshClaude(accountId);
4144
4497
  if (accountId && allowances.length === 0) {
4145
- return writeError(res, 404, `Claude account '${accountId}' not found`);
4498
+ return writeError2(res, 404, `Claude account '${accountId}' not found`);
4146
4499
  }
4147
4500
  return writeJson2(res, 200, { allowances });
4148
4501
  }
4149
- return writeError(res, 405, `method ${method} not allowed on account allowances`);
4502
+ return writeError2(res, 405, `method ${method} not allowed on account allowances`);
4150
4503
  }
4151
4504
 
4152
4505
  // src/admin/adminApi.ts
@@ -4724,6 +5077,12 @@ function parseModelConfigsInput(raw, existing) {
4724
5077
  else if (typeof prior?.vision === "boolean") entry.vision = prior.vision;
4725
5078
  if (typeof m["reasoning"] === "boolean") entry.reasoning = m["reasoning"];
4726
5079
  else if (typeof prior?.reasoning === "boolean") entry.reasoning = prior.reasoning;
5080
+ const thinkingLevels = validateThinkingLevels(m["thinkingLevels"]);
5081
+ if (thinkingLevels) entry.thinkingLevels = thinkingLevels;
5082
+ else if (prior?.thinkingLevels) entry.thinkingLevels = prior.thinkingLevels;
5083
+ const thinkingTokenLimit = validateThinkingTokenLimit(m["thinkingTokenLimit"]);
5084
+ if (thinkingTokenLimit) entry.thinkingTokenLimit = thinkingTokenLimit;
5085
+ else if (prior?.thinkingTokenLimit) entry.thinkingTokenLimit = prior.thinkingTokenLimit;
4727
5086
  out.push(entry);
4728
5087
  }
4729
5088
  return out.length > 0 ? out : void 0;
@@ -5381,6 +5740,7 @@ async function handleCli(req, res, method, rest, deps) {
5381
5740
  const result = await handleCliLaunch(cli, body, {
5382
5741
  llmConfig: deps.llmConfig,
5383
5742
  providers,
5743
+ routeLeaseManager: deps.routeLeaseManager,
5384
5744
  opener: deps.cliTerminalOpener,
5385
5745
  probe: deps.cliPathProbe
5386
5746
  });
@@ -5643,7 +6003,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
5643
6003
  }
5644
6004
 
5645
6005
  // src/admin/version.ts
5646
- var DAEMON_VERSION = true ? "0.1.7" : "0.0.0-dev";
6006
+ var DAEMON_VERSION = true ? "0.1.9" : "0.0.0-dev";
5647
6007
 
5648
6008
  // src/admin/AdminServer.ts
5649
6009
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -5763,6 +6123,10 @@ var AdminServer = class {
5763
6123
  await handleWebhookTest(req, res);
5764
6124
  return;
5765
6125
  }
6126
+ if (path2 === "/admin/api/route-leases" || path2.startsWith("/admin/api/route-leases/")) {
6127
+ await handleRouteLeaseApi(req, res, path2, this.deps);
6128
+ return;
6129
+ }
5766
6130
  if (path2.startsWith("/admin/api/")) {
5767
6131
  await handleAdminApi(req, res, path2, this.deps);
5768
6132
  return;
@@ -5774,8 +6138,8 @@ var AdminServer = class {
5774
6138
  }
5775
6139
  /** Constant-time bearer/header check against the configured token. */
5776
6140
  isAuthorized(req, token) {
5777
- const header = req.headers["authorization"];
5778
- const bearer = typeof header === "string" && header.startsWith("Bearer ") ? header.slice("Bearer ".length).trim() : void 0;
6141
+ const header2 = req.headers["authorization"];
6142
+ const bearer = typeof header2 === "string" && header2.startsWith("Bearer ") ? header2.slice("Bearer ".length).trim() : void 0;
5779
6143
  const xToken = req.headers["x-admin-token"];
5780
6144
  const presented = bearer ?? (typeof xToken === "string" ? xToken.trim() : void 0);
5781
6145
  return constantTimeEquals(presented, token);
@@ -5910,7 +6274,7 @@ var OAuthSessionStore = class {
5910
6274
  };
5911
6275
 
5912
6276
  // src/commands/loopbackCallback.ts
5913
- import { createServer } from "http";
6277
+ import { createServer as createServer2 } from "http";
5914
6278
  var LOOPBACK_HOST = "127.0.0.1";
5915
6279
  var LOOPBACK_PORT = 1455;
5916
6280
  var CALLBACK_PATH = "/auth/callback";
@@ -5932,7 +6296,7 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
5932
6296
  fn();
5933
6297
  server2.close();
5934
6298
  };
5935
- const server = createServer((req, res) => {
6299
+ const server = createServer2((req, res) => {
5936
6300
  const url = new URL(req.url ?? "", `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}`);
5937
6301
  if (url.pathname !== CALLBACK_PATH) {
5938
6302
  res.writeHead(404, HTML_HEADERS);
@@ -6210,6 +6574,15 @@ function toLLMProvider(row) {
6210
6574
  api_base_url: row.baseUrl,
6211
6575
  api_key: resolvePreferredApiKey(row),
6212
6576
  models,
6577
+ modelConfigs: row.modelConfigs?.map((config) => ({
6578
+ id: config.id,
6579
+ name: config.name ?? config.id,
6580
+ enabled: config.enabled ?? true,
6581
+ vision: config.vision,
6582
+ reasoning: config.reasoning,
6583
+ thinkingLevels: config.thinkingLevels,
6584
+ thinkingTokenLimit: config.thinkingTokenLimit
6585
+ })),
6213
6586
  enabled: true,
6214
6587
  transformer,
6215
6588
  // app-parity-2 child 3: POPULATE the coding-plan endpoint onto the core
@@ -6381,7 +6754,7 @@ function safeStringify(value) {
6381
6754
  }
6382
6755
 
6383
6756
  // src/ports/JsonApiServerSettingsStore.ts
6384
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
6757
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
6385
6758
  import { OUTBOUND_API_SERVER_CONFIG_KEY } from "@omnicross/core/outbound-api";
6386
6759
  var JsonApiServerSettingsStore = class {
6387
6760
  /**
@@ -6408,7 +6781,7 @@ var JsonApiServerSettingsStore = class {
6408
6781
  if (key !== OUTBOUND_API_SERVER_CONFIG_KEY) return;
6409
6782
  const file = this.readFile();
6410
6783
  file.server = this.encryptSecrets(value);
6411
- writeFileSync5(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
6784
+ writeFileSync6(this.configPath, JSON.stringify(file, null, 2) + "\n", "utf8");
6412
6785
  }
6413
6786
  /** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
6414
6787
  encryptSecrets(config) {
@@ -6762,7 +7135,7 @@ function median(values) {
6762
7135
  }
6763
7136
 
6764
7137
  // src/ports/JsonOutboundKeyDb.ts
6765
- import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
7138
+ import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
6766
7139
  var JsonOutboundKeyDb = class {
6767
7140
  /**
6768
7141
  * @param secretBox OPTIONAL reversible-secret codec. When present, a created
@@ -6904,7 +7277,7 @@ var JsonOutboundKeyDb = class {
6904
7277
  }
6905
7278
  }
6906
7279
  writeRows(rows) {
6907
- writeFileSync6(this.keysPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
7280
+ writeFileSync7(this.keysPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
6908
7281
  }
6909
7282
  };
6910
7283
  function applyPolicyField(row, field, value) {
@@ -6914,7 +7287,7 @@ function applyPolicyField(row, field, value) {
6914
7287
  }
6915
7288
 
6916
7289
  // src/ports/JsonPricingStore.ts
6917
- import { existsSync as existsSync9, readFileSync as readFileSync9, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
7290
+ import { existsSync as existsSync9, readFileSync as readFileSync9, renameSync as renameSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
6918
7291
  import { randomUUID as randomUUID5 } from "crypto";
6919
7292
  var JsonPricingStore = class {
6920
7293
  constructor(pricingPath) {
@@ -7055,13 +7428,13 @@ var JsonPricingStore = class {
7055
7428
  writeRows(rows) {
7056
7429
  const temporaryPath = `${this.pricingPath}.${process.pid}.${randomUUID5()}.tmp`;
7057
7430
  try {
7058
- writeFileSync7(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
7431
+ writeFileSync8(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
7059
7432
  encoding: "utf8",
7060
7433
  flag: "wx"
7061
7434
  });
7062
7435
  this.replaceFile(temporaryPath);
7063
7436
  } finally {
7064
- rmSync2(temporaryPath, { force: true });
7437
+ rmSync3(temporaryPath, { force: true });
7065
7438
  }
7066
7439
  }
7067
7440
  /** Isolated for deterministic failure testing; never removes the target. */
@@ -7076,7 +7449,7 @@ function isUsablePricingRow(value) {
7076
7449
  }
7077
7450
 
7078
7451
  // src/pricing/PricingRefreshScheduler.ts
7079
- import { existsSync as existsSync10, readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as writeFileSync8 } from "fs";
7452
+ import { existsSync as existsSync10, readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as writeFileSync9 } from "fs";
7080
7453
  var EMPTY_STATE2 = {
7081
7454
  lastAttemptAt: null,
7082
7455
  lastSuccessAt: null,
@@ -7169,7 +7542,7 @@ var PricingRefreshScheduler = class {
7169
7542
  }
7170
7543
  writeState(state) {
7171
7544
  const temporaryPath = `${this.statePath}.tmp`;
7172
- writeFileSync8(temporaryPath, `${JSON.stringify(state, null, 2)}
7545
+ writeFileSync9(temporaryPath, `${JSON.stringify(state, null, 2)}
7173
7546
  `, "utf8");
7174
7547
  renameSync4(temporaryPath, this.statePath);
7175
7548
  }
@@ -7179,7 +7552,7 @@ function finiteOrNull(value) {
7179
7552
  }
7180
7553
 
7181
7554
  // src/ports/JsonVoucherDb.ts
7182
- import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
7555
+ import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
7183
7556
  var JsonVoucherDb = class {
7184
7557
  constructor(vouchersPath) {
7185
7558
  this.vouchersPath = vouchersPath;
@@ -7266,12 +7639,12 @@ var JsonVoucherDb = class {
7266
7639
  }
7267
7640
  }
7268
7641
  writeRows(rows) {
7269
- writeFileSync9(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
7642
+ writeFileSync10(this.vouchersPath, JSON.stringify(rows, null, 2) + "\n", "utf8");
7270
7643
  }
7271
7644
  };
7272
7645
 
7273
7646
  // src/ports/JsonSubscriptionCredentialStore.ts
7274
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
7647
+ import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
7275
7648
  import { dirname as dirname6 } from "path";
7276
7649
  import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
7277
7650
  import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling3 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
@@ -7954,7 +8327,7 @@ var JsonSubscriptionCredentialStore = class {
7954
8327
  persist(config) {
7955
8328
  mkdirSync4(dirname6(this.tokensPath), { recursive: true });
7956
8329
  const encrypted = encryptTokens(config, this.box);
7957
- writeFileSync10(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
8330
+ writeFileSync11(this.tokensPath, JSON.stringify(encrypted, null, 2) + "\n", "utf8");
7958
8331
  }
7959
8332
  /**
7960
8333
  * Read + parse `tokens.json`, tolerating a missing/corrupt file, then DECRYPT
@@ -8523,7 +8896,7 @@ import {
8523
8896
  readFileSync as readFileSync14,
8524
8897
  readdirSync,
8525
8898
  statSync as statSync3,
8526
- writeFileSync as writeFileSync11
8899
+ writeFileSync as writeFileSync12
8527
8900
  } from "fs";
8528
8901
  import { basename, dirname as dirname7, join as join6 } from "path";
8529
8902
  var SIDECAR_VERSION = 1;
@@ -8565,7 +8938,7 @@ function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfte
8565
8938
  minTs: previous.minTs === null ? record.ts : Math.min(previous.minTs, record.ts),
8566
8939
  maxTs: previous.maxTs === null ? record.ts : Math.max(previous.maxTs, record.ts)
8567
8940
  };
8568
- writeFileSync11(statsPath, JSON.stringify(next), "utf8");
8941
+ writeFileSync12(statsPath, JSON.stringify(next), "utf8");
8569
8942
  }
8570
8943
  function queryCovers(stats, from, to) {
8571
8944
  return stats.requestCount === 0 || stats.minTs !== null && stats.maxTs !== null && from <= stats.minTs && to >= stats.maxTs;
@@ -8709,7 +9082,7 @@ async function readAuditStats(auditDir, query2 = {}) {
8709
9082
  total.errorCount += scanned.filtered.errorCount + (resumable?.errorCount ?? 0);
8710
9083
  total.complete = total.complete && scanned.filtered.complete;
8711
9084
  const current = resumable ? mergePersistedStats(resumable, scanned.all) : scanned.all;
8712
- if (current.complete) writeFileSync11(statsPath, JSON.stringify(current), "utf8");
9085
+ if (current.complete) writeFileSync12(statsPath, JSON.stringify(current), "utf8");
8713
9086
  } catch {
8714
9087
  total.complete = false;
8715
9088
  }
@@ -9271,6 +9644,78 @@ var TokenRefreshScheduler = class {
9271
9644
  }
9272
9645
  };
9273
9646
 
9647
+ // src/routeLeaseSubscriptionPreflight.ts
9648
+ import {
9649
+ RouteLeaseError as RouteLeaseError3
9650
+ } from "@omnicross/core/provider-proxy";
9651
+ import { getSharedAccountAllowanceScheduling as getSharedAccountAllowanceScheduling4 } from "@omnicross/core/pipeline/AccountAllowanceScheduling";
9652
+ import { getSharedAccountHealth as getSharedAccountHealth3 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
9653
+ import { accountSupportsModel } from "@omnicross/subscriptions/scheduler/accountModelMap";
9654
+ var PROVIDERS = /* @__PURE__ */ new Set(["claude", "codex", "gemini", "opencodego"]);
9655
+ function accountArray(config, providerId) {
9656
+ const record = config;
9657
+ const key = `${providerId}Accounts`;
9658
+ const accounts = record[key];
9659
+ if (Array.isArray(accounts)) return accounts;
9660
+ const legacy = record[providerId];
9661
+ if (!legacy || typeof legacy !== "object") return [];
9662
+ const activeKey = `active${providerId[0].toUpperCase()}${providerId.slice(1)}AccountId`;
9663
+ return [{ id: String(record[activeKey] ?? "active"), enabled: true, tokens: legacy }];
9664
+ }
9665
+ function hasCredential(providerId, account) {
9666
+ const tokens = account.tokens;
9667
+ if (providerId === "opencodego") return typeof tokens.apiKey === "string" && tokens.apiKey.length > 0;
9668
+ return typeof tokens.accessToken === "string" && tokens.accessToken.length > 0;
9669
+ }
9670
+ function safeProviderId(value) {
9671
+ if (!PROVIDERS.has(value)) {
9672
+ throw new RouteLeaseError3("upstream_not_found", "subscription provider was not found");
9673
+ }
9674
+ return value;
9675
+ }
9676
+ function createRouteLeaseSubscriptionPreflight(credentials) {
9677
+ return {
9678
+ async assertAvailable(upstream, model) {
9679
+ const providerId = safeProviderId(upstream.providerId);
9680
+ const config = await credentials.getFullConfig();
9681
+ const all = accountArray(config, providerId);
9682
+ if (all.length === 0) {
9683
+ throw new RouteLeaseError3("upstream_unavailable", "subscription provider has no configured account");
9684
+ }
9685
+ let bounded = all;
9686
+ if (upstream.kind === "account") {
9687
+ bounded = all.filter((account) => account.id === upstream.accountId);
9688
+ } else if (upstream.kind === "account-group") {
9689
+ bounded = all.filter((account) => account.group?.trim() === upstream.group);
9690
+ }
9691
+ if (bounded.length === 0) {
9692
+ throw new RouteLeaseError3("upstream_not_found", "the selected subscription resource was not found");
9693
+ }
9694
+ const modelEligible = bounded.filter(
9695
+ (account) => accountSupportsModel(account.supportedModels, model)
9696
+ );
9697
+ if (modelEligible.length === 0) {
9698
+ throw new RouteLeaseError3("model_not_configured", "model is not supported by the selected subscription resource");
9699
+ }
9700
+ const credentialEligible = modelEligible.filter(
9701
+ (account) => account.enabled !== false && hasCredential(providerId, account)
9702
+ );
9703
+ const health2 = getSharedAccountHealth3();
9704
+ const allowance = getSharedAccountAllowanceScheduling4();
9705
+ const candidates = credentialEligible.filter(
9706
+ (account) => health2.isSchedulable(providerId, account.id) && allowance.preview(providerId, account.id, account.priority ?? 50).schedulable
9707
+ );
9708
+ if (candidates.length > 0) return;
9709
+ if (upstream.kind === "account") {
9710
+ throw new RouteLeaseError3("upstream_unavailable", "the selected subscription account is unavailable");
9711
+ }
9712
+ throw new RouteLeaseError3("upstream_exhausted", "the selected subscription pool has no eligible account", {
9713
+ retryAfterSeconds: 30
9714
+ });
9715
+ }
9716
+ };
9717
+ }
9718
+
9274
9719
  // src/webhook/WebhookDispatcher.ts
9275
9720
  import { createHmac as createHmac2 } from "crypto";
9276
9721
  import { fetchUpstream as fetchUpstream6 } from "@omnicross/core/pipeline/upstreamFetch";
@@ -9453,7 +9898,7 @@ function buildDaemon(config, paths) {
9453
9898
  new JsonAccountAllowancePersistence(defaultAccountAllowancePath(paths.configPath))
9454
9899
  );
9455
9900
  setSharedAccountAllowanceStore(accountAllowanceStore);
9456
- getSharedAccountAllowanceScheduling4().configure(
9901
+ getSharedAccountAllowanceScheduling5().configure(
9457
9902
  normalizeServerConfig(decryptedConfig.server).allowanceScheduling
9458
9903
  );
9459
9904
  const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
@@ -9521,10 +9966,21 @@ function buildDaemon(config, paths) {
9521
9966
  onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at)
9522
9967
  });
9523
9968
  const providerProxy = getProviderProxy({ llmConfig, apiKeyPool, usageRecorder });
9969
+ const routeLeaseManager = new RouteLeaseManager(
9970
+ providerProxy,
9971
+ new RouteLeaseTargetResolver(llmConfig, {
9972
+ providerKeys: apiKeyPool,
9973
+ subscriptions: createRouteLeaseSubscriptionPreflight(credentialStore)
9974
+ }),
9975
+ routeLeaseDescriptorPort,
9976
+ { logger }
9977
+ );
9978
+ providerProxy.registerBeforeStop(() => routeLeaseManager.shutdown());
9979
+ providerProxy.registerBeforeStop(() => resetCliSessions());
9524
9980
  llmConfig.setReloadHook(() => apiKeyPool.invalidateCache());
9525
9981
  const accountHealthProbeScheduler = new AccountHealthProbeScheduler(
9526
9982
  credentialStore,
9527
- getSharedAccountHealth3(),
9983
+ getSharedAccountHealth4(),
9528
9984
  logger,
9529
9985
  DEFAULT_ACCOUNT_PROBE
9530
9986
  );
@@ -9571,6 +10027,7 @@ function buildDaemon(config, paths) {
9571
10027
  keySpendReader: keySpendTracker,
9572
10028
  settingsStore,
9573
10029
  outboundApiServer,
10030
+ routeLeaseManager,
9574
10031
  subscriptionAccounts,
9575
10032
  accountAllowanceService,
9576
10033
  allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
@@ -9663,7 +10120,7 @@ function buildDaemon(config, paths) {
9663
10120
  logger,
9664
10121
  fetchImpl: (url, init) => fetchUpstream7(url, init)
9665
10122
  });
9666
- setWebhookRuntime(webhookDispatcher, getSharedAccountHealth3());
10123
+ setWebhookRuntime(webhookDispatcher, getSharedAccountHealth4());
9667
10124
  const auditWriter = new AuditWriter(auditDir, logger);
9668
10125
  const auditPruneSweeper = new AuditPruneSweeper(auditDir, logger, DEFAULT_AUDIT_CONFIG);
9669
10126
  setAuditRuntime(auditWriter, auditPruneSweeper);
@@ -9678,7 +10135,7 @@ function buildDaemon(config, paths) {
9678
10135
  const tokenRefreshScheduler = new TokenRefreshScheduler(credentialStore, logger);
9679
10136
  const accountHealthSweeper = new AccountHealthSweeper(
9680
10137
  credentialStore,
9681
- getSharedAccountHealth3(),
10138
+ getSharedAccountHealth4(),
9682
10139
  logger
9683
10140
  );
9684
10141
  return {
@@ -9687,6 +10144,7 @@ function buildDaemon(config, paths) {
9687
10144
  keyDb,
9688
10145
  settingsStore,
9689
10146
  providerProxy,
10147
+ routeLeaseManager,
9690
10148
  outboundApiServer,
9691
10149
  apiKeyPool,
9692
10150
  autoDisableStore,