@integrity-labs/agt-cli 0.28.450 → 0.28.452

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/bin/agt.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  success,
41
41
  table,
42
42
  warn
43
- } from "../chunk-NJZI6BPI.js";
43
+ } from "../chunk-WOZINHUT.js";
44
44
  import {
45
45
  AnchorSessionClient,
46
46
  CHANNEL_REGISTRY,
@@ -4830,7 +4830,7 @@ import { execFileSync, execSync } from "child_process";
4830
4830
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4831
4831
  import chalk18 from "chalk";
4832
4832
  import ora16 from "ora";
4833
- var cliVersion = true ? "0.28.450" : "dev";
4833
+ var cliVersion = true ? "0.28.452" : "dev";
4834
4834
  async function fetchLatestVersion() {
4835
4835
  const host2 = getHost();
4836
4836
  if (!host2) return null;
@@ -6002,7 +6002,7 @@ function handleError(err) {
6002
6002
  }
6003
6003
 
6004
6004
  // src/bin/agt.ts
6005
- var cliVersion2 = true ? "0.28.450" : "dev";
6005
+ var cliVersion2 = true ? "0.28.452" : "dev";
6006
6006
  var program = new Command();
6007
6007
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
6008
6008
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -4910,7 +4910,7 @@ function requireHost() {
4910
4910
  }
4911
4911
 
4912
4912
  // src/lib/api-client.ts
4913
- var agtCliVersion = true ? "0.28.450" : "dev";
4913
+ var agtCliVersion = true ? "0.28.452" : "dev";
4914
4914
  var lastConfigHash = null;
4915
4915
  function setConfigHash(hash) {
4916
4916
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -7411,4 +7411,4 @@ export {
7411
7411
  managerInstallSystemUnitCommand,
7412
7412
  managerUninstallSystemUnitCommand
7413
7413
  };
7414
- //# sourceMappingURL=chunk-NJZI6BPI.js.map
7414
+ //# sourceMappingURL=chunk-WOZINHUT.js.map
@@ -42,7 +42,7 @@ import {
42
42
  resolveEffectivePinRaw,
43
43
  safeWriteJsonAtomic,
44
44
  setConfigHash
45
- } from "../chunk-NJZI6BPI.js";
45
+ } from "../chunk-WOZINHUT.js";
46
46
  import {
47
47
  getProjectDir as getProjectDir2,
48
48
  getReadyTasks,
@@ -841,6 +841,14 @@ function logClaudeMdCeilingTransition(codeName, report, log2) {
841
841
  function reaperRestartBreakerReason(activeKeys) {
842
842
  return activeKeys.length >= 2 ? "mcp-presence-reaper" : void 0;
843
843
  }
844
+ var KNOWN_REASON_CLASS_MAP = {
845
+ crash: true,
846
+ provisioning: true,
847
+ "credential-rotation": true
848
+ };
849
+ var KNOWN_REASON_CLASSES = new Set(
850
+ Object.keys(KNOWN_REASON_CLASS_MAP)
851
+ );
844
852
  var PROVISIONING_RELOAD_REASONS = /* @__PURE__ */ new Set([
845
853
  "hot-reload-mcp",
846
854
  "managed-mcp-churn",
@@ -897,6 +905,11 @@ function maxProvisioningBucket(events) {
897
905
  }
898
906
  return best;
899
907
  }
908
+ function tripClass(trip) {
909
+ if (trip.trippedClass) return trip.trippedClass;
910
+ if (!Array.isArray(trip.eventsAtTrip) || trip.eventsAtTrip.length === 0) return "crash";
911
+ return trip.eventsAtTrip.every((e) => isProvisioningReloadReason(e.reason)) ? "provisioning" : "crash";
912
+ }
900
913
  var DEFAULT_MAX = 2;
901
914
  var DEFAULT_WINDOW_MS = 6e5;
902
915
  var DEFAULT_PROVISIONING_MAX = 5;
@@ -1007,7 +1020,10 @@ var RestartBreaker = class {
1007
1020
  const trip = {
1008
1021
  trippedAt: at,
1009
1022
  eventsAtTrip: [...tripEvents],
1010
- statusMessage: formatStatusMessage(tripEvents, tripWindowMs, trippedClass, provBucket.key)
1023
+ statusMessage: formatStatusMessage(tripEvents, tripWindowMs, trippedClass, provBucket.key),
1024
+ // ENG-7577: persist the tripping class so the recovery path can gate on
1025
+ // it after a manager restart, without re-deriving it from prose.
1026
+ trippedClass
1011
1027
  };
1012
1028
  this.trips.set(codeName, trip);
1013
1029
  this.events.delete(codeName);
@@ -1048,7 +1064,11 @@ var RestartBreaker = class {
1048
1064
  if (!saved) return;
1049
1065
  for (const [codeName, trip] of Object.entries(saved)) {
1050
1066
  if (trip && typeof trip.trippedAt === "number" && Array.isArray(trip.eventsAtTrip)) {
1051
- this.trips.set(codeName, trip);
1067
+ const klass = trip.trippedClass;
1068
+ this.trips.set(
1069
+ codeName,
1070
+ klass === void 0 || KNOWN_REASON_CLASSES.has(klass) ? trip : { ...trip, trippedClass: void 0 }
1071
+ );
1052
1072
  }
1053
1073
  }
1054
1074
  }
@@ -1275,6 +1295,9 @@ function decideAutoResume(opts) {
1275
1295
  const { trip, marker, config: config2, now } = opts;
1276
1296
  if (!config2.enabled) return { action: "skip", reason: "disabled" };
1277
1297
  if (!trip) return { action: "skip", reason: "no-trip" };
1298
+ if (tripClass(trip) !== "provisioning") {
1299
+ return { action: "skip", reason: "class-not-eligible" };
1300
+ }
1278
1301
  if (marker && now - marker.autoResumedAt < config2.backoffResetMs) {
1279
1302
  return { action: "skip", reason: "already-auto-resumed" };
1280
1303
  }
@@ -1296,6 +1319,28 @@ function hydrateAutoResumeMarkers(saved) {
1296
1319
  return map;
1297
1320
  }
1298
1321
 
1322
+ // src/lib/breaker-stale-trip.ts
1323
+ var DEFAULT_STALE_TRIP_SETTLE_MS = 12e4;
1324
+ var MIN_STALE_TRIP_SETTLE_MS = 1e3;
1325
+ function readStaleTripSettleMs() {
1326
+ return Math.max(
1327
+ MIN_STALE_TRIP_SETTLE_MS,
1328
+ readEnvNumber("AGT_RESTART_BREAKER_STALE_TRIP_SETTLE_MS", DEFAULT_STALE_TRIP_SETTLE_MS)
1329
+ );
1330
+ }
1331
+ function decideStaleTripClear(opts) {
1332
+ const { trip, agentStatus, killSwitched, settleMs, now } = opts;
1333
+ if (!trip) return { action: "hold", reason: "no-trip" };
1334
+ if (agentStatus !== "active" || killSwitched) {
1335
+ return { action: "hold", reason: "pause-in-force" };
1336
+ }
1337
+ const ageMs = now - trip.trippedAt;
1338
+ if (ageMs < settleMs) {
1339
+ return { action: "hold", reason: "settling", remainingMs: settleMs - ageMs };
1340
+ }
1341
+ return { action: "clear", ageMs };
1342
+ }
1343
+
1299
1344
  // src/lib/resume-reconciler.ts
1300
1345
  var DEFAULT_MIN_HEALTHY_CYCLES = 2;
1301
1346
  var DEFAULT_BACKOFF_RESET_MS2 = 864e5;
@@ -1726,12 +1771,22 @@ var WINDOW_EXEMPT_RESTART_REASONS = /* @__PURE__ */ new Set([
1726
1771
  "sender-policy-change",
1727
1772
  "channel-mcp-down"
1728
1773
  ]);
1774
+ var RESTART_WINDOW_DEFER_ESCALATE_AFTER = 20;
1775
+ function shouldRelaxRestartWindowGate(deferStreak) {
1776
+ return deferStreak >= RESTART_WINDOW_DEFER_ESCALATE_AFTER;
1777
+ }
1778
+ function nextWindowDeferStreak(priorStreak, decision) {
1779
+ if (decision === "proceed") return 0;
1780
+ if (decision === "defer-window") return priorStreak + 1;
1781
+ return priorStreak;
1782
+ }
1729
1783
  function isWindowExemptRestartReason(reason) {
1730
1784
  return reason != null && WINDOW_EXEMPT_RESTART_REASONS.has(reason);
1731
1785
  }
1732
1786
  function decideRestartGate(opts) {
1733
1787
  if (isMaintenanceWindowDisabled(opts.env)) return "proceed";
1734
- if (!opts.windowExempt && opts.window && !isWithinMaintenanceWindow(opts.window, opts.now)) {
1788
+ const windowAxisApplies = !opts.windowExempt && !shouldRelaxRestartWindowGate(opts.windowDeferStreak ?? 0);
1789
+ if (windowAxisApplies && opts.window && !isWithinMaintenanceWindow(opts.window, opts.now)) {
1735
1790
  return "defer-window";
1736
1791
  }
1737
1792
  const paneThreshold = opts.idleThresholdSeconds ?? RESTART_IDLE_THRESHOLD_SECONDS;
@@ -8895,6 +8950,33 @@ function maybeReportCurrentTrip(codeName) {
8895
8950
  log(`[circuit-breaker] Failed to report trip for '${codeName}' to API: ${err.message}`);
8896
8951
  });
8897
8952
  }
8953
+ function maybeClearStaleTrip(agent) {
8954
+ const codeName = agent.code_name;
8955
+ const trip = restartBreaker.getTrip(codeName);
8956
+ const decision = decideStaleTripClear({
8957
+ trip,
8958
+ agentStatus: agent.status,
8959
+ killSwitched: Boolean(agent.kill_switch),
8960
+ settleMs: readStaleTripSettleMs(),
8961
+ now: Date.now()
8962
+ });
8963
+ if (decision.action !== "clear") return;
8964
+ restartBreaker.clear(codeName);
8965
+ reportedTrips.delete(codeName);
8966
+ dependencyRecoveryLedger.clear(codeName);
8967
+ log(
8968
+ `[circuit-breaker] agent=${codeName} cleared STALE trip \u2014 authoritative status=active while a trip was held (age=${Math.round(decision.ageMs / 6e4)}min, trippedAt=${new Date(trip.trippedAt).toISOString()}); the pause this trip caused is no longer in force (ENG-7577)`
8969
+ );
8970
+ const marker = autoResumeMarkers.get(codeName);
8971
+ const isSelfResume = marker !== void 0 && Date.now() - marker.autoResumedAt < AUTO_RESUME_SELF_WINDOW_MS;
8972
+ if (!isSelfResume) autoResumeMarkers.delete(codeName);
8973
+ state6 = {
8974
+ ...state6,
8975
+ circuitBreakerTrips: restartBreaker.serialize(),
8976
+ circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
8977
+ };
8978
+ send({ type: "state-update", state: state6 });
8979
+ }
8898
8980
  var autoResumeMarkers = /* @__PURE__ */ new Map();
8899
8981
  var autoResumeInFlight = /* @__PURE__ */ new Set();
8900
8982
  var AUTO_RESUME_SELF_WINDOW_MS = 12e4;
@@ -8921,11 +9003,11 @@ function maybeAutoResume(agent) {
8921
9003
  now: Date.now()
8922
9004
  });
8923
9005
  if (decision.action === "skip") {
8924
- if ((decision.reason === "already-auto-resumed" || decision.reason === "not-quiet-yet") && trip) {
9006
+ if ((decision.reason === "already-auto-resumed" || decision.reason === "not-quiet-yet" || decision.reason === "class-not-eligible") && trip) {
8925
9007
  const key = `${codeName}:${trip.trippedAt}`;
8926
9008
  if (autoResumeLoggedSkips.get(key) !== decision.reason) {
8927
9009
  autoResumeLoggedSkips.set(key, decision.reason);
8928
- const detail = decision.reason === "not-quiet-yet" ? ` (~${Math.ceil((decision.remainingMs ?? 0) / 6e4)}min remaining)` : " \u2014 credit spent for this cycle; operator resume re-arms it";
9010
+ const detail = decision.reason === "not-quiet-yet" ? ` (~${Math.ceil((decision.remainingMs ?? 0) / 6e4)}min remaining)` : decision.reason === "class-not-eligible" ? ` class=${tripClass(trip)} \u2014 the blind quiet-timer only auto-recovers provisioning-reload trips; this one stays paused for an operator (ENG-7577)` : " \u2014 credit spent for this cycle; operator resume re-arms it";
8929
9011
  log(`[auto-resume] agent=${codeName} decision=skip reason=${decision.reason}${detail} (ENG-6088)`);
8930
9012
  }
8931
9013
  }
@@ -9299,6 +9381,7 @@ function transcriptAgeSecondsFor(codeName) {
9299
9381
  const sessionId = getSessionState(codeName)?.currentSessionId ?? null;
9300
9382
  return transcriptActivityAgeSeconds(getProjectDir2(codeName), sessionId, /* @__PURE__ */ new Date());
9301
9383
  }
9384
+ var restartGateDeferStreaks = /* @__PURE__ */ new Map();
9302
9385
  function restartGateFor(codeName, reason) {
9303
9386
  if (!isGateableRestartReason(reason)) return "bypass";
9304
9387
  const tzInputs = agentRestartTimezoneInputs.get(codeName);
@@ -9311,16 +9394,28 @@ function restartGateFor(codeName, reason) {
9311
9394
  tzInputs?.agentTimezoneExpiresAt ?? null
9312
9395
  )
9313
9396
  } : cachedMaintenanceWindow;
9314
- return decideRestartGate({
9397
+ const streakKey = `${codeName}::${reason}`;
9398
+ const priorStreak = restartGateDeferStreaks.get(streakKey) ?? 0;
9399
+ const decision = decideRestartGate({
9315
9400
  window: perAgentWindow,
9316
9401
  // ENG-6491: channel adds (channel-set-change) skip the off-peak window but
9317
9402
  // still defer-until-idle — they come online as soon as the agent is quiet.
9318
9403
  windowExempt: isWindowExemptRestartReason(reason),
9404
+ windowDeferStreak: priorStreak,
9319
9405
  paneLogAgeSeconds: paneLogAgeSecondsFor(codeName),
9320
9406
  transcriptAgeSeconds: transcriptAgeSecondsFor(codeName),
9321
9407
  inboundAgeSeconds: inboundAgeSecondsFor(codeName),
9322
9408
  now: /* @__PURE__ */ new Date()
9323
9409
  });
9410
+ const nextStreak = nextWindowDeferStreak(priorStreak, decision);
9411
+ if (nextStreak === 0) restartGateDeferStreaks.delete(streakKey);
9412
+ else restartGateDeferStreaks.set(streakKey, nextStreak);
9413
+ if (nextStreak === RESTART_WINDOW_DEFER_ESCALATE_AFTER && nextStreak !== priorStreak) {
9414
+ log(
9415
+ `[maintenance-window] '${codeName}': '${reason}' restart deferred ${nextStreak}\xD7 by the window \u2014 dropping the maintenance-window wait (ENG-8224). Still defer-until-idle: it will fire on the next quiet gap, never mid-turn.`
9416
+ );
9417
+ }
9418
+ return decision;
9324
9419
  }
9325
9420
  function isHostBusyForForcedUpdate(opts) {
9326
9421
  const now = /* @__PURE__ */ new Date();
@@ -9787,7 +9882,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9787
9882
  var lastVersionCheckAt = 0;
9788
9883
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9789
9884
  var lastResponsivenessProbeAt = 0;
9790
- var agtCliVersion = true ? "0.28.450" : "dev";
9885
+ var agtCliVersion = true ? "0.28.452" : "dev";
9791
9886
  function resolveBrewPath(execFileSync2) {
9792
9887
  try {
9793
9888
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -11778,6 +11873,7 @@ async function processAgent(agent, agentStates) {
11778
11873
  const previousKnownStatus = agentState.knownStatuses.get(agent.agent_id);
11779
11874
  agentState.agentDisplayNames.set(agent.code_name, agent.display_name);
11780
11875
  agentState.codeNameToAgentId.set(agent.code_name, agent.agent_id);
11876
+ maybeClearStaleTrip(agent);
11781
11877
  maybeReportCurrentTrip(agent.code_name);
11782
11878
  if (agent.framework) {
11783
11879
  agentFrameworkCache.set(agent.code_name, agent.framework);
@@ -11920,6 +12016,7 @@ async function processAgent(agent, agentStates) {
11920
12016
  if (previousStatus === "paused" && agent.status === "active" && restartBreaker.isTripped(agent.code_name)) {
11921
12017
  restartBreaker.clear(agent.code_name);
11922
12018
  reportedTrips.delete(agent.code_name);
12019
+ dependencyRecoveryLedger.clear(agent.code_name);
11923
12020
  log(`[circuit-breaker] Cleared trip for '${agent.code_name}' on operator resume (paused \u2192 active)`);
11924
12021
  }
11925
12022
  if (previousStatus === "paused" && agent.status === "active") {