agent-shim 11.83.4 → 11.83.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -23762,9 +23762,9 @@ var require_env_paths = __commonJS({
23762
23762
  "node_modules/.pnpm/env-paths@2.2.1/node_modules/env-paths/index.js"(exports2, module2) {
23763
23763
  "use strict";
23764
23764
  var path79 = require("path");
23765
- var os16 = require("os");
23766
- var homedir = os16.homedir();
23767
- var tmpdir = os16.tmpdir();
23765
+ var os17 = require("os");
23766
+ var homedir = os17.homedir();
23767
+ var tmpdir = os17.tmpdir();
23768
23768
  var { env } = process;
23769
23769
  var macos = (name) => {
23770
23770
  const library = path79.join(homedir, "Library");
@@ -36845,13 +36845,18 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
36845
36845
  });
36846
36846
  const { predecessor } = ports;
36847
36847
  const sourceOf = (listener, preferredPort) => predecessor === void 0 ? { preferredPort } : { inherit: async () => await predecessor.take(listener) };
36848
- const fail = (message) => {
36848
+ const fail = async (message) => {
36849
36849
  ports.closeRcStreamHub();
36850
36850
  if (predecessor === void 0) {
36851
36851
  writeFrontDoorState(fs32, paths.frontdoorStateFile, { lastError: message, ...sticky() });
36852
36852
  fs32.removeRecursive(paths.frontdoorLockFile);
36853
36853
  }
36854
36854
  ports.log(`agent-shim frontdoor supervisor: ${message}`);
36855
+ const chain = ports.respawnChain;
36856
+ if (predecessor === void 0 && chain !== void 0 && chain.generation > 0) {
36857
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: this door is generation ${String(chain.generation)} of the watchdog's respawn chain and nothing waits on it, so it starts generation ${String(chain.generation + 1)} rather than leaving no door`);
36858
+ await chain.startNext();
36859
+ }
36855
36860
  return 1;
36856
36861
  };
36857
36862
  if (predecessor === void 0) {
@@ -36863,7 +36868,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
36863
36868
  http9 = await ports.startProviderListener(sourceOf("provider", lastPort));
36864
36869
  } catch (error63) {
36865
36870
  ports.doorHealth.listenerFailed("provider", error63 instanceof Error ? error63.message : String(error63));
36866
- return fail(`could not start the provider listener: ${error63 instanceof Error ? error63.message : String(error63)}`);
36871
+ return await fail(`could not start the provider listener: ${error63 instanceof Error ? error63.message : String(error63)}`);
36867
36872
  }
36868
36873
  if (previousSticky !== void 0 && http9.port !== previousSticky) {
36869
36874
  ports.log(`agent-shim frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(http9.port)} (sessions launched against the old port are stale until they relaunch)`);
@@ -36876,7 +36881,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
36876
36881
  } catch (error63) {
36877
36882
  await http9.close();
36878
36883
  ports.doorHealth.listenerFailed("connect", error63 instanceof Error ? error63.message : String(error63));
36879
- return fail(`could not start the CONNECT surface: ${error63 instanceof Error ? error63.message : String(error63)}`);
36884
+ return await fail(`could not start the CONNECT surface: ${error63 instanceof Error ? error63.message : String(error63)}`);
36880
36885
  }
36881
36886
  if (previousStickyConnect !== void 0 && connect3.port !== previousStickyConnect) {
36882
36887
  ports.log(`agent-shim frontdoor supervisor: sticky CONNECT port ${String(previousStickyConnect)} was occupied; moving to ${String(connect3.port)} (OAuth sessions launched against the old port are stale until they relaunch)`);
@@ -36890,7 +36895,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
36890
36895
  await connect3.close();
36891
36896
  await http9.close();
36892
36897
  ports.doorHealth.listenerFailed("direct", error63 instanceof Error ? error63.message : String(error63));
36893
- return fail(`could not start the direct listener: ${error63 instanceof Error ? error63.message : String(error63)}`);
36898
+ return await fail(`could not start the direct listener: ${error63 instanceof Error ? error63.message : String(error63)}`);
36894
36899
  }
36895
36900
  if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
36896
36901
  ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom admits the new address once its supervisor rewrites the allowlist file)`);
@@ -39981,7 +39986,7 @@ var init_package = __esm({
39981
39986
  "package.json"() {
39982
39987
  package_default = {
39983
39988
  name: "agent-shim",
39984
- version: "11.83.4",
39989
+ version: "11.83.6",
39985
39990
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
39986
39991
  license: "MIT",
39987
39992
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -68738,9 +68743,12 @@ var init_cloudUpstream = __esm({
68738
68743
  AntigravityUpstreamError = class extends Error {
68739
68744
  status;
68740
68745
  retryAfter;
68746
+ /** The backend's own answer, as it sent it, which carries more than the message says (the model a 429 names and when its allowance lifts). */
68747
+ body;
68741
68748
  constructor(status, message, retryAfter) {
68742
68749
  super(`Google Antigravity API error (${String(status)}): ${message}`);
68743
68750
  this.name = "AntigravityUpstreamError";
68751
+ this.body = message;
68744
68752
  this.status = status;
68745
68753
  this.retryAfter = retryAfter;
68746
68754
  }
@@ -68789,6 +68797,60 @@ function antigravityQuotaPressure(quota, model) {
68789
68797
  const pressures = [windowPressure(fiveHour, FIVE_HOUR_PERIOD_SECONDS), windowPressure(weekly, WEEK_PERIOD_SECONDS)].filter((pressure) => pressure !== void 0);
68790
68798
  return pressures.length === 0 ? void 0 : Math.min(...pressures);
68791
68799
  }
68800
+ function parseQuotaExhaustion(body, nowMs) {
68801
+ let parsed;
68802
+ try {
68803
+ parsed = JSON.parse(body);
68804
+ } catch {
68805
+ return void 0;
68806
+ }
68807
+ const details = isRecord7(parsed) && isRecord7(parsed.error) && Array.isArray(parsed.error.details) ? parsed.error.details : [];
68808
+ let model;
68809
+ let resetAtMs;
68810
+ for (const detail of details) {
68811
+ if (!isRecord7(detail)) {
68812
+ continue;
68813
+ }
68814
+ if (detail.reason === "QUOTA_EXHAUSTED" && isRecord7(detail.metadata)) {
68815
+ const named = detail.metadata.model;
68816
+ const stamp = typeof detail.metadata.quotaResetTimeStamp === "string" ? Date.parse(detail.metadata.quotaResetTimeStamp) : Number.NaN;
68817
+ model = typeof named === "string" && named !== "" ? named : void 0;
68818
+ resetAtMs = Number.isNaN(stamp) ? void 0 : stamp;
68819
+ } else if (typeof detail.retryDelay === "string" && resetAtMs === void 0) {
68820
+ const seconds = RETRY_DELAY.exec(detail.retryDelay)?.groups?.seconds;
68821
+ resetAtMs = seconds === void 0 ? void 0 : nowMs + Math.ceil(Number(seconds) * MS_PER_SECOND9);
68822
+ }
68823
+ }
68824
+ return model === void 0 || resetAtMs === void 0 ? void 0 : { model, resetAtMs };
68825
+ }
68826
+ function bucketKeyOf(model, resetInSeconds) {
68827
+ const lower = model.toLowerCase();
68828
+ if (lower.includes("argon")) {
68829
+ return "gemini-argon-wtu-weekly";
68830
+ }
68831
+ const withinFiveHours = resetInSeconds <= FIVE_HOUR_PERIOD_SECONDS;
68832
+ if (lower.includes("gemini")) {
68833
+ return withinFiveHours ? "gemini-5h" : "gemini-weekly";
68834
+ }
68835
+ return withinFiveHours ? "3p-5h" : "3p-weekly";
68836
+ }
68837
+ function quotaWithExhaustions(quota, exhaustions, nowMs) {
68838
+ const spent = [...exhaustions].filter(([, resetAtMs]) => resetAtMs > nowMs);
68839
+ if (spent.length === 0) {
68840
+ return quota;
68841
+ }
68842
+ const merged = { ...quota };
68843
+ for (const [model, resetAtMs] of spent) {
68844
+ const resetIn = Math.ceil((resetAtMs - nowMs) / MS_PER_SECOND9);
68845
+ const key = bucketKeyOf(model, resetIn);
68846
+ const known = merged[key];
68847
+ if (known?.remaining_fraction === 0 && (known.reset_in_seconds ?? 0) >= resetIn) {
68848
+ continue;
68849
+ }
68850
+ merged[key] = { remaining_fraction: 0, reset_in_seconds: resetIn };
68851
+ }
68852
+ return merged;
68853
+ }
68792
68854
  function calculateUtilization(remainingFraction) {
68793
68855
  if (remainingFraction === void 0 || !Number.isFinite(remainingFraction)) {
68794
68856
  return void 0;
@@ -68970,7 +69032,7 @@ function ageQuotaMap(quota, elapsedMs) {
68970
69032
  Object.entries(quota).map(([key, bucket]) => [key, bucket?.reset_in_seconds === void 0 ? bucket : { ...bucket, reset_in_seconds: Math.max(0, bucket.reset_in_seconds - elapsedSec) }])
68971
69033
  );
68972
69034
  }
68973
- var MS_PER_SECOND9, DEFAULT_RETRY_AFTER_SECONDS, FRACTION_DIGITS, GEMINI_5H_KEYS, GEMINI_WEEKLY_KEYS, ARGON_WEEKLY_KEYS, THIRD_PARTY_5H_KEYS, THIRD_PARTY_WEEKLY_KEYS, QUOTA_FAMILY_WINDOWS, SECONDS_PER_HOUR, HOURS_PER_FIVE_HOUR_WINDOW, DAYS_PER_WEEK, HOURS_PER_DAY4, FIVE_HOUR_PERIOD_SECONDS, WEEK_PERIOD_SECONDS, ANTIGRAVITY_QUOTA_REFRESH_MS;
69035
+ var MS_PER_SECOND9, DEFAULT_RETRY_AFTER_SECONDS, FRACTION_DIGITS, GEMINI_5H_KEYS, GEMINI_WEEKLY_KEYS, ARGON_WEEKLY_KEYS, THIRD_PARTY_5H_KEYS, THIRD_PARTY_WEEKLY_KEYS, QUOTA_FAMILY_WINDOWS, SECONDS_PER_HOUR, HOURS_PER_FIVE_HOUR_WINDOW, DAYS_PER_WEEK, HOURS_PER_DAY4, FIVE_HOUR_PERIOD_SECONDS, WEEK_PERIOD_SECONDS, RETRY_DELAY, ANTIGRAVITY_QUOTA_REFRESH_MS;
68974
69036
  var init_quota = __esm({
68975
69037
  "src/antigravity/quota.ts"() {
68976
69038
  "use strict";
@@ -68997,6 +69059,7 @@ var init_quota = __esm({
68997
69059
  HOURS_PER_DAY4 = 24;
68998
69060
  FIVE_HOUR_PERIOD_SECONDS = HOURS_PER_FIVE_HOUR_WINDOW * SECONDS_PER_HOUR;
68999
69061
  WEEK_PERIOD_SECONDS = DAYS_PER_WEEK * HOURS_PER_DAY4 * SECONDS_PER_HOUR;
69062
+ RETRY_DELAY = /^(?<seconds>\d+(?:\.\d+)?)s$/u;
69000
69063
  ANTIGRAVITY_QUOTA_REFRESH_MS = 6e4;
69001
69064
  }
69002
69065
  });
@@ -71163,7 +71226,13 @@ var init_providerQuota = __esm({
71163
71226
  /** The account's paid subscription; absent when the account has none. */
71164
71227
  paidTier: external_exports.object({ name: external_exports.string().optional(), id: external_exports.string().optional() }).optional()
71165
71228
  });
71166
- ANTIGRAVITY_WINDOW_MS = { "gemini-5h": FIVE_HOURS_MS, "3p-5h": FIVE_HOURS_MS, "gemini-weekly": WEEK_MS, "3p-weekly": WEEK_MS };
71229
+ ANTIGRAVITY_WINDOW_MS = {
71230
+ "gemini-5h": FIVE_HOURS_MS,
71231
+ "3p-5h": FIVE_HOURS_MS,
71232
+ "gemini-weekly": WEEK_MS,
71233
+ "3p-weekly": WEEK_MS,
71234
+ "gemini-argon-wtu-weekly": WEEK_MS
71235
+ };
71167
71236
  antigravityQuotaAdapter = {
71168
71237
  source: ANTIGRAVITY_SOURCE,
71169
71238
  matches: (baseUrl) => URL.canParse(baseUrl) && new URL(baseUrl).hostname === ANTIGRAVITY_HOST,
@@ -126078,6 +126147,17 @@ var import_node_https = __toESM(require("node:https"), 1);
126078
126147
  var import_node_net2 = __toESM(require("node:net"), 1);
126079
126148
  init_legacy();
126080
126149
 
126150
+ // src/frontdoor/machineLoad.ts
126151
+ var import_node_os14 = __toESM(require("node:os"), 1);
126152
+ var processorShare = (reading) => reading.loadAverage <= reading.cpus ? 1 : reading.cpus / reading.loadAverage;
126153
+ function readMachineLoad() {
126154
+ const [oneMinute] = import_node_os14.default.loadavg();
126155
+ if (oneMinute === void 0) {
126156
+ throw new Error("os.loadavg() returned no one-minute load average");
126157
+ }
126158
+ return { loadAverage: oneMinute, cpus: import_node_os14.default.cpus().length };
126159
+ }
126160
+
126081
126161
  // src/frontdoor/rcControl.ts
126082
126162
  var https = __toESM(require("node:https"), 1);
126083
126163
  init_capability();
@@ -126764,35 +126844,54 @@ async function listenFrontDoor(server, options = {}) {
126764
126844
  if (options.onError !== void 0) {
126765
126845
  server.on("error", options.onError);
126766
126846
  }
126767
- const deadline = Date.now() + HEALTH_START_BUDGET_MS;
126847
+ await awaitStartHealth(
126848
+ {
126849
+ probe: async () => await frontDoorHealthy(bound, ca2),
126850
+ now: () => Date.now(),
126851
+ sleep: async (ms) => {
126852
+ await new Promise((resolve3) => {
126853
+ setTimeout(resolve3, ms);
126854
+ });
126855
+ },
126856
+ readLoad: readMachineLoad
126857
+ },
126858
+ `127.0.0.1:${String(bound)}`
126859
+ );
126860
+ return {
126861
+ port: bound,
126862
+ close: async () => {
126863
+ await new Promise((resolve3) => {
126864
+ server.close(() => {
126865
+ resolve3(void 0);
126866
+ });
126867
+ server.closeAllConnections();
126868
+ });
126869
+ },
126870
+ // The port is released as the close runs, before the first await (see `trackConnections` for what happens to the connections already accepted).
126871
+ drain: async () => {
126872
+ await new Promise((resolve3) => {
126873
+ connections.drain(() => {
126874
+ resolve3(void 0);
126875
+ });
126876
+ });
126877
+ }
126878
+ };
126879
+ }
126880
+ async function awaitStartHealth(ports, address) {
126881
+ const startedAt = ports.now();
126882
+ let scheduledMs = 0;
126883
+ let countedTo = startedAt;
126768
126884
  for (; ; ) {
126769
- if (await frontDoorHealthy(bound, ca2)) {
126770
- return {
126771
- port: bound,
126772
- close: async () => {
126773
- await new Promise((resolve3) => {
126774
- server.close(() => {
126775
- resolve3(void 0);
126776
- });
126777
- server.closeAllConnections();
126778
- });
126779
- },
126780
- // The port is released as the close runs, before the first await (see `trackConnections` for what happens to the connections already accepted).
126781
- drain: async () => {
126782
- await new Promise((resolve3) => {
126783
- connections.drain(() => {
126784
- resolve3(void 0);
126785
- });
126786
- });
126787
- }
126788
- };
126885
+ if (await ports.probe()) {
126886
+ return;
126789
126887
  }
126790
- if (Date.now() >= deadline) {
126791
- throw new Error(`the front door listener on 127.0.0.1:${String(bound)} did not answer its health probe`);
126888
+ const now = ports.now();
126889
+ scheduledMs += (now - countedTo) * processorShare(ports.readLoad());
126890
+ countedTo = now;
126891
+ if (scheduledMs >= HEALTH_START_BUDGET_MS) {
126892
+ throw new Error(`the front door listener on ${address} did not answer its health probe within ${String(HEALTH_START_BUDGET_MS)}ms of processor time (${String(now - startedAt)}ms of wall time)`);
126792
126893
  }
126793
- await new Promise((resolve3) => {
126794
- setTimeout(resolve3, HEALTH_RETRY_MS);
126795
- });
126894
+ await ports.sleep(HEALTH_RETRY_MS);
126796
126895
  }
126797
126896
  }
126798
126897
  function trackConnections(server) {
@@ -134484,7 +134583,7 @@ var Builder = class _Builder {
134484
134583
  });
134485
134584
  }
134486
134585
  };
134487
- var os13 = Builder.create();
134586
+ var os14 = Builder.create();
134488
134587
 
134489
134588
  // node_modules/.pnpm/@orpc+openapi@2.0.0-beta.43/node_modules/@orpc/openapi/dist/index.mjs
134490
134589
  var DEFS_REF_PREFIX = "#/$defs/";
@@ -138848,7 +138947,7 @@ function doorApiBearer(headers) {
138848
138947
  return typeof presented === "string" && presented.slice(0, bearerPrefix).toLowerCase() === "bearer " ? presented.slice(bearerPrefix) : void 0;
138849
138948
  }
138850
138949
  function doorApiAuth(expectedToken) {
138851
- return os13.$context().use(({ context, next }) => {
138950
+ return os14.$context().use(({ context, next }) => {
138852
138951
  const token = doorApiBearer(context.headers);
138853
138952
  if (token === void 0 || !isLiveCapability(token, [expectedToken])) {
138854
138953
  throw new ORPCError("UNAUTHORIZED", { message: "the front door's typed API demands this generation's control token as a Bearer credential" });
@@ -141811,6 +141910,15 @@ function exhaustedMessage(exhaustion) {
141811
141910
  function createAntigravityRoute(ports) {
141812
141911
  const fallbackId = ports.fallbackId ?? (() => (0, import_node_crypto24.randomUUID)());
141813
141912
  const lastRouted = ports.lastRouted ?? /* @__PURE__ */ new Map();
141913
+ const exhaustedModels = ports.exhaustedModels ?? /* @__PURE__ */ new Map();
141914
+ const liveQuota = (nowMs) => {
141915
+ for (const [model, resetAtMs] of exhaustedModels) {
141916
+ if (resetAtMs <= nowMs) {
141917
+ exhaustedModels.delete(model);
141918
+ }
141919
+ }
141920
+ return quotaWithExhaustions(ports.quotaSource?.(), exhaustedModels, nowMs);
141921
+ };
141814
141922
  return async (request7) => {
141815
141923
  await Promise.resolve();
141816
141924
  if (request7.method !== "POST") {
@@ -141864,71 +141972,90 @@ function createAntigravityRoute(ports) {
141864
141972
  const requestedModel = parsed.data.model ?? config2.defaultModel;
141865
141973
  const nowMs = ports.now?.() ?? Date.now();
141866
141974
  const sessionKey = parsed.data.metadata?.user_id === void 0 ? void 0 : `${parsed.data.metadata.user_id}\0${parsed.data.model ?? ""}`;
141867
- const translated = translateRequest(parsed.data, config2, { quota: ports.quotaSource?.(), nowMs, previous: sessionKey === void 0 ? void 0 : lastRouted.get(sessionKey) });
141868
- if (sessionKey !== void 0) {
141869
- lastRouted.delete(sessionKey);
141870
- lastRouted.set(sessionKey, translated.model);
141871
- if (lastRouted.size > ROUTING_MEMORY_SESSIONS) {
141872
- const oldest = lastRouted.keys().next();
141873
- if (oldest.done !== true) {
141874
- lastRouted.delete(oldest.value);
141975
+ const routeRequest = () => {
141976
+ const routed = translateRequest(parsed.data, config2, { quota: liveQuota(nowMs), nowMs, previous: sessionKey === void 0 ? void 0 : lastRouted.get(sessionKey) });
141977
+ if (sessionKey !== void 0) {
141978
+ lastRouted.delete(sessionKey);
141979
+ lastRouted.set(sessionKey, routed.model);
141980
+ if (lastRouted.size > ROUTING_MEMORY_SESSIONS) {
141981
+ const oldest = lastRouted.keys().next();
141982
+ if (oldest.done !== true) {
141983
+ lastRouted.delete(oldest.value);
141984
+ }
141875
141985
  }
141876
141986
  }
141877
- }
141878
- const relay = createAntigravityRelay({
141879
- // The frame carries the model that actually served the request, as the real API echoes the concrete model behind an alias: a usage record read from the response then attributes quota to the backend model, not the tier alias the session typed.
141880
- servedModel: translated.model,
141881
- toolNames: translated.toolNames,
141882
- fallbackId
141883
- });
141884
- const upstreamChunks2 = config2.mode === "local" ? callLocalAntigravity({
141885
- config: config2,
141886
- model: translated.model,
141887
- request: translated.request,
141888
- signal: request7.signal,
141889
- connector: ports.localConnector
141890
- }) : callCloudAntigravity(
141891
- {
141987
+ return routed;
141988
+ };
141989
+ let translated = routeRequest();
141990
+ const tried = /* @__PURE__ */ new Set();
141991
+ let iterator;
141992
+ let firstResult;
141993
+ for (; ; ) {
141994
+ tried.add(translated.model);
141995
+ const upstreamChunks2 = config2.mode === "local" ? callLocalAntigravity({
141892
141996
  config: config2,
141893
141997
  model: translated.model,
141894
141998
  request: translated.request,
141895
- token,
141896
- credentialTarget,
141897
- signal: request7.signal
141898
- },
141899
- ports.fetchFn
141900
- );
141901
- const iterator = upstreamChunks2[Symbol.asyncIterator]();
141902
- let firstResult;
141903
- try {
141904
- firstResult = await iterator.next();
141905
- } catch (error63) {
141906
- if (error63 instanceof AntigravityUpstreamError) {
141907
- const is429 = error63.status === HTTP_STATUS.tooManyRequests;
141908
- const quota = ports.quotaSource?.();
141909
- const quotaHeaders3 = unifiedAntigravityQuotaHeaders({ quota, rejected: is429, nowMs, model: translated.model });
141910
- const retryAfter = is429 && quotaHeaders3["retry-after"] === void 0 ? parseRetryAfterSeconds(error63.retryAfter) : void 0;
141911
- const exhaustion = is429 ? antigravityQuotaExhaustion(quota, translated.model, nowMs) : void 0;
141912
- const errorType = is429 ? "rate_limit_error" : "api_error";
141999
+ signal: request7.signal,
142000
+ connector: ports.localConnector
142001
+ }) : callCloudAntigravity(
142002
+ {
142003
+ config: config2,
142004
+ model: translated.model,
142005
+ request: translated.request,
142006
+ token,
142007
+ credentialTarget,
142008
+ signal: request7.signal
142009
+ },
142010
+ ports.fetchFn
142011
+ );
142012
+ iterator = upstreamChunks2[Symbol.asyncIterator]();
142013
+ try {
142014
+ firstResult = await iterator.next();
142015
+ break;
142016
+ } catch (error63) {
142017
+ if (error63 instanceof AntigravityUpstreamError) {
142018
+ const is429 = error63.status === HTTP_STATUS.tooManyRequests;
142019
+ const learned = is429 ? parseQuotaExhaustion(error63.body, nowMs) : void 0;
142020
+ if (learned !== void 0) {
142021
+ exhaustedModels.set(learned.model, learned.resetAtMs);
142022
+ const next = routeRequest();
142023
+ if (!tried.has(next.model)) {
142024
+ translated = next;
142025
+ continue;
142026
+ }
142027
+ }
142028
+ const quota = liveQuota(nowMs);
142029
+ const quotaHeaders3 = unifiedAntigravityQuotaHeaders({ quota, rejected: is429, nowMs, model: translated.model });
142030
+ const retryAfter = is429 && quotaHeaders3["retry-after"] === void 0 ? parseRetryAfterSeconds(error63.retryAfter) : void 0;
142031
+ const exhaustion = is429 ? antigravityQuotaExhaustion(quota, translated.model, nowMs) : void 0;
142032
+ const errorType = is429 ? "rate_limit_error" : "api_error";
142033
+ return {
142034
+ status: error63.status,
142035
+ headers: {
142036
+ "Content-Type": "application/json",
142037
+ ...quotaHeaders3,
142038
+ ...retryAfter !== void 0 ? { "retry-after": String(retryAfter) } : {}
142039
+ },
142040
+ body: JSON.stringify({ type: "error", error: { type: errorType, message: exhaustion === void 0 ? error63.message : exhaustedMessage(exhaustion) } })
142041
+ };
142042
+ }
142043
+ const errMsg = error63 instanceof Error ? error63.message : String(error63);
141913
142044
  return {
141914
- status: error63.status,
141915
- headers: {
141916
- "Content-Type": "application/json",
141917
- ...quotaHeaders3,
141918
- ...retryAfter !== void 0 ? { "retry-after": String(retryAfter) } : {}
141919
- },
141920
- body: JSON.stringify({ type: "error", error: { type: errorType, message: exhaustion === void 0 ? error63.message : exhaustedMessage(exhaustion) } })
142045
+ status: HTTP_STATUS.badGateway,
142046
+ headers: { "Content-Type": "application/json" },
142047
+ body: JSON.stringify({ type: "error", error: { type: "api_error", message: errMsg } })
141921
142048
  };
141922
142049
  }
141923
- const errMsg = error63 instanceof Error ? error63.message : String(error63);
141924
- return {
141925
- status: HTTP_STATUS.badGateway,
141926
- headers: { "Content-Type": "application/json" },
141927
- body: JSON.stringify({ type: "error", error: { type: "api_error", message: errMsg } })
141928
- };
141929
142050
  }
142051
+ const relay = createAntigravityRelay({
142052
+ // The frame carries the model that actually served the request, as the real API echoes the concrete model behind an alias: a usage record read from the response then attributes quota to the backend model, not the tier alias the session typed.
142053
+ servedModel: translated.model,
142054
+ toolNames: translated.toolNames,
142055
+ fallbackId
142056
+ });
141930
142057
  const quotaHeaders2 = unifiedAntigravityQuotaHeaders({
141931
- quota: ports.quotaSource?.(),
142058
+ quota: liveQuota(nowMs),
141932
142059
  rejected: false,
141933
142060
  nowMs,
141934
142061
  model: translated.model
@@ -142392,6 +142519,7 @@ function createProviderRouteResolver(deps) {
142392
142519
  const resolveCredential2 = shareInFlightResolutions(resolveCredentialAsync);
142393
142520
  const antigravityQuotaSources = /* @__PURE__ */ new Map();
142394
142521
  const antigravityRoutingMemory = /* @__PURE__ */ new Map();
142522
+ const antigravityExhaustedModels = /* @__PURE__ */ new Map();
142395
142523
  const definesProvider = (name) => {
142396
142524
  try {
142397
142525
  return loadProvider(deps.providersDir, name, deps.fs) !== void 0;
@@ -142410,6 +142538,15 @@ function createProviderRouteResolver(deps) {
142410
142538
  antigravityRoutingMemory.set(provider, created);
142411
142539
  return created;
142412
142540
  };
142541
+ const exhaustedModelsFor = (provider) => {
142542
+ const existing = antigravityExhaustedModels.get(provider);
142543
+ if (existing !== void 0) {
142544
+ return existing;
142545
+ }
142546
+ const created = /* @__PURE__ */ new Map();
142547
+ antigravityExhaustedModels.set(provider, created);
142548
+ return created;
142549
+ };
142413
142550
  const antigravityQuotaOf = (provider, definition, config2) => {
142414
142551
  const existing = antigravityQuotaSources.get(provider);
142415
142552
  if (existing !== void 0) {
@@ -142505,7 +142642,8 @@ function createProviderRouteResolver(deps) {
142505
142642
  };
142506
142643
  },
142507
142644
  quotaSource: antigravityQuotaOf(provider, antigravityProvider, config2),
142508
- lastRouted: memoryFor(provider)
142645
+ lastRouted: memoryFor(provider),
142646
+ exhaustedModels: exhaustedModelsFor(provider)
142509
142647
  },
142510
142648
  directOrigin2(deps.directPort()),
142511
142649
  provider
@@ -145631,6 +145769,72 @@ var DOOR_RESPAWN_MAX_GENERATIONS = 3;
145631
145769
  var DOOR_RESPAWN_COOLDOWN_CAP_MS = DOOR_STALL_BOUND_MS * DOOR_RESPAWN_MAX_GENERATIONS;
145632
145770
  var CHECKS_PER_PROBE_DEADLINE = 2;
145633
145771
  var DOOR_WATCHDOG_CHECK_MS = PROBE_TIMEOUT_MS / CHECKS_PER_PROBE_DEADLINE;
145772
+ var RESPAWN_SOURCE = `
145773
+ const log = (line) => {
145774
+ if (logFile === undefined) return;
145775
+ try {
145776
+ fs.appendFileSync(logFile, new Date().toISOString() + " frontdoor " + String(ownPid) + ": " + line + "\\n");
145777
+ } catch (error) {
145778
+ // Nowhere left to report a failed log write to; the thread's work goes on without it.
145779
+ }
145780
+ };
145781
+ // Whether a process is alive: signal 0 only tests, and EPERM means it exists under another user.
145782
+ const isRunning = (pid) => {
145783
+ try {
145784
+ process.kill(pid, 0);
145785
+ return true;
145786
+ } catch (error) {
145787
+ return error.code === "EPERM";
145788
+ }
145789
+ };
145790
+ // The pid holding the start lock when that holder is alive, which means a door is already being started and another must not be: the launcher's ensure and this respawn are the two things that start a door, and the lock is the one protocol that keeps them from starting two. A dead holder's lock is litter and is cleared, exactly as the launcher's ensure clears it.
145791
+ const liveLockHolder = () => {
145792
+ let holder;
145793
+ try {
145794
+ holder = JSON.parse(fs.readFileSync(startLockFile, "utf8")).pid;
145795
+ } catch (error) {
145796
+ return undefined;
145797
+ }
145798
+ if (typeof holder === "number" && isRunning(holder)) return holder;
145799
+ fs.rmSync(startLockFile, { force: true });
145800
+ return undefined;
145801
+ };
145802
+ // Creates the lock naming the replacement as its holder, atomically (the wx flag), so the launcher's ensure sees a live holder while the replacement boots and waits for it instead of starting a door of its own; the replacement removes the lock once it is serving, as every door does.
145803
+ const claimStartLock = (holder) => {
145804
+ try {
145805
+ fs.writeFileSync(startLockFile, JSON.stringify({ pid: holder, at: Date.now() }) + "\\n", { flag: "wx" });
145806
+ return true;
145807
+ } catch (error) {
145808
+ if (error.code === "EEXIST") return false;
145809
+ throw error;
145810
+ }
145811
+ };
145812
+ const spawnReplacement = () => {
145813
+ if (respawn === undefined || logFile === undefined) return;
145814
+ const nextGeneration = respawnGeneration + 1;
145815
+ const cooldownMs = Math.min(Math.max(0, nextGeneration - respawnMaxGeneration) * cooldownStepMs, cooldownCapMs);
145816
+ const startingBy = liveLockHolder();
145817
+ if (startingBy !== undefined) {
145818
+ log("not starting a replacement: process " + String(startingBy) + " holds the start lock, so a door is already being started and a second one would race it for the sticky ports");
145819
+ return;
145820
+ }
145821
+ try {
145822
+ const out = fs.openSync(logFile, "a");
145823
+ const child = require("node:child_process").spawn(respawn.command, respawn.args, { detached: true, stdio: ["ignore", out, out], env: Object.assign({}, respawn.env, { [awaitPidEnv]: String(ownPid), [respawnGenerationEnv]: String(nextGeneration), [delayEnv]: String(cooldownMs) }) });
145824
+ child.unref();
145825
+ fs.closeSync(out);
145826
+ if (!claimStartLock(child.pid)) {
145827
+ // A launcher took the lock between the check and the spawn, so its door is the one that is starting: this one must not also bind.
145828
+ process.kill(child.pid, "SIGKILL");
145829
+ log("withdrew replacement door " + String(child.pid) + ": a launcher took the start lock first and its door is the one starting");
145830
+ return;
145831
+ }
145832
+ log("started replacement door " + String(child.pid) + " of generation " + String(nextGeneration) + ", which binds once this process is gone" + (cooldownMs > 0 ? " and a " + String(cooldownMs) + "ms cooldown has passed, since the chain has gone through " + String(respawnMaxGeneration) + " consecutive replacements" : ""));
145833
+ } catch (error) {
145834
+ log("could not start a replacement door: " + (error instanceof Error ? error.message : String(error)));
145835
+ }
145836
+ };
145837
+ `;
145634
145838
  var WATCHDOG_SOURCE = `
145635
145839
  const { workerData, parentPort } = require("node:worker_threads");
145636
145840
  const fs = require("node:fs");
@@ -145647,7 +145851,8 @@ let scheduledMs = 0;
145647
145851
  let waitingOutStarvation = false;
145648
145852
  let stopped = false;
145649
145853
  const readLoad = () => machineLoad === undefined ? { loadAverage: os.loadavg()[0], cpus: os.cpus().length } : machineLoad;
145650
- const processorShare = (reading) => reading.loadAverage <= reading.cpus ? 1 : reading.cpus / reading.loadAverage;
145854
+ // The main thread's own formula, embedded as its source text (see \`processorShare\`), so the stall bound and the start-up probe budget are counted alike.
145855
+ const processorShare = ${processorShare.toString()};
145651
145856
  const describeLoad = (reading) => reading.loadAverage.toFixed(1) + " across " + String(reading.cpus) + " cpus";
145652
145857
  const record = (message) => {
145653
145858
  try {
@@ -145660,14 +145865,7 @@ const record = (message) => {
145660
145865
  // The state file is a courtesy to whoever looks next; the process still has to end.
145661
145866
  }
145662
145867
  };
145663
- const log = (line) => {
145664
- if (logFile === undefined) return;
145665
- try {
145666
- fs.appendFileSync(logFile, new Date().toISOString() + " frontdoor " + String(ownPid) + ": " + line + "\\n");
145667
- } catch (error) {
145668
- // Nowhere left to report a failed log write to; the process still has to end.
145669
- }
145670
- };
145868
+ ${RESPAWN_SOURCE}
145671
145869
  const MS_PER_SECOND = 1000;
145672
145870
  // Enough of the stack to read the caller chain from the timer or socket callback down to the call that held the loop.
145673
145871
  const STACK_FRAMES_LOGGED = 12;
@@ -145708,62 +145906,6 @@ const describeMainThread = (waitMs, resume) => new Promise((resolve) => {
145708
145906
  finish("the main thread could not be inspected: " + (error instanceof Error ? error.message : String(error)));
145709
145907
  }
145710
145908
  });
145711
- // Whether a process is alive: signal 0 only tests, and EPERM means it exists under another user.
145712
- const isRunning = (pid) => {
145713
- try {
145714
- process.kill(pid, 0);
145715
- return true;
145716
- } catch (error) {
145717
- return error.code === "EPERM";
145718
- }
145719
- };
145720
- // The pid holding the start lock when that holder is alive, which means a door is already being started and another must not be: the launcher's ensure and this respawn are the two things that start a door, and the lock is the one protocol that keeps them from starting two. A dead holder's lock is litter and is cleared, exactly as the launcher's ensure clears it.
145721
- const liveLockHolder = () => {
145722
- let holder;
145723
- try {
145724
- holder = JSON.parse(fs.readFileSync(startLockFile, "utf8")).pid;
145725
- } catch (error) {
145726
- return undefined;
145727
- }
145728
- if (typeof holder === "number" && isRunning(holder)) return holder;
145729
- fs.rmSync(startLockFile, { force: true });
145730
- return undefined;
145731
- };
145732
- // Creates the lock naming the replacement as its holder, atomically (the wx flag), so the launcher's ensure sees a live holder while the replacement boots and waits for it instead of starting a door of its own; the replacement removes the lock once it is serving, as every door does.
145733
- const claimStartLock = (holder) => {
145734
- try {
145735
- fs.writeFileSync(startLockFile, JSON.stringify({ pid: holder, at: Date.now() }) + "\\n", { flag: "wx" });
145736
- return true;
145737
- } catch (error) {
145738
- if (error.code === "EEXIST") return false;
145739
- throw error;
145740
- }
145741
- };
145742
- const spawnReplacement = () => {
145743
- if (respawn === undefined || logFile === undefined) return;
145744
- const nextGeneration = respawnGeneration + 1;
145745
- const cooldownMs = Math.min(Math.max(0, nextGeneration - respawnMaxGeneration) * cooldownStepMs, cooldownCapMs);
145746
- const startingBy = liveLockHolder();
145747
- if (startingBy !== undefined) {
145748
- log("not starting a replacement: process " + String(startingBy) + " holds the start lock, so a door is already being started and a second one would race it for the sticky ports");
145749
- return;
145750
- }
145751
- try {
145752
- const out = fs.openSync(logFile, "a");
145753
- const child = require("node:child_process").spawn(respawn.command, respawn.args, { detached: true, stdio: ["ignore", out, out], env: Object.assign({}, respawn.env, { [awaitPidEnv]: String(ownPid), [respawnGenerationEnv]: String(nextGeneration), [delayEnv]: String(cooldownMs) }) });
145754
- child.unref();
145755
- fs.closeSync(out);
145756
- if (!claimStartLock(child.pid)) {
145757
- // A launcher took the lock between the check and the spawn, so its door is the one that is starting: this one must not also bind.
145758
- process.kill(child.pid, "SIGKILL");
145759
- log("withdrew replacement door " + String(child.pid) + ": a launcher took the start lock first and its door is the one starting");
145760
- return;
145761
- }
145762
- log("started replacement door " + String(child.pid) + " of generation " + String(nextGeneration) + ", which binds once this process is gone" + (cooldownMs > 0 ? " and a " + String(cooldownMs) + "ms cooldown has passed, since the stall has recurred through " + String(respawnMaxGeneration) + " consecutive replacements" : ""));
145763
- } catch (error) {
145764
- log("could not start a replacement door: " + (error instanceof Error ? error.message : String(error)));
145765
- }
145766
- };
145767
145909
  // Captures where a main thread that could not be paused is held natively, with the platform's own sampler, so a stall the inspector cannot name still leaves a stack to read. The wait is the sampling time plus the sampler's start-up and symbolication, stretched by the processor share the machine's load leaves it, and the capture never delays the process ending beyond that. The sampler writes its report only once it has finished, so one ended at the deadline, or one that failed, leaves no file, and the log says which rather than naming a file that is not there.
145768
145910
  const takeNativeSample = () => new Promise((resolve) => {
145769
145911
  if (nativeSample === undefined) { resolve(); return; }
@@ -145854,12 +145996,13 @@ parentPort.on("message", (message) => {
145854
145996
  }
145855
145997
  });
145856
145998
  `;
145999
+ var RESPAWN_ENV_NAMES = { awaitPidEnv: DOOR_AWAIT_PID_ENV, respawnGenerationEnv: DOOR_RESPAWN_GENERATION_ENV, delayEnv: DOOR_RESPAWN_DELAY_ENV };
145857
146000
  function startThreadWatchdog(params) {
145858
146001
  const shared = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
145859
146002
  const counter = new Int32Array(shared);
145860
146003
  const worker = new import_node_worker_threads2.Worker(WATCHDOG_SOURCE, {
145861
146004
  eval: true,
145862
- workerData: { shared, boundMs: params.boundMs, checkEveryMs: params.checkEveryMs, pauseWaitMs: params.pauseWaitMs, stateFile: params.stateFile, ownPid: params.ownPid, action: params.action, logFile: params.logFile, respawn: params.respawn, respawnGeneration: params.respawnGeneration, respawnMaxGeneration: params.respawnMaxGeneration, cooldownStepMs: params.cooldownStepMs, cooldownCapMs: params.cooldownCapMs, startLockFile: params.startLockFile, awaitPidEnv: DOOR_AWAIT_PID_ENV, respawnGenerationEnv: DOOR_RESPAWN_GENERATION_ENV, delayEnv: DOOR_RESPAWN_DELAY_ENV, nativeSample: params.nativeSample, machineLoad: params.machineLoad }
146005
+ workerData: { shared, boundMs: params.boundMs, checkEveryMs: params.checkEveryMs, pauseWaitMs: params.pauseWaitMs, stateFile: params.stateFile, ownPid: params.ownPid, action: params.action, logFile: params.logFile, respawn: params.respawn, respawnGeneration: params.respawnGeneration, respawnMaxGeneration: params.respawnMaxGeneration, cooldownStepMs: params.cooldownStepMs, cooldownCapMs: params.cooldownCapMs, startLockFile: params.startLockFile, ...RESPAWN_ENV_NAMES, nativeSample: params.nativeSample, machineLoad: params.machineLoad }
145863
146006
  });
145864
146007
  worker.unref();
145865
146008
  const beat = setInterval(() => {
@@ -145884,6 +146027,25 @@ function startThreadWatchdog(params) {
145884
146027
  }
145885
146028
  };
145886
146029
  }
146030
+ var NEXT_GENERATION_SOURCE = `
146031
+ const { workerData } = require("node:worker_threads");
146032
+ const fs = require("node:fs");
146033
+ const { ownPid, logFile, respawn, respawnGeneration, respawnMaxGeneration, cooldownStepMs, cooldownCapMs, startLockFile, awaitPidEnv, respawnGenerationEnv, delayEnv } = workerData;
146034
+ ${RESPAWN_SOURCE}
146035
+ spawnReplacement();
146036
+ `;
146037
+ async function startNextRespawnGeneration(params) {
146038
+ const worker = new import_node_worker_threads2.Worker(NEXT_GENERATION_SOURCE, {
146039
+ eval: true,
146040
+ workerData: { ownPid: params.ownPid, logFile: params.logFile, respawn: params.respawn, respawnGeneration: params.respawnGeneration, respawnMaxGeneration: params.respawnMaxGeneration, cooldownStepMs: params.cooldownStepMs, cooldownCapMs: params.cooldownCapMs, startLockFile: params.startLockFile, ...RESPAWN_ENV_NAMES }
146041
+ });
146042
+ await new Promise((resolve3, reject) => {
146043
+ worker.once("error", reject);
146044
+ worker.once("exit", () => {
146045
+ resolve3();
146046
+ });
146047
+ });
146048
+ }
145887
146049
 
145888
146050
  // src/frontdoor/commands.ts
145889
146051
  init_probe();
@@ -146049,6 +146211,16 @@ function realFrontDoorSupervisorPorts(paths, respawnGeneration, lifecycle, track
146049
146211
  const log = (line) => {
146050
146212
  appendDoorLog(paths, `frontdoor ${String(process.pid)}: ${line}`);
146051
146213
  };
146214
+ const respawnChain = () => ({
146215
+ ownPid: process.pid,
146216
+ logFile: paths.frontdoorLogPath,
146217
+ respawn: respawnInvocation(paths),
146218
+ respawnGeneration,
146219
+ respawnMaxGeneration: DOOR_RESPAWN_MAX_GENERATIONS,
146220
+ cooldownStepMs: DOOR_STALL_BOUND_MS,
146221
+ cooldownCapMs: DOOR_RESPAWN_COOLDOWN_CAP_MS,
146222
+ startLockFile: paths.frontdoorLockFile
146223
+ });
146052
146224
  let directPort = 0;
146053
146225
  const rcSelfHost = rcSelfHostFromEnv(process.env);
146054
146226
  const codexPorts = createCodexRoutePorts(log, paths.chatgptSignInFile);
@@ -146500,20 +146672,19 @@ function realFrontDoorSupervisorPorts(paths, respawnGeneration, lifecycle, track
146500
146672
  return { port: handle.port, listening: server, close: handle.close, drain: handle.drain };
146501
146673
  },
146502
146674
  closeRcStreamHub: rcHub.close,
146675
+ respawnChain: {
146676
+ generation: respawnGeneration,
146677
+ startNext: async () => {
146678
+ await startNextRespawnGeneration(respawnChain());
146679
+ }
146680
+ },
146503
146681
  startWatchdog: () => startThreadWatchdog({
146682
+ ...respawnChain(),
146504
146683
  stateFile: paths.frontdoorStateFile,
146505
- ownPid: process.pid,
146506
146684
  boundMs: DOOR_STALL_BOUND_MS,
146507
146685
  checkEveryMs: DOOR_WATCHDOG_CHECK_MS,
146508
146686
  pauseWaitMs: DOOR_WATCHDOG_CHECK_MS,
146509
146687
  action: "kill-process",
146510
- logFile: paths.frontdoorLogPath,
146511
- respawn: respawnInvocation(paths),
146512
- respawnGeneration,
146513
- respawnMaxGeneration: DOOR_RESPAWN_MAX_GENERATIONS,
146514
- cooldownStepMs: DOOR_STALL_BOUND_MS,
146515
- cooldownCapMs: DOOR_RESPAWN_COOLDOWN_CAP_MS,
146516
- startLockFile: paths.frontdoorLockFile,
146517
146688
  ...process.platform === "darwin" ? { nativeSample: { command: MACOS_SAMPLER, args: ["{pid}", "{seconds}", "-file", "{file}"], directory: import_node_path61.default.join(import_node_path61.default.dirname(paths.frontdoorLogPath), "stall-samples"), seconds: Math.max(1, Math.ceil(DOOR_WATCHDOG_CHECK_MS / MS_PER_SECOND17)), overheadMs: PROBE_TIMEOUT_MS, keep: DOOR_RESPAWN_MAX_GENERATIONS + 1 } } : {}
146518
146689
  }),
146519
146690
  // The launch lifecycle publisher over the same backbone the typed API serves: the tick's registry facts go in here, and the door's own register, prune and end moments come out as source-tagged events.
@@ -146627,7 +146798,7 @@ function registerFrontDoorCommand(program2, deps) {
146627
146798
 
146628
146799
  // src/statusline/commands.ts
146629
146800
  var import_node_child_process9 = require("node:child_process");
146630
- var import_node_os15 = __toESM(require("node:os"), 1);
146801
+ var import_node_os16 = __toESM(require("node:os"), 1);
146631
146802
  var import_node_path70 = __toESM(require("node:path"), 1);
146632
146803
  init_commandDeps();
146633
146804
  init_load();
@@ -146769,20 +146940,21 @@ function windowsOf(states, nowMs) {
146769
146940
  let fiveHourResetAt;
146770
146941
  let sevenDayUsed;
146771
146942
  let sevenDayResetAt;
146943
+ let bindingUsed;
146772
146944
  for (const state of states) {
146773
146945
  const unified = state.rateLimit?.unified;
146774
146946
  if (unified === void 0) {
146775
146947
  continue;
146776
146948
  }
146777
146949
  const five = livePercentOf(unified.fiveHour, nowMs);
146778
- if (five !== void 0 && (fiveHourUsed === void 0 || five > fiveHourUsed)) {
146779
- fiveHourUsed = five;
146780
- fiveHourResetAt = unified.fiveHour?.resetsAt;
146781
- }
146782
146950
  const seven = livePercentOf(unified.sevenDay, nowMs);
146783
- if (seven !== void 0 && (sevenDayUsed === void 0 || seven > sevenDayUsed)) {
146951
+ const max = five === void 0 ? seven : seven === void 0 ? five : Math.max(five, seven);
146952
+ if (max !== void 0 && (bindingUsed === void 0 || max > bindingUsed)) {
146953
+ bindingUsed = max;
146954
+ fiveHourUsed = five;
146955
+ fiveHourResetAt = five === void 0 ? void 0 : unified.fiveHour?.resetsAt;
146784
146956
  sevenDayUsed = seven;
146785
- sevenDayResetAt = unified.sevenDay?.resetsAt;
146957
+ sevenDayResetAt = seven === void 0 ? void 0 : unified.sevenDay?.resetsAt;
146786
146958
  }
146787
146959
  }
146788
146960
  return { fiveHourUsed, fiveHourResetAt, sevenDayUsed, sevenDayResetAt };
@@ -146828,8 +147000,9 @@ function antigravityQuotaMapOf(payload) {
146828
147000
  );
146829
147001
  }
146830
147002
  function bucketView(bucket, nowMs) {
147003
+ const expired = bucket?.reset_in_seconds !== void 0 && Number.isFinite(bucket.reset_in_seconds) && bucket.reset_in_seconds <= 0;
146831
147004
  return {
146832
- used: bucket?.remaining_fraction === void 0 || !Number.isFinite(bucket.remaining_fraction) ? void 0 : (1 - Math.min(1, Math.max(0, bucket.remaining_fraction))) * FULL_PERCENT2,
147005
+ used: bucket?.remaining_fraction === void 0 || !Number.isFinite(bucket.remaining_fraction) ? void 0 : expired ? 0 : (1 - Math.min(1, Math.max(0, bucket.remaining_fraction))) * FULL_PERCENT2,
146833
147006
  resetAt: bucket?.reset_in_seconds === void 0 || !Number.isFinite(bucket.reset_in_seconds) ? void 0 : isoOf(nowMs + bucket.reset_in_seconds * MS_PER_SECOND18)
146834
147007
  };
146835
147008
  }
@@ -146879,10 +147052,12 @@ function enrichStatuslinePayload(params) {
146879
147052
  ...params.modelChoice === void 0 ? {} : { modelChoice: params.modelChoice },
146880
147053
  ...params.doorCostUsd === void 0 ? {} : { doorCostUsd: params.doorCostUsd }
146881
147054
  };
147055
+ let scopedName;
146882
147056
  if (params.snapshot !== void 0) {
146883
- const scopedName = providerScopedState(params.snapshot, params.providerName, params.env.AGENT_SHIM_PROVIDER);
147057
+ const routedProvider = routedProviderOf(params);
147058
+ scopedName = providerScopedState(params.snapshot, params.providerName, params.env.AGENT_SHIM_PROVIDER) ?? providerScopedState(params.snapshot, routedProvider, routedProvider);
146884
147059
  const scopedState = scopedName === void 0 ? void 0 : params.snapshot.providers[scopedName];
146885
- const states = summary.provider === void 0 ? Object.values(params.snapshot.providers) : scopedState === void 0 ? [] : [scopedState];
147060
+ const states = summary.provider === void 0 && scopedState === void 0 ? Object.values(params.snapshot.providers) : scopedState === void 0 ? [] : [scopedState];
146886
147061
  let email3;
146887
147062
  let sub;
146888
147063
  let plan;
@@ -146913,7 +147088,7 @@ function enrichStatuslinePayload(params) {
146913
147088
  );
146914
147089
  summary = { ...summary, ...windowsOf(states, params.nowMs), providerAccountEmail: email3, providerAccountSub: sub, providerPlan: plan, providerQuotaWindows, quotaFamilies: quotaFamiliesOf(providerBuckets, params.nowMs) };
146915
147090
  }
146916
- if (summary.provider === void 0) {
147091
+ if (summary.provider === void 0 && scopedName === void 0) {
146917
147092
  const agyBuckets = antigravityQuotaMapOf(params.payload);
146918
147093
  if (agyBuckets !== void 0) {
146919
147094
  summary = { ...summary, quotaFamilies: quotaFamiliesOf(agyBuckets, params.nowMs) };
@@ -146935,6 +147110,29 @@ function enrichStatuslinePayload(params) {
146935
147110
  }
146936
147111
  return { payload: enriched, summary };
146937
147112
  }
147113
+ function routedProviderOf(params) {
147114
+ if (params.modelChoice?.provider !== void 0 && params.modelChoice.provider !== "") {
147115
+ return params.modelChoice.provider;
147116
+ }
147117
+ const targetPrefix = prefixBeforeSlash(params.routedTarget);
147118
+ if (targetPrefix !== void 0) {
147119
+ return targetPrefix;
147120
+ }
147121
+ const model = isObject4(params.payload) && isObject4(params.payload.model) ? params.payload.model : void 0;
147122
+ return prefixBeforeSlash(typeof model?.id === "string" ? model.id : void 0) ?? prefixBeforeSlash(typeof model?.display_name === "string" ? model.display_name : void 0);
147123
+ }
147124
+ function prefixBeforeSlash(value3) {
147125
+ if (value3 === void 0) {
147126
+ return void 0;
147127
+ }
147128
+ const slash = value3.indexOf("/");
147129
+ if (slash <= 0) {
147130
+ return void 0;
147131
+ }
147132
+ const prefix = value3.slice(0, slash).trim();
147133
+ const rest = value3.slice(slash + 1).trim();
147134
+ return prefix === "" || rest === "" ? void 0 : prefix;
147135
+ }
146938
147136
  function providerScopedState(snapshot, providerName, providerDisplayName) {
146939
147137
  if (providerName !== void 0 && providerName in snapshot.providers) {
146940
147138
  return providerName;
@@ -147418,7 +147616,7 @@ function contextViewOf(frame) {
147418
147616
  // src/statusline/ports.ts
147419
147617
  init_blockingSpawn();
147420
147618
  var import_node_fs26 = __toESM(require("node:fs"), 1);
147421
- var import_node_os14 = __toESM(require("node:os"), 1);
147619
+ var import_node_os15 = __toESM(require("node:os"), 1);
147422
147620
  var import_node_path63 = __toESM(require("node:path"), 1);
147423
147621
 
147424
147622
  // src/statusline/memoryUse.ts
@@ -147552,7 +147750,7 @@ function discard(target) {
147552
147750
  }
147553
147751
  }
147554
147752
  function readMemoryUse() {
147555
- const totalBytes = import_node_os14.default.totalmem();
147753
+ const totalBytes = import_node_os15.default.totalmem();
147556
147754
  if (process.platform === "darwin") {
147557
147755
  const output2 = runWithPipe(RUN_TIMEOUT_MS, "/usr/bin/vm_stat", [], process.cwd());
147558
147756
  const usedBytes = output2 === void 0 ? void 0 : usedBytesFromVmStat(output2);
@@ -147562,13 +147760,13 @@ function readMemoryUse() {
147562
147760
  const meminfo = readText("/proc/meminfo");
147563
147761
  return meminfo === void 0 ? void 0 : memoryUseFromMeminfo(meminfo);
147564
147762
  }
147565
- return { usedBytes: totalBytes - import_node_os14.default.freemem(), totalBytes };
147763
+ return { usedBytes: totalBytes - import_node_os15.default.freemem(), totalBytes };
147566
147764
  }
147567
147765
  function realStatuslinePorts(env, stateDirectory, runTimeoutMs = RUN_TIMEOUT_MS) {
147568
147766
  const { locale, timeZone } = Intl.DateTimeFormat().resolvedOptions();
147569
147767
  return {
147570
147768
  env,
147571
- homeDirectory: import_node_os14.default.homedir(),
147769
+ homeDirectory: import_node_os15.default.homedir(),
147572
147770
  stateDirectory,
147573
147771
  clock: { locale, timeZone },
147574
147772
  readText,
@@ -151240,7 +151438,7 @@ function registerStatuslineCommand(program2, deps) {
151240
151438
  } else {
151241
151439
  const configDir = process.env.CLAUDE_CONFIG_DIR;
151242
151440
  try {
151243
- account = readAccountMetadataFile(realFarmFs, configDir === void 0 || configDir === "" ? import_node_path70.default.join(import_node_os15.default.homedir(), ".claude.json") : import_node_path70.default.join(configDir, ".claude.json"));
151441
+ account = readAccountMetadataFile(realFarmFs, configDir === void 0 || configDir === "" ? import_node_path70.default.join(import_node_os16.default.homedir(), ".claude.json") : import_node_path70.default.join(configDir, ".claude.json"));
151244
151442
  } catch {
151245
151443
  account = void 0;
151246
151444
  }
@@ -151289,7 +151487,7 @@ function registerStatuslineCommand(program2, deps) {
151289
151487
  });
151290
151488
  return;
151291
151489
  }
151292
- const live = statuslineSettingsForRender({ env: process.env, cwd: process.cwd(), home: import_node_os15.default.homedir(), paths: deps.paths, fs: { ...realFsPort, readConfigFile: cosmiconfigReader() } });
151490
+ const live = statuslineSettingsForRender({ env: process.env, cwd: process.cwd(), home: import_node_os16.default.homedir(), paths: deps.paths, fs: { ...realFsPort, readConfigFile: cosmiconfigReader() } });
151293
151491
  if (live.problem !== void 0) {
151294
151492
  process.stderr.write(`agent-shim: statusline: ${live.problem}. Drawing with the defaults.
151295
151493
  `);
@@ -151302,7 +151500,7 @@ function registerStatuslineCommand(program2, deps) {
151302
151500
  let stackedUnderDefault = false;
151303
151501
  if (isAntigravityPayload(payload)) {
151304
151502
  try {
151305
- stackedUnderDefault = antigravityStacksWithDefault(realFarmFs, import_node_os15.default.homedir());
151503
+ stackedUnderDefault = antigravityStacksWithDefault(realFarmFs, import_node_os16.default.homedir());
151306
151504
  } catch {
151307
151505
  stackedUnderDefault = false;
151308
151506
  }
package/dist/index.cjs CHANGED
@@ -4183,13 +4183,18 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4183
4183
  });
4184
4184
  const { predecessor } = ports;
4185
4185
  const sourceOf = (listener, preferredPort) => predecessor === void 0 ? { preferredPort } : { inherit: async () => await predecessor.take(listener) };
4186
- const fail = (message) => {
4186
+ const fail = async (message) => {
4187
4187
  ports.closeRcStreamHub();
4188
4188
  if (predecessor === void 0) {
4189
4189
  writeFrontDoorState(fs15, paths.frontdoorStateFile, { lastError: message, ...sticky() });
4190
4190
  fs15.removeRecursive(paths.frontdoorLockFile);
4191
4191
  }
4192
4192
  ports.log(`agent-shim frontdoor supervisor: ${message}`);
4193
+ const chain = ports.respawnChain;
4194
+ if (predecessor === void 0 && chain !== void 0 && chain.generation > 0) {
4195
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: this door is generation ${String(chain.generation)} of the watchdog's respawn chain and nothing waits on it, so it starts generation ${String(chain.generation + 1)} rather than leaving no door`);
4196
+ await chain.startNext();
4197
+ }
4193
4198
  return 1;
4194
4199
  };
4195
4200
  if (predecessor === void 0) {
@@ -4201,7 +4206,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4201
4206
  http3 = await ports.startProviderListener(sourceOf("provider", lastPort));
4202
4207
  } catch (error) {
4203
4208
  ports.doorHealth.listenerFailed("provider", error instanceof Error ? error.message : String(error));
4204
- return fail(`could not start the provider listener: ${error instanceof Error ? error.message : String(error)}`);
4209
+ return await fail(`could not start the provider listener: ${error instanceof Error ? error.message : String(error)}`);
4205
4210
  }
4206
4211
  if (previousSticky !== void 0 && http3.port !== previousSticky) {
4207
4212
  ports.log(`agent-shim frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(http3.port)} (sessions launched against the old port are stale until they relaunch)`);
@@ -4214,7 +4219,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4214
4219
  } catch (error) {
4215
4220
  await http3.close();
4216
4221
  ports.doorHealth.listenerFailed("connect", error instanceof Error ? error.message : String(error));
4217
- return fail(`could not start the CONNECT surface: ${error instanceof Error ? error.message : String(error)}`);
4222
+ return await fail(`could not start the CONNECT surface: ${error instanceof Error ? error.message : String(error)}`);
4218
4223
  }
4219
4224
  if (previousStickyConnect !== void 0 && connect3.port !== previousStickyConnect) {
4220
4225
  ports.log(`agent-shim frontdoor supervisor: sticky CONNECT port ${String(previousStickyConnect)} was occupied; moving to ${String(connect3.port)} (OAuth sessions launched against the old port are stale until they relaunch)`);
@@ -4228,7 +4233,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4228
4233
  await connect3.close();
4229
4234
  await http3.close();
4230
4235
  ports.doorHealth.listenerFailed("direct", error instanceof Error ? error.message : String(error));
4231
- return fail(`could not start the direct listener: ${error instanceof Error ? error.message : String(error)}`);
4236
+ return await fail(`could not start the direct listener: ${error instanceof Error ? error.message : String(error)}`);
4232
4237
  }
4233
4238
  if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
4234
4239
  ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom admits the new address once its supervisor rewrites the allowlist file)`);
@@ -4848,7 +4853,7 @@ var init_package = __esm({
4848
4853
  "package.json"() {
4849
4854
  package_default = {
4850
4855
  name: "agent-shim",
4851
- version: "11.83.4",
4856
+ version: "11.83.6",
4852
4857
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
4853
4858
  license: "MIT",
4854
4859
  author: "Joseph Mearman <joseph@mearman.co.uk>",
package/dist/index.mjs CHANGED
@@ -4160,13 +4160,18 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4160
4160
  });
4161
4161
  const { predecessor } = ports;
4162
4162
  const sourceOf = (listener, preferredPort) => predecessor === void 0 ? { preferredPort } : { inherit: async () => await predecessor.take(listener) };
4163
- const fail = (message) => {
4163
+ const fail = async (message) => {
4164
4164
  ports.closeRcStreamHub();
4165
4165
  if (predecessor === void 0) {
4166
4166
  writeFrontDoorState(fs15, paths.frontdoorStateFile, { lastError: message, ...sticky() });
4167
4167
  fs15.removeRecursive(paths.frontdoorLockFile);
4168
4168
  }
4169
4169
  ports.log(`agent-shim frontdoor supervisor: ${message}`);
4170
+ const chain = ports.respawnChain;
4171
+ if (predecessor === void 0 && chain !== void 0 && chain.generation > 0) {
4172
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: this door is generation ${String(chain.generation)} of the watchdog's respawn chain and nothing waits on it, so it starts generation ${String(chain.generation + 1)} rather than leaving no door`);
4173
+ await chain.startNext();
4174
+ }
4170
4175
  return 1;
4171
4176
  };
4172
4177
  if (predecessor === void 0) {
@@ -4178,7 +4183,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4178
4183
  http3 = await ports.startProviderListener(sourceOf("provider", lastPort));
4179
4184
  } catch (error) {
4180
4185
  ports.doorHealth.listenerFailed("provider", error instanceof Error ? error.message : String(error));
4181
- return fail(`could not start the provider listener: ${error instanceof Error ? error.message : String(error)}`);
4186
+ return await fail(`could not start the provider listener: ${error instanceof Error ? error.message : String(error)}`);
4182
4187
  }
4183
4188
  if (previousSticky !== void 0 && http3.port !== previousSticky) {
4184
4189
  ports.log(`agent-shim frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(http3.port)} (sessions launched against the old port are stale until they relaunch)`);
@@ -4191,7 +4196,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4191
4196
  } catch (error) {
4192
4197
  await http3.close();
4193
4198
  ports.doorHealth.listenerFailed("connect", error instanceof Error ? error.message : String(error));
4194
- return fail(`could not start the CONNECT surface: ${error instanceof Error ? error.message : String(error)}`);
4199
+ return await fail(`could not start the CONNECT surface: ${error instanceof Error ? error.message : String(error)}`);
4195
4200
  }
4196
4201
  if (previousStickyConnect !== void 0 && connect3.port !== previousStickyConnect) {
4197
4202
  ports.log(`agent-shim frontdoor supervisor: sticky CONNECT port ${String(previousStickyConnect)} was occupied; moving to ${String(connect3.port)} (OAuth sessions launched against the old port are stale until they relaunch)`);
@@ -4205,7 +4210,7 @@ async function serveFrontDoor(idleShutdownMinutes, ports, options) {
4205
4210
  await connect3.close();
4206
4211
  await http3.close();
4207
4212
  ports.doorHealth.listenerFailed("direct", error instanceof Error ? error.message : String(error));
4208
- return fail(`could not start the direct listener: ${error instanceof Error ? error.message : String(error)}`);
4213
+ return await fail(`could not start the direct listener: ${error instanceof Error ? error.message : String(error)}`);
4209
4214
  }
4210
4215
  if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
4211
4216
  ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom admits the new address once its supervisor rewrites the allowlist file)`);
@@ -4824,7 +4829,7 @@ var init_package = __esm({
4824
4829
  "package.json"() {
4825
4830
  package_default = {
4826
4831
  name: "agent-shim",
4827
- version: "11.83.4",
4832
+ version: "11.83.6",
4828
4833
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
4829
4834
  license: "MIT",
4830
4835
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -26355,6 +26355,13 @@ interface FrontDoorSupervisorPorts {
26355
26355
  * The door this one replaces through a handover, when it was started for one: its listeners are adopted from that door's sockets instead of bound, the state file is not written until this door accepts on all three (the old door owns it and keeps serving until then), and the old door is told once this door serves. Absent for every other start.
26356
26356
  */
26357
26357
  readonly predecessor?: Predecessor;
26358
+ /**
26359
+ * The watchdog's respawn chain this door belongs to: `generation` is how many watchdog replacements produced it (0 for a door a launch or `frontdoor restart` started), and `startNext` starts the door of the next generation the way the watchdog starts one on a stall (see `startNextRespawnGeneration`). A door of generation 1 or more whose start fails calls `startNext` before it exits, once its listeners are down and it has released the start lock, because nobody else would start a door: the stalled door that started it is gone and no launch is waiting on it, so every registered session would get connection refused until some later launch happened to start one. A door a launch started reports its failure to that launch instead, and a handover successor leaves the door it was replacing serving. Optional for a host that does not respawn.
26360
+ */
26361
+ readonly respawnChain?: {
26362
+ readonly generation: number;
26363
+ readonly startNext: () => Promise<void>;
26364
+ };
26358
26365
  /** Stops the work the door runs on its own account rather than for a request (the usage, window, job and catalogue keepers), called once when a drain begins so the successor's are the only ones acting. Optional because a host without such work has nothing to stop. */
26359
26366
  readonly stopBackgroundWork?: () => void;
26360
26367
  /**
@@ -26383,7 +26390,7 @@ interface RunFrontDoorSupervisorOptions {
26383
26390
  /**
26384
26391
  * Runs the front-door supervisor: binds all three listeners (the HTTPS provider listener, the CONNECT surface and the plain-HTTP direct listener, each on its sticky port), records the serving state, and shuts down with an empty session registry, either at the first such tick after the executable it serves as has been replaced on disk (so an applied update reaches the door without waiting out the idle window) or after `idleShutdownMinutes` of it. A door whose state file comes to name another live supervisor is superseded: from then on it takes no new sessions, never writes the state file again, and exits as soon as the sessions it was serving have ended. Returns the exit code: 0 for any of these retirements, 1 for a fatal start failure.
26385
26392
  *
26386
- * The listeners live in this process, so there is no child to keep alive: a crash of this process is a crash of every routed session's door at once, and recovery is the next launch's ensure spawning a replacement on the same sticky ports, which is exactly the failure model the issue asks to settle with tests. Keeping the headroom daemon alive is the headroom supervisor's job, not this one's; the two daemons idle out independently.
26393
+ * The listeners live in this process, so there is no child to keep alive: a crash of this process is a crash of every routed session's door at once, and recovery is the next launch's ensure spawning a replacement on the same sticky ports, or, for a stall or a failed start in a door the watchdog's respawn chain started, the next generation of that chain (see `FrontDoorSupervisorPorts.respawnChain`). Keeping the headroom daemon alive is the headroom supervisor's job, not this one's; the two daemons idle out independently.
26387
26394
  */
26388
26395
  declare function runFrontDoorSupervisor(idleShutdownMinutes: number, ports: FrontDoorSupervisorPorts, options?: RunFrontDoorSupervisorOptions): Promise<number>;
26389
26396
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-shim",
3
- "version": "11.83.4",
3
+ "version": "11.83.6",
4
4
  "description": "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
5
5
  "license": "MIT",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",