agent-shim 8.30.0 → 8.32.0

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
@@ -61796,7 +61796,7 @@ function registerIdentityCommand(program2, deps) {
61796
61796
  // package.json
61797
61797
  var package_default = {
61798
61798
  name: "agent-shim",
61799
- version: "8.30.0",
61799
+ version: "8.32.0",
61800
61800
  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.",
61801
61801
  license: "MIT",
61802
61802
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -62775,10 +62775,10 @@ function windowBlockedUntil(windows) {
62775
62775
  const resets = windows.flatMap((window) => window.status === "rejected" && window.resetsAtMs !== void 0 ? [window.resetsAtMs] : []);
62776
62776
  return resets.length === 0 ? void 0 : Math.max(...resets);
62777
62777
  }
62778
- function fiveHourBurn(records) {
62778
+ function windowBurn(records, which) {
62779
62779
  const points = [];
62780
62780
  for (const record2 of records) {
62781
- const window = record2.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record2.rateLimitHeaders)?.fiveHour;
62781
+ const window = record2.rateLimitHeaders === void 0 ? void 0 : parseUnifiedRateLimit(record2.rateLimitHeaders)?.[which];
62782
62782
  if (window?.utilization !== void 0 && window.resetsAt !== void 0) {
62783
62783
  points.push({ resetsAt: window.resetsAt, point: { atMs: Date.parse(record2.at), utilization: window.utilization } });
62784
62784
  }
@@ -62872,11 +62872,16 @@ function assessIdentity(member, nowMs) {
62872
62872
  const score = remaining * plan.capacity / (untilResetMs / MS_PER_HOUR);
62873
62873
  const fiveRemaining = five?.utilization === void 0 ? void 0 : Math.max(0, 1 - five.utilization);
62874
62874
  const fiveUntil = five?.resetsAtMs === void 0 ? FIVE_HOUR_WINDOW_MS : Math.max(five.resetsAtMs - nowMs, MS_PER_SECOND3);
62875
- const burn = fiveHourBurn(member.records);
62875
+ const burn = windowBurn(member.records, "fiveHour");
62876
+ const sevenRemaining = seven?.utilization === void 0 ? void 0 : Math.max(0, 1 - seven.utilization);
62877
+ const sevenUntil = seven?.resetsAtMs === void 0 ? SEVEN_DAY_WINDOW_MS : Math.max(seven.resetsAtMs - nowMs, MS_PER_SECOND3);
62878
+ const sevenDayBurn = windowBurn(member.records, "sevenDay");
62876
62879
  return {
62877
62880
  candidate: { ...base, class: "scored", score, reasons: [...windowReasons.map((reason, index) => index === windowReasons.length - 1 ? `${reason}${observedAgo}` : reason), ...planReasons] },
62878
62881
  ...fiveRemaining === void 0 ? {} : { fiveHour: { remaining: fiveRemaining, untilResetMs: fiveUntil } },
62879
- ...burn === void 0 ? {} : { burn }
62882
+ ...burn === void 0 ? {} : { burn },
62883
+ ...sevenRemaining === void 0 ? {} : { sevenDay: { remaining: sevenRemaining, untilResetMs: sevenUntil } },
62884
+ ...sevenDayBurn === void 0 ? {} : { sevenDayBurn }
62880
62885
  };
62881
62886
  }
62882
62887
  function rankPool(input2) {
@@ -62887,14 +62892,27 @@ function rankPool(input2) {
62887
62892
  const candidates = assessments.map((assessment) => {
62888
62893
  const { candidate, fiveHour, burn } = assessment;
62889
62894
  const pace = burn ?? (sharedDemand === void 0 ? void 0 : sharedDemand / capacityOf(candidate.plan));
62890
- if (candidate.class !== "scored" || fiveHour === void 0 || pace === void 0) {
62895
+ const { sevenDay, sevenDayBurn } = assessment;
62896
+ if (candidate.class !== "scored") {
62891
62897
  return { ...candidate, feasible: true };
62892
62898
  }
62893
- const runsDryInMs = fiveHour.remaining / pace;
62894
- if (runsDryInMs >= fiveHour.untilResetMs) {
62899
+ const drySpots = [];
62900
+ if (fiveHour !== void 0 && pace !== void 0) {
62901
+ const runsDryInMs = fiveHour.remaining / pace;
62902
+ if (runsDryInMs < fiveHour.untilResetMs) {
62903
+ drySpots.push(`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`);
62904
+ }
62905
+ }
62906
+ if (sevenDay !== void 0 && sevenDayBurn !== void 0) {
62907
+ const runsDryInMs = sevenDay.remaining / sevenDayBurn;
62908
+ if (runsDryInMs < sevenDay.untilResetMs) {
62909
+ drySpots.push(`7d window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(sevenDay.untilResetMs)}`);
62910
+ }
62911
+ }
62912
+ if (drySpots.length === 0) {
62895
62913
  return { ...candidate, feasible: true };
62896
62914
  }
62897
- return { ...candidate, feasible: false, reasons: [`5h window would run dry in ${formatAge(runsDryInMs)}, before it resets in ${formatAge(fiveHour.untilResetMs)}`, ...candidate.reasons] };
62915
+ return { ...candidate, feasible: false, reasons: [...drySpots, ...candidate.reasons] };
62898
62916
  });
62899
62917
  const ordered = input2.preference === "listed" ? [...candidates] : [...candidates].sort(
62900
62918
  (left, right) => CLASS_ORDER[left.class] - CLASS_ORDER[right.class] || Number(right.feasible) - Number(left.feasible) || (right.score ?? 0) - (left.score ?? 0) || left.identity.localeCompare(right.identity)
@@ -62902,7 +62920,8 @@ function rankPool(input2) {
62902
62920
  const sticky = input2.sticky;
62903
62921
  const stickyIndex = sticky === void 0 ? -1 : ordered.findIndex((candidate) => candidate.identity === sticky.identity);
62904
62922
  const stickyCandidate = ordered[stickyIndex];
62905
- if (sticky !== void 0 && stickyCandidate?.class === "scored" && stickyCandidate.feasible) {
62923
+ const keepSticky = stickyCandidate?.class === "scored" && stickyCandidate.feasible;
62924
+ if (sticky !== void 0 && keepSticky) {
62906
62925
  const ageMs = nowMs - Date.parse(sticky.at);
62907
62926
  const warm = ageMs <= PROMPT_CACHE_TTL_MS;
62908
62927
  if (input2.resuming || warm) {
@@ -62911,12 +62930,14 @@ function rankPool(input2) {
62911
62930
  ordered.unshift({ ...stickyCandidate, reasons: [why, ...stickyCandidate.reasons] });
62912
62931
  }
62913
62932
  }
62933
+ const movedOff = sticky !== void 0 && input2.resuming && !keepSticky ? { identity: sticky.identity, reason: stickyCandidate?.reasons[0] ?? "no longer among the pool's members" } : void 0;
62914
62934
  const pick2 = ordered.find((candidate) => candidate.class !== "ineligible");
62915
62935
  const returns = ordered.flatMap((candidate) => candidate.blockedUntilMs === void 0 ? [] : [{ identity: candidate.identity, atMs: candidate.blockedUntilMs }]);
62916
62936
  const earliestReturn = returns.length === 0 ? void 0 : returns.reduce((soonest, entry) => entry.atMs < soonest.atMs ? entry : soonest);
62917
62937
  return {
62918
62938
  candidates: ordered,
62919
- ...pick2 === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick: pick2 }
62939
+ ...pick2 === void 0 ? { ...earliestReturn === void 0 ? {} : { earliestReturn } } : { pick: pick2 },
62940
+ ...movedOff === void 0 || pick2 === void 0 ? {} : { movedOff }
62920
62941
  };
62921
62942
  }
62922
62943
 
@@ -63150,6 +63171,7 @@ function createUsageStore(deps) {
63150
63171
  fs20.mkdirPrivate(paths.usageSnapshotsDir);
63151
63172
  fs20.writeFilePrivate(snapshotPath(paths.usageSnapshotsDir, identity), `${JSON.stringify(snapshot, null, 2)}
63152
63173
  `);
63174
+ deps.publish?.(snapshot);
63153
63175
  return true;
63154
63176
  };
63155
63177
  return {
@@ -63331,6 +63353,7 @@ function collectPoolPick(params) {
63331
63353
  candidates: ranking.candidates.map(candidateView),
63332
63354
  missing: missing.map(({ identity }) => identity),
63333
63355
  ...ranking.earliestReturn === void 0 ? {} : { earliestReturn: { identity: ranking.earliestReturn.identity, at: new Date(ranking.earliestReturn.atMs).toISOString() } },
63356
+ ...ranking.movedOff === void 0 ? {} : { movedOff: ranking.movedOff },
63334
63357
  ...sticky.problem === void 0 ? {} : { stickyProblem: sticky.problem }
63335
63358
  };
63336
63359
  }
@@ -65682,6 +65705,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
65682
65705
  try {
65683
65706
  http8 = await ports.startProviderListener(lastPort);
65684
65707
  } catch (error62) {
65708
+ ports.doorHealth.listenerFailed("provider", error62 instanceof Error ? error62.message : String(error62));
65685
65709
  return fail(`could not start the provider listener: ${error62 instanceof Error ? error62.message : String(error62)}`);
65686
65710
  }
65687
65711
  if (previousSticky !== void 0 && http8.port !== previousSticky) {
@@ -65694,6 +65718,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
65694
65718
  connect3 = await ports.startConnectListener(lastConnectPort);
65695
65719
  } catch (error62) {
65696
65720
  await http8.close();
65721
+ ports.doorHealth.listenerFailed("connect", error62 instanceof Error ? error62.message : String(error62));
65697
65722
  return fail(`could not start the CONNECT surface: ${error62 instanceof Error ? error62.message : String(error62)}`);
65698
65723
  }
65699
65724
  if (previousStickyConnect !== void 0 && connect3.port !== previousStickyConnect) {
@@ -65707,6 +65732,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
65707
65732
  } catch (error62) {
65708
65733
  await connect3.close();
65709
65734
  await http8.close();
65735
+ ports.doorHealth.listenerFailed("direct", error62 instanceof Error ? error62.message : String(error62));
65710
65736
  return fail(`could not start the direct listener: ${error62 instanceof Error ? error62.message : String(error62)}`);
65711
65737
  }
65712
65738
  if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
@@ -65714,6 +65740,7 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
65714
65740
  }
65715
65741
  lastDirectPort = direct.port;
65716
65742
  writeFrontDoorState(fs20, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http8.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
65743
+ ports.doorHealth.generation({ providerPort: http8.port, connectPort: connect3.port, directPort: direct.port });
65717
65744
  fs20.removeRecursive(paths.frontdoorLockFile);
65718
65745
  ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http8.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}`);
65719
65746
  let idleSince;
@@ -65726,11 +65753,13 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
65726
65753
  const listed = listFrontDoorSessions(fs20, paths.frontdoorSessionsDir);
65727
65754
  const pruned = pruneDeadFrontDoorSessions(fs20, paths.frontdoorSessionsDir, ports.isRunning);
65728
65755
  ports.observeLaunchRegistry(listed, pruned);
65756
+ ports.checkQuotaExpiry();
65729
65757
  const prunedPids = new Set(pruned);
65730
65758
  if (listed.filter((session) => !prunedPids.has(session.pid)).length === 0) {
65731
65759
  idleSince ??= ports.now();
65732
65760
  if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE2) {
65733
65761
  ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
65762
+ ports.doorHealth.idleShutdown();
65734
65763
  await direct.close();
65735
65764
  await connect3.close();
65736
65765
  await http8.close();
@@ -69604,6 +69633,8 @@ var PoolPickReportSchema = external_exports.strictObject({
69604
69633
  missing: external_exports.array(external_exports.string()),
69605
69634
  /** When nothing can be picked, the soonest any member returns. */
69606
69635
  earliestReturn: external_exports.strictObject({ identity: external_exports.string(), at: external_exports.iso.datetime() }).optional(),
69636
+ /** The member a resumed conversation started on but moved off, and why, when the ranking could not keep it. */
69637
+ movedOff: external_exports.strictObject({ identity: external_exports.string(), reason: external_exports.string() }).optional(),
69607
69638
  /** Why the last-pick record could not be read, when that is the case. */
69608
69639
  stickyProblem: external_exports.string().optional()
69609
69640
  });
@@ -73910,6 +73941,9 @@ function frontDoorRcApiClient(port, ca, token) {
73910
73941
  // src/frontdoor/eventSchemas.ts
73911
73942
  var DOOR_EVENT_SOURCE_RC = "rc";
73912
73943
  var LAUNCH_EVENT_SOURCE = "launch";
73944
+ var USAGE_EVENT_SOURCE = "usage";
73945
+ var DOOR_HEALTH_EVENT_SOURCE = "door";
73946
+ var QUOTA_EXPIRING_EVENT_SOURCE = "quota-expiring";
73913
73947
  var DoorEventSchema = external_exports.strictObject({
73914
73948
  source: external_exports.string().min(1),
73915
73949
  /** A positive integer because the backbone's counters start at one and only ever increase. */
@@ -73925,6 +73959,20 @@ var LaunchLifecycleEventSchema = external_exports.strictObject({
73925
73959
  startedAt: external_exports.number(),
73926
73960
  observedAt: external_exports.number()
73927
73961
  });
73962
+ var DoorHealthEventSchema = external_exports.discriminatedUnion("kind", [
73963
+ external_exports.strictObject({ kind: external_exports.literal("generation"), pid: external_exports.number().int().positive(), providerPort: external_exports.number().int().positive(), connectPort: external_exports.number().int().positive(), directPort: external_exports.number().int().positive(), observedAt: external_exports.number() }),
73964
+ external_exports.strictObject({ kind: external_exports.literal("listenerFailed"), pid: external_exports.number().int().positive(), listener: external_exports.enum(["provider", "connect", "direct"]), message: external_exports.string(), observedAt: external_exports.number() }),
73965
+ external_exports.strictObject({ kind: external_exports.literal("idleShutdown"), pid: external_exports.number().int().positive(), observedAt: external_exports.number() })
73966
+ ]);
73967
+ var QuotaExpiringEventSchema = external_exports.strictObject({
73968
+ identity: external_exports.string().min(1),
73969
+ provider: external_exports.string().min(1),
73970
+ window: external_exports.enum(["fiveHour", "sevenDay"]),
73971
+ /** The fraction of the window already used when it was seen expiring, always below one because a fully used window has nothing left to spend. */
73972
+ utilization: external_exports.number().min(0).max(1),
73973
+ resetsAt: external_exports.iso.datetime(),
73974
+ observedAt: external_exports.number()
73975
+ });
73928
73976
 
73929
73977
  // src/frontdoor/eventsApi.ts
73930
73978
  var EVENTS_API_TAG = "events";
@@ -74035,6 +74083,22 @@ function createDoorApiNodeHandler(deps) {
74035
74083
  return doorApiNodeHandlerOf(createDoorApiRouter(deps), deps.expectedToken);
74036
74084
  }
74037
74085
 
74086
+ // src/frontdoor/doorHealthEvents.ts
74087
+ function createDoorHealthPublisher(hub, now, pid) {
74088
+ const publisher = hub.publisher(DOOR_HEALTH_EVENT_SOURCE);
74089
+ return {
74090
+ generation: (ports) => {
74091
+ publisher.publish({ kind: "generation", pid, providerPort: ports.providerPort, connectPort: ports.connectPort, directPort: ports.directPort, observedAt: now() });
74092
+ },
74093
+ listenerFailed: (listener, message) => {
74094
+ publisher.publish({ kind: "listenerFailed", pid, listener, message, observedAt: now() });
74095
+ },
74096
+ idleShutdown: () => {
74097
+ publisher.publish({ kind: "idleShutdown", pid, observedAt: now() });
74098
+ }
74099
+ };
74100
+ }
74101
+
74038
74102
  // src/frontdoor/eventHub.ts
74039
74103
  var DOOR_EVENT_BUFFER_EVENTS = 100;
74040
74104
  function createDoorEventHub() {
@@ -74141,6 +74205,65 @@ function rcFanoutOnDoorHub(fanout, hub) {
74141
74205
  };
74142
74206
  }
74143
74207
 
74208
+ // src/frontdoor/quotaExpiringEvents.ts
74209
+ var MS_PER_HOUR2 = 36e5;
74210
+ var MS_PER_DAY2 = 864e5;
74211
+ var FIVE_HOUR_WINDOW_HOURS = 5;
74212
+ var SEVEN_DAY_WINDOW_DAYS = 7;
74213
+ var FIVE_HOUR_WINDOW_MS2 = FIVE_HOUR_WINDOW_HOURS * MS_PER_HOUR2;
74214
+ var SEVEN_DAY_WINDOW_MS2 = SEVEN_DAY_WINDOW_DAYS * MS_PER_DAY2;
74215
+ var FINAL_SPAN_DENOMINATOR = 30;
74216
+ var QUOTA_EXPIRING_WINDOW_FRACTION = 1 / FINAL_SPAN_DENOMINATOR;
74217
+ function createQuotaExpiringPublisher(hub, deps) {
74218
+ const publisher = hub.publisher(QUOTA_EXPIRING_EVENT_SOURCE);
74219
+ const latest = /* @__PURE__ */ new Map();
74220
+ for (const snapshot of deps.seed) {
74221
+ latest.set(snapshot.identity, snapshot);
74222
+ }
74223
+ hub.subscribe([USAGE_EVENT_SOURCE], (event) => {
74224
+ const snapshot = UsageSnapshotSchema.parse(event.payload);
74225
+ latest.set(snapshot.identity, snapshot);
74226
+ });
74227
+ const announced = /* @__PURE__ */ new Set();
74228
+ const windowsOf = (snapshot) => {
74229
+ const views = [];
74230
+ for (const provider of Object.keys(snapshot.providers).sort()) {
74231
+ const unified = snapshot.providers[provider]?.rateLimit?.unified;
74232
+ const fiveHour = unified?.fiveHour;
74233
+ if (fiveHour?.utilization !== void 0 && fiveHour.resetsAt !== void 0) {
74234
+ views.push({ provider, view: { window: "fiveHour", spanMs: FIVE_HOUR_WINDOW_MS2, utilization: fiveHour.utilization, resetsAtMs: Date.parse(fiveHour.resetsAt) } });
74235
+ }
74236
+ const sevenDay = unified?.sevenDay;
74237
+ if (sevenDay?.utilization !== void 0 && sevenDay.resetsAt !== void 0) {
74238
+ views.push({ provider, view: { window: "sevenDay", spanMs: SEVEN_DAY_WINDOW_MS2, utilization: sevenDay.utilization, resetsAtMs: Date.parse(sevenDay.resetsAt) } });
74239
+ }
74240
+ }
74241
+ return views;
74242
+ };
74243
+ return {
74244
+ check: () => {
74245
+ const now = deps.now();
74246
+ for (const identity of [...latest.keys()].sort()) {
74247
+ const snapshot = latest.get(identity);
74248
+ if (snapshot === void 0) {
74249
+ continue;
74250
+ }
74251
+ for (const { provider, view } of windowsOf(snapshot)) {
74252
+ if (view.utilization >= 1 || view.resetsAtMs <= now || view.resetsAtMs - now >= view.spanMs * QUOTA_EXPIRING_WINDOW_FRACTION) {
74253
+ continue;
74254
+ }
74255
+ const key = `${identity}:${provider}:${view.window}:${String(view.resetsAtMs)}`;
74256
+ if (announced.has(key)) {
74257
+ continue;
74258
+ }
74259
+ announced.add(key);
74260
+ publisher.publish({ identity, provider, window: view.window, utilization: view.utilization, resetsAt: new Date(view.resetsAtMs).toISOString(), observedAt: now });
74261
+ }
74262
+ }
74263
+ }
74264
+ };
74265
+ }
74266
+
74144
74267
  // src/frontdoor/launchEvents.ts
74145
74268
  function createLaunchEventPublisher(hub, now) {
74146
74269
  const publisher = hub.publisher(LAUNCH_EVENT_SOURCE);
@@ -78277,7 +78400,7 @@ var OPENROUTER_HOST = "openrouter.ai";
78277
78400
  var OPENROUTER_KEY_URL = `https://${OPENROUTER_HOST}/api/v1/key`;
78278
78401
  var OPENROUTER_CREDITS_URL = `https://${OPENROUTER_HOST}/api/v1/credits`;
78279
78402
  var HTTP_FORBIDDEN = 403;
78280
- var MS_PER_DAY2 = HOURS_PER_DAY3 * SECONDS_PER_HOUR * MS_PER_SECOND10;
78403
+ var MS_PER_DAY3 = HOURS_PER_DAY3 * SECONDS_PER_HOUR * MS_PER_SECOND10;
78281
78404
  var OpenRouterKeySchema = external_exports.object({
78282
78405
  data: external_exports.object({
78283
78406
  limit: external_exports.number().nonnegative().nullable(),
@@ -78309,7 +78432,7 @@ async function openRouterGet(url2, schema, apiKey, http8, signal) {
78309
78432
  return parsed.data;
78310
78433
  }
78311
78434
  function nextUtcMidnight(nowMs) {
78312
- return new Date((Math.floor(nowMs / MS_PER_DAY2) + 1) * MS_PER_DAY2).toISOString();
78435
+ return new Date((Math.floor(nowMs / MS_PER_DAY3) + 1) * MS_PER_DAY3).toISOString();
78313
78436
  }
78314
78437
  var openRouterQuotaAdapter = {
78315
78438
  source: OPENROUTER_SOURCE,
@@ -78331,7 +78454,7 @@ var openRouterQuotaAdapter = {
78331
78454
  windows.push({ measures: "account-credits", period: "prepaid", utilization: used / total, limit: total, used, remaining: Math.max(total - used, 0) });
78332
78455
  }
78333
78456
  if (free !== void 0 && free.limit > 0) {
78334
- windows.push({ measures: "free-model-requests", periodMs: MS_PER_DAY2, utilization: free.used / free.limit, resetsAt: nextUtcMidnight(nowMs), limit: free.limit, used: free.used, remaining: Math.max(free.remaining, 0) });
78457
+ windows.push({ measures: "free-model-requests", periodMs: MS_PER_DAY3, utilization: free.used / free.limit, resetsAt: nextUtcMidnight(nowMs), limit: free.limit, used: free.used, remaining: Math.max(free.remaining, 0) });
78335
78458
  }
78336
78459
  return ProviderQuotaSchema.parse({
78337
78460
  observedAt: new Date(nowMs).toISOString(),
@@ -78520,6 +78643,9 @@ function realFrontDoorSupervisorPorts(paths) {
78520
78643
  const rcCredentialStore = createRcCredentialStore(realFarmFs, paths.frontdoorRcCredentialsDir);
78521
78644
  const rcTracker = createRcSessionTracker({ now: () => Date.now(), idleMs: RC_IDLE_EXPIRY_MS, credentialStore: rcCredentialStore });
78522
78645
  const doorEvents = createDoorEventHub();
78646
+ const usagePublisher = doorEvents.publisher(USAGE_EVENT_SOURCE);
78647
+ const quotaExpiring = createQuotaExpiringPublisher(doorEvents, { seed: listUsageSnapshots(realFarmFs, paths.usageSnapshotsDir), now: () => Date.now() });
78648
+ const doorHealth = createDoorHealthPublisher(doorEvents, () => Date.now(), process.pid);
78523
78649
  const rcLiveUsage = createRcLiveUsage({ now: () => Date.now(), hub: doorEvents });
78524
78650
  const rcFanout = rcFanoutOnDoorHub(createRcEventFanout(), doorEvents);
78525
78651
  const rcHub = createRcStreamHub({
@@ -78565,7 +78691,10 @@ function realFrontDoorSupervisorPorts(paths) {
78565
78691
  pid: process.pid,
78566
78692
  now: () => Date.now(),
78567
78693
  readAccount: createAccountReader(realFarmFs, paths.identitiesDir),
78568
- log
78694
+ log,
78695
+ publish: (snapshot) => {
78696
+ usagePublisher.publish(snapshot);
78697
+ }
78569
78698
  });
78570
78699
  const quotaRefresher = createRealQuotaRefresher({ paths, store: usageStore, log });
78571
78700
  const usageMiddleware = createUsageMiddleware({
@@ -78764,6 +78893,8 @@ function realFrontDoorSupervisorPorts(paths) {
78764
78893
  closeRcStreamHub: rcHub.close,
78765
78894
  // 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.
78766
78895
  observeLaunchRegistry: createLaunchEventPublisher(doorEvents, () => Date.now()).observe,
78896
+ doorHealth,
78897
+ checkQuotaExpiry: quotaExpiring.check,
78767
78898
  log: (line) => {
78768
78899
  appendLog(paths, line);
78769
78900
  }
@@ -80582,13 +80713,13 @@ function formatLimit(limit) {
80582
80713
  return `last limit: ${limit.kind} (${String(limit.status)}) at ${limit.observedAt}${limit.window === void 0 ? "" : ` on the ${limit.window} window`}${wait.length === 0 ? "" : `, ${wait.join(", ")}`}`;
80583
80714
  }
80584
80715
  var MONEY_DECIMALS = 2;
80585
- var MS_PER_HOUR2 = 36e5;
80716
+ var MS_PER_HOUR3 = 36e5;
80586
80717
  var HOURS_PER_DAY4 = 24;
80587
80718
  function formatPeriod(window) {
80588
80719
  if (window.periodMs === void 0) {
80589
80720
  return window.period ?? "unknown period";
80590
80721
  }
80591
- const hours = window.periodMs / MS_PER_HOUR2;
80722
+ const hours = window.periodMs / MS_PER_HOUR3;
80592
80723
  return hours >= HOURS_PER_DAY4 && Number.isInteger(hours / HOURS_PER_DAY4) ? `${String(hours / HOURS_PER_DAY4)}d` : `${String(hours)}h`;
80593
80724
  }
80594
80725
  function formatCount(value2) {
@@ -81080,10 +81211,11 @@ function resolvePoolLaunch(params) {
81080
81211
  if (graph.ranking.candidates.length === 0) {
81081
81212
  return { ok: false, message: `agent-shim: no member of pool "${poolName}" is an existing identity.` };
81082
81213
  }
81083
- const { pick: pick2, earliestReturn } = graph.ranking;
81214
+ const { pick: pick2, earliestReturn, movedOff } = graph.ranking;
81084
81215
  if (pick2 !== void 0) {
81085
81216
  recordStickyPick(params.usageFs, params.paths.usagePicksFile, params.cwd, pick2.identity, nowMs);
81086
- return { ok: true, identity: pick2.identity, explanation: `pool ${poolName}: ${pick2.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
81217
+ const move = movedOff === void 0 ? "" : `moved off ${movedOff.identity} (${movedOff.reason}); `;
81218
+ return { ok: true, identity: pick2.identity, explanation: `pool ${poolName}: ${move}${pick2.reasons.slice(0, REASONS_LOGGED).join("; ")}` };
81087
81219
  }
81088
81220
  if (earliestReturn === void 0) {
81089
81221
  return { ok: false, message: `agent-shim: no member of pool "${poolName}" can be picked.` };