@integrity-labs/agt-cli 0.28.636 → 0.28.638

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,10 +40,10 @@ import {
40
40
  success,
41
41
  table,
42
42
  warn
43
- } from "../chunk-KIOTRBOI.js";
43
+ } from "../chunk-LCKKFDCR.js";
44
44
  import {
45
45
  readDirectChatSessionState
46
- } from "../chunk-4HVSMJFM.js";
46
+ } from "../chunk-MRQ2GRTN.js";
47
47
  import {
48
48
  AnchorSessionClient,
49
49
  CHANNEL_REGISTRY,
@@ -73,7 +73,7 @@ import {
73
73
  requiredMcpWildcard,
74
74
  resolveChannels,
75
75
  serializeManifestForSlackCli
76
- } from "../chunk-O4ADFO57.js";
76
+ } from "../chunk-MAZUBHNT.js";
77
77
  import "../chunk-XWVM4KPK.js";
78
78
 
79
79
  // src/bin/agt.ts
@@ -4909,7 +4909,7 @@ import { execFileSync, execSync } from "child_process";
4909
4909
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4910
4910
  import chalk18 from "chalk";
4911
4911
  import ora16 from "ora";
4912
- var cliVersion = true ? "0.28.636" : "dev";
4912
+ var cliVersion = true ? "0.28.638" : "dev";
4913
4913
  async function fetchLatestVersion() {
4914
4914
  const host2 = getHost();
4915
4915
  if (!host2) return null;
@@ -6089,7 +6089,7 @@ function handleError(err) {
6089
6089
  }
6090
6090
 
6091
6091
  // src/bin/agt.ts
6092
- var cliVersion2 = true ? "0.28.636" : "dev";
6092
+ var cliVersion2 = true ? "0.28.638" : "dev";
6093
6093
  var program = new Command();
6094
6094
  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");
6095
6095
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -6,8 +6,9 @@ import {
6
6
  buildRemoteMcpEntry,
7
7
  expandTemplateVars,
8
8
  isolationMode,
9
+ log,
9
10
  parseEnvIntegrations
10
- } from "./chunk-4HVSMJFM.js";
11
+ } from "./chunk-MRQ2GRTN.js";
11
12
  import {
12
13
  BIND_FAILURE_QUARANTINE_THRESHOLD,
13
14
  INTEGRATIONS_SECTION_END,
@@ -21,6 +22,8 @@ import {
21
22
  RESTART_BREAKER_OPERATOR_MAX,
22
23
  RESTART_BREAKER_PROVISIONING_MAX,
23
24
  RESTART_BREAKER_PROVISIONING_WINDOW_MS,
25
+ RESTART_BREAKER_SCHEDULED_MAX,
26
+ RESTART_BREAKER_SCHEDULED_WINDOW_MS,
24
27
  SCRATCH_RETENTION_DAYS,
25
28
  SLACK_SOCKET_STATE_FILENAME,
26
29
  bestConnectivityEvidence,
@@ -52,7 +55,7 @@ import {
52
55
  resolveConnectivityProbe,
53
56
  worseConnectivityOutcome,
54
57
  wrapScheduledTaskPrompt
55
- } from "./chunk-O4ADFO57.js";
58
+ } from "./chunk-MAZUBHNT.js";
56
59
  import {
57
60
  parsePsRows
58
61
  } from "./chunk-XWVM4KPK.js";
@@ -1535,7 +1538,7 @@ function getAgentDir(codeName) {
1535
1538
  return resolveRealAgentPath(join2(getHomeDir(), ".augmented", codeName));
1536
1539
  }
1537
1540
  var migratedCodeNames = /* @__PURE__ */ new Set();
1538
- function migrateLegacyClaudecodeDir(codeName, log) {
1541
+ function migrateLegacyClaudecodeDir(codeName, log2) {
1539
1542
  assertValidCodeName(codeName);
1540
1543
  if (migratedCodeNames.has(codeName))
1541
1544
  return;
@@ -1546,7 +1549,7 @@ function migrateLegacyClaudecodeDir(codeName, log) {
1546
1549
  }
1547
1550
  const newRoot = getAgentDir(codeName);
1548
1551
  const emit = (msg) => {
1549
- log?.(msg);
1552
+ log2?.(msg);
1550
1553
  };
1551
1554
  try {
1552
1555
  const walkAndMigrate = (srcDir, destDir) => {
@@ -6070,7 +6073,7 @@ function exchangeFailureKind(err) {
6070
6073
  }
6071
6074
 
6072
6075
  // src/lib/api-client.ts
6073
- var agtCliVersion = true ? "0.28.636" : "dev";
6076
+ var agtCliVersion = true ? "0.28.638" : "dev";
6074
6077
  var lastConfigHash = null;
6075
6078
  function setConfigHash(hash) {
6076
6079
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -6884,7 +6887,7 @@ function findMcpChildrenForAgent(args) {
6884
6887
  return matched;
6885
6888
  }
6886
6889
  function reapStaleMcpChildren(args) {
6887
- const { log, codeName, serverKeys, mcpJson, graceMs = 5e3, isolated = false } = args;
6890
+ const { log: log2, codeName, serverKeys, mcpJson, graceMs = 5e3, isolated = false } = args;
6888
6891
  if (serverKeys.length === 0) return [];
6889
6892
  const runPs = args.runPs ?? (isolated ? () => execFileSync("docker", ["exec", `agt-${codeName}`, "ps", "-eo", "pid,ppid,args"], {
6890
6893
  encoding: "utf-8",
@@ -6927,7 +6930,7 @@ function reapStaleMcpChildren(args) {
6927
6930
  try {
6928
6931
  psOutput = runPs();
6929
6932
  } catch (err) {
6930
- log(`[stale-mcp-reaper] ps invocation failed for '${codeName}': ${err.message} \u2014 skipping reap`);
6933
+ log2(`[stale-mcp-reaper] ps invocation failed for '${codeName}': ${err.message} \u2014 skipping reap`);
6931
6934
  return [];
6932
6935
  }
6933
6936
  const rows = parsePsRows(psOutput);
@@ -6939,7 +6942,7 @@ function reapStaleMcpChildren(args) {
6939
6942
  const pkgMatch = argv.match(/(@[a-z0-9_-]+\/[a-z0-9_-]+|[a-z0-9_-]+-mcp-server|[a-z0-9_-]+-mcp\b)/i);
6940
6943
  return pkgMatch ? `${pkgMatch[1]} (pid ${pid})` : `pid ${pid}`;
6941
6944
  };
6942
- log(
6945
+ log2(
6943
6946
  `[stale-mcp-reaper] '${codeName}': rotating ${targets.length} stale MCP child(ren) for [${serverKeys.join(", ")}]: ${targets.map(describe).join(", ")}`
6944
6947
  );
6945
6948
  for (const pid of targets) {
@@ -6961,7 +6964,7 @@ function reapStaleMcpChildren(args) {
6961
6964
  try {
6962
6965
  freshPsOutput = runPs();
6963
6966
  } catch (err) {
6964
- log(`[stale-mcp-reaper] '${codeName}': fresh ps for SIGKILL re-verify failed: ${err.message} \u2014 skipping SIGKILL pass`);
6967
+ log2(`[stale-mcp-reaper] '${codeName}': fresh ps for SIGKILL re-verify failed: ${err.message} \u2014 skipping SIGKILL pass`);
6965
6968
  return;
6966
6969
  }
6967
6970
  const stillOwned = new Set(
@@ -6975,14 +6978,14 @@ function reapStaleMcpChildren(args) {
6975
6978
  );
6976
6979
  const stragglers = targets.filter((pid) => isAlive(pid) && stillOwned.has(pid));
6977
6980
  if (stragglers.length === 0) return;
6978
- log(
6981
+ log2(
6979
6982
  `[stale-mcp-reaper] '${codeName}': ${stragglers.length} child(ren) survived SIGTERM; sending SIGKILL: ${stragglers.map(describe).join(", ")}`
6980
6983
  );
6981
6984
  for (const pid of stragglers) {
6982
6985
  killProcess(pid, "SIGKILL");
6983
6986
  }
6984
6987
  } catch (err) {
6985
- log(`[stale-mcp-reaper] '${codeName}': error in SIGKILL pass: ${err.message}`);
6988
+ log2(`[stale-mcp-reaper] '${codeName}': error in SIGKILL pass: ${err.message}`);
6986
6989
  }
6987
6990
  }, graceMs).unref();
6988
6991
  return targets;
@@ -7051,7 +7054,7 @@ function findMissingMcpServers(args) {
7051
7054
  }
7052
7055
  function reapMissingMcpSessions(args) {
7053
7056
  const {
7054
- log,
7057
+ log: log2,
7055
7058
  codeName,
7056
7059
  mcpJson,
7057
7060
  sessionStartedAt,
@@ -7107,7 +7110,7 @@ function reapMissingMcpSessions(args) {
7107
7110
  try {
7108
7111
  psOutput = runPs();
7109
7112
  } catch (err) {
7110
- log(`[mcp-presence-reaper] ps invocation failed for '${codeName}': ${err.message} \u2014 skipping`);
7113
+ log2(`[mcp-presence-reaper] ps invocation failed for '${codeName}': ${err.message} \u2014 skipping`);
7111
7114
  return { missing: [], restarted: false, reason: "healthy" };
7112
7115
  }
7113
7116
  const rows = parsePsRows(psOutput);
@@ -7123,7 +7126,7 @@ function reapMissingMcpSessions(args) {
7123
7126
  try {
7124
7127
  onRotationRespawnOutcome(codeName, key, outcome);
7125
7128
  } catch (err) {
7126
- log(
7129
+ log2(
7127
7130
  `[mcp-presence-reaper] onRotationRespawnOutcome callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
7128
7131
  );
7129
7132
  }
@@ -7150,7 +7153,7 @@ function reapMissingMcpSessions(args) {
7150
7153
  escalated: false
7151
7154
  };
7152
7155
  if (priorStall && priorStall !== stall) {
7153
- log(
7156
+ log2(
7154
7157
  `[mcp-presence-reaper] '${codeName}:${key}' discarding stall state from a previous rotation (rotation=${priorStall.rotationRecordedAt}, probes=${priorStall.probeAttempts}, escalated=${priorStall.escalated}) \u2014 this is a NEW rotation (rotation=${rotationRecordedAt}) and gets a full prompt-probe budget (ENG-8225)`
7155
7158
  );
7156
7159
  }
@@ -7168,14 +7171,14 @@ function reapMissingMcpSessions(args) {
7168
7171
  stall.escalated = true;
7169
7172
  rotationProbeExhausted.push(key);
7170
7173
  recordRotationOutcome(key, "never-respawned");
7171
- log(
7174
+ log2(
7172
7175
  `[mcp-presence-reaper] '${codeName}:${key}' rotation outcome=never-respawned \u2014 child SIGTERM'd by stale-mcp-reaper never returned after ${stall.probeAttempts} prompt bind-probe(s); escalating (integration marked unhealthy + reaper_action alert) and releasing to the normal restart path (ENG-8225)`
7173
7176
  );
7174
7177
  if (onRotationRespawnGaveUp) {
7175
7178
  try {
7176
7179
  onRotationRespawnGaveUp(codeName, key, { attempts: stall.probeAttempts });
7177
7180
  } catch (err) {
7178
- log(
7181
+ log2(
7179
7182
  `[mcp-presence-reaper] onRotationRespawnGaveUp callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
7180
7183
  );
7181
7184
  }
@@ -7192,7 +7195,7 @@ function reapMissingMcpSessions(args) {
7192
7195
  maxAttempts: maxRotationPromptProbes
7193
7196
  });
7194
7197
  } catch (err) {
7195
- log(
7198
+ log2(
7196
7199
  `[mcp-presence-reaper] onRotationRespawnStalled callback threw for '${codeName}:${key}' (suppressed; falling back to the pre-ENG-8225 restart path): ${err.message}`
7197
7200
  );
7198
7201
  armed = false;
@@ -7205,17 +7208,17 @@ function reapMissingMcpSessions(args) {
7205
7208
  stall.probeAttempts = attempt;
7206
7209
  stall.lastProbeAt = now();
7207
7210
  rotationProbeArmed.push(key);
7208
- log(
7211
+ log2(
7209
7212
  `[mcp-presence-reaper] '${codeName}:${key}' rotation grace expired (${rotationGraceMs}ms) with no live child and no tool call to respawn it \u2014 armed prompt bind-probe ${attempt}/${maxRotationPromptProbes} (ENG-7429) instead of restarting the session (ENG-8225)`
7210
7213
  );
7211
7214
  }
7212
7215
  if (rotationGraced.length > 0) {
7213
- log(
7216
+ log2(
7214
7217
  `[mcp-presence-reaper] '${codeName}': skipping [${rotationGraced.join(", ")}] within rotation grace window (${rotationGraceMs}ms) \u2014 child(ren) just SIGTERM'd by stale-mcp-reaper, awaiting respawn on next tool call (ENG-5344)`
7215
7218
  );
7216
7219
  }
7217
7220
  if (rotationProbeWaiting.length > 0) {
7218
- log(
7221
+ log2(
7219
7222
  `[mcp-presence-reaper] '${codeName}': skipping [${rotationProbeWaiting.join(", ")}] \u2014 prompt bind-probe already armed for the stalled rotation, waiting out the ${rotationPromptProbeIntervalMs}ms re-arm interval (ENG-8225)`
7220
7223
  );
7221
7224
  }
@@ -7238,7 +7241,7 @@ function reapMissingMcpSessions(args) {
7238
7241
  key,
7239
7242
  viaProbe ? "respawned-after-prompt-probe" : "respawned-on-tool-call"
7240
7243
  );
7241
- log(
7244
+ log2(
7242
7245
  viaProbe ? `[mcp-presence-reaper] '${codeName}:${key}' rotation outcome=respawned-after-prompt-probe \u2014 child is live again after ${stall?.probeAttempts} prompt bind-probe(s)${stall?.escalated ? " (recovered AFTER escalation \u2014 the earlier never-respawned verdict stands for that window)" : ""}, no session restart needed (ENG-8225)` : `[mcp-presence-reaper] '${codeName}:${key}' rotation outcome=respawned-on-tool-call \u2014 child returned on the agent's own next tool call, ENG-5344's assumption held (ENG-8225)`
7243
7246
  );
7244
7247
  clearStaleRotation(codeName, [key]);
@@ -7306,7 +7309,7 @@ function reapMissingMcpSessions(args) {
7306
7309
  genuinelyDead.push(key);
7307
7310
  if (currentGenerationExternallyStarted && !state.deadChannelLogged) {
7308
7311
  state.deadChannelLogged = true;
7309
- log(
7312
+ log2(
7310
7313
  `[mcp-presence-reaper] '${codeName}:${key}' continuously missing ${Math.round((nowMs - state.continuouslyMissingSince) / 6e4)}m (> ${Math.round(deadChannelMs / 6e4)}m) \u2014 genuinely dead, overriding storm-awareness so it can reach give-up/quarantine instead of churning (ENG-8062)`
7311
7314
  );
7312
7315
  }
@@ -7332,7 +7335,7 @@ function reapMissingMcpSessions(args) {
7332
7335
  state.gaveUp = true;
7333
7336
  if (!state.gaveUpLogged) {
7334
7337
  const cause = overAttemptsCap ? `${MAX_PRESENCE_RESTART_ATTEMPTS} consecutive failed restarts \u2014 declared but never recovers (likely orphaned config; see ENG-5279)` : `${restartsInWindow} restarts within ${Math.round(BACKOFF_WINDOW_MS / 6e4)}m \u2014 persistently flapping (crash-loop; ENG-6480)`;
7335
- log(
7338
+ log2(
7336
7339
  `[mcp-presence-reaper] giving up on '${codeName}:${key}' after ${cause}`
7337
7340
  );
7338
7341
  state.gaveUpLogged = true;
@@ -7340,7 +7343,7 @@ function reapMissingMcpSessions(args) {
7340
7343
  try {
7341
7344
  onGiveUp(codeName, key);
7342
7345
  } catch (err) {
7343
- log(
7346
+ log2(
7344
7347
  `[mcp-presence-reaper] onGiveUp callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
7345
7348
  );
7346
7349
  }
@@ -7352,21 +7355,21 @@ function reapMissingMcpSessions(args) {
7352
7355
  state.quarantineLogged = true;
7353
7356
  if (quarantineMode === "enforce") {
7354
7357
  quarantined.push(key);
7355
- log(
7358
+ log2(
7356
7359
  `[mcp-presence-reaper] QUARANTINE '${codeName}:${key}' \u2014 optional channel dead past restart budget + ${quarantineDwellMs}ms dwell; dropping from provisioned set so the dead channel stops restarting the whole session (ENG-5932)`
7357
7360
  );
7358
7361
  if (onQuarantine) {
7359
7362
  try {
7360
7363
  onQuarantine(codeName, key);
7361
7364
  } catch (err) {
7362
- log(
7365
+ log2(
7363
7366
  `[mcp-presence-reaper] onQuarantine callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
7364
7367
  );
7365
7368
  }
7366
7369
  }
7367
7370
  } else {
7368
7371
  wouldQuarantine.push(key);
7369
- log(
7372
+ log2(
7370
7373
  `[mcp-presence-reaper] SHADOW would-quarantine '${codeName}:${key}' \u2014 optional channel dead past restart budget + ${quarantineDwellMs}ms dwell; no action taken (ENG-5932 shadow mode)`
7371
7374
  );
7372
7375
  }
@@ -7388,7 +7391,7 @@ function reapMissingMcpSessions(args) {
7388
7391
  if (active.length === 0) {
7389
7392
  const reason = backingOff.length > 0 ? "backoff" : "all-keys-over-cap";
7390
7393
  if (backingOff.length > 0) {
7391
- log(
7394
+ log2(
7392
7395
  `[mcp-presence-reaper] '${codeName}': holding off restart for [${backingOff.join(", ")}] \u2014 escalating backoff interval not yet elapsed (ENG-6480)${givenUp.length > 0 ? ` (given up: [${givenUp.join(", ")}])` : ""}`
7393
7396
  );
7394
7397
  }
@@ -7409,7 +7412,7 @@ function reapMissingMcpSessions(args) {
7409
7412
  };
7410
7413
  }
7411
7414
  const givenUpSuffix = givenUp.length > 0 ? ` (skipping over-cap: [${givenUp.join(", ")}])` : "";
7412
- log(
7415
+ log2(
7413
7416
  `[mcp-presence-reaper] '${codeName}': declared MCP(s) [${active.join(", ")}] have no live children \u2014 restarting session${givenUpSuffix}`
7414
7417
  );
7415
7418
  if (onRestart) {
@@ -7417,13 +7420,13 @@ function reapMissingMcpSessions(args) {
7417
7420
  const maybePromise = onRestart(codeName, { activeKeys: active, givenUpKeys: givenUp });
7418
7421
  if (maybePromise && typeof maybePromise.then === "function") {
7419
7422
  void maybePromise.then(void 0, (err) => {
7420
- log(
7423
+ log2(
7421
7424
  `[mcp-presence-reaper] onRestart callback rejected for '${codeName}' (suppressed; restart proceeds): ${err.message}`
7422
7425
  );
7423
7426
  });
7424
7427
  }
7425
7428
  } catch (err) {
7426
- log(
7429
+ log2(
7427
7430
  `[mcp-presence-reaper] onRestart callback threw for '${codeName}' (suppressed; restart proceeds): ${err.message}`
7428
7431
  );
7429
7432
  }
@@ -7466,7 +7469,12 @@ var KNOWN_REASON_CLASS_MAP = {
7466
7469
  // forced by the Record type, which is exactly why that shape was chosen over
7467
7470
  // a Set literal (ENG-7577, CodeRabbit on #3861) - a new union member cannot
7468
7471
  // be silently omitted from the rehydration allowlist.
7469
- "self-healing": true
7472
+ "self-healing": true,
7473
+ // ENG-9138: the platform-scheduled class. Same Record-not-Set reasoning: the
7474
+ // compiler forces every new class into this allowlist, so a `trippedClass`
7475
+ // rehydrated from manager-state.json can never name a class this build does
7476
+ // not know about.
7477
+ scheduled: true
7470
7478
  };
7471
7479
  var KNOWN_REASON_CLASSES = new Set(
7472
7480
  Object.keys(KNOWN_REASON_CLASS_MAP)
@@ -7507,6 +7515,12 @@ var SELF_HEALING_REASONS = /* @__PURE__ */ new Set([
7507
7515
  function isSelfHealingReason(reason) {
7508
7516
  return SELF_HEALING_REASONS.has(reason);
7509
7517
  }
7518
+ var SCHEDULED_REASONS = /* @__PURE__ */ new Set([
7519
+ "day-rollover"
7520
+ ]);
7521
+ function isScheduledRestartReason(reason) {
7522
+ return SCHEDULED_REASONS.has(reason);
7523
+ }
7510
7524
  var OPERATOR_CONFIG_REASONS = /* @__PURE__ */ new Set([
7511
7525
  "operator-integration-change"
7512
7526
  ]);
@@ -7520,6 +7534,7 @@ function restartReasonClass(reason) {
7520
7534
  if (isCredentialRotationReason(reason)) return "credential-rotation";
7521
7535
  if (isSelfHealingReason(reason)) return "self-healing";
7522
7536
  if (isOperatorConfigReason(reason)) return "operator-config";
7537
+ if (isScheduledRestartReason(reason)) return "scheduled";
7523
7538
  return isProvisioningReloadReason(reason) ? "provisioning" : "crash";
7524
7539
  }
7525
7540
  function countByClass(events) {
@@ -7528,12 +7543,14 @@ function countByClass(events) {
7528
7543
  let credentialRotation = 0;
7529
7544
  let selfHealing = 0;
7530
7545
  let operatorConfig = 0;
7546
+ let scheduled = 0;
7531
7547
  for (const e of events) {
7532
7548
  const klass = restartReasonClass(e.reason);
7533
7549
  if (klass === "provisioning") provisioning += 1;
7534
7550
  else if (klass === "credential-rotation") credentialRotation += 1;
7535
7551
  else if (klass === "self-healing") selfHealing += 1;
7536
7552
  else if (klass === "operator-config") operatorConfig += 1;
7553
+ else if (klass === "scheduled") scheduled += 1;
7537
7554
  else crash += 1;
7538
7555
  }
7539
7556
  return {
@@ -7541,7 +7558,8 @@ function countByClass(events) {
7541
7558
  provisioning,
7542
7559
  "credential-rotation": credentialRotation,
7543
7560
  "self-healing": selfHealing,
7544
- "operator-config": operatorConfig
7561
+ "operator-config": operatorConfig,
7562
+ scheduled
7545
7563
  };
7546
7564
  }
7547
7565
  function maxProvisioningBucket(events) {
@@ -7571,6 +7589,8 @@ var DEFAULT_WINDOW_MS = 6e5;
7571
7589
  var DEFAULT_PROVISIONING_MAX = RESTART_BREAKER_PROVISIONING_MAX;
7572
7590
  var DEFAULT_PROVISIONING_WINDOW_MS = RESTART_BREAKER_PROVISIONING_WINDOW_MS;
7573
7591
  var DEFAULT_OPERATOR_MAX = RESTART_BREAKER_OPERATOR_MAX;
7592
+ var DEFAULT_SCHEDULED_MAX = RESTART_BREAKER_SCHEDULED_MAX;
7593
+ var DEFAULT_SCHEDULED_WINDOW_MS = RESTART_BREAKER_SCHEDULED_WINDOW_MS;
7574
7594
  function readEnvNumber(name, fallback) {
7575
7595
  const raw = process.env[name];
7576
7596
  if (!raw) return fallback;
@@ -7598,7 +7618,15 @@ var RestartBreaker = class {
7598
7618
  * env var without the other cannot invert the two tallies.
7599
7619
  */
7600
7620
  operatorMax;
7601
- /** Longest window either tally needs — how long the events log must retain. */
7621
+ /**
7622
+ * ENG-9138: the scheduled class's LOOP bar. Readonly and deliberately NOT
7623
+ * floored against any other tally: it gates a REPORT, not a pause, so it
7624
+ * cannot invert anything the way operatorMax/provisioningMax can.
7625
+ */
7626
+ scheduledMax;
7627
+ /** ENG-9138: sliding window for the scheduled tally. */
7628
+ scheduledWindowMs;
7629
+ /** Longest window any tally needs — how long the events log must retain. */
7602
7630
  retentionMs;
7603
7631
  now;
7604
7632
  /** ENG-7891 slice 5b: code_name → durable runtime key (agent_id when id-keyed). Identity on the legacy fleet. */
@@ -7624,6 +7652,8 @@ var RestartBreaker = class {
7624
7652
  this.provisioningMax = opts.provisioningMax ?? readEnvNumber("AGT_RESTART_BREAKER_PROVISIONING_MAX", DEFAULT_PROVISIONING_MAX);
7625
7653
  this.provisioningWindowMs = opts.provisioningWindowMs ?? readEnvNumber("AGT_RESTART_BREAKER_PROVISIONING_WINDOW_MS", DEFAULT_PROVISIONING_WINDOW_MS);
7626
7654
  this.operatorMax = opts.operatorMax ?? readEnvNumber("AGT_RESTART_BREAKER_OPERATOR_MAX", DEFAULT_OPERATOR_MAX);
7655
+ this.scheduledMax = opts.scheduledMax ?? readEnvNumber("AGT_RESTART_BREAKER_SCHEDULED_MAX", DEFAULT_SCHEDULED_MAX);
7656
+ this.scheduledWindowMs = opts.scheduledWindowMs ?? readEnvNumber("AGT_RESTART_BREAKER_SCHEDULED_WINDOW_MS", DEFAULT_SCHEDULED_WINDOW_MS);
7627
7657
  this.now = opts.now ?? Date.now;
7628
7658
  this.runtimeKey = opts.runtimeKey ?? ((codeName) => codeName);
7629
7659
  this.agentCreatedAt = opts.agentCreatedAt ?? (() => void 0);
@@ -7665,7 +7695,7 @@ var RestartBreaker = class {
7665
7695
  if (this.newAgentGraceCeiling < minimumGraceCeiling) {
7666
7696
  this.newAgentGraceCeiling = minimumGraceCeiling;
7667
7697
  }
7668
- this.retentionMs = Math.max(this.windowMs, this.provisioningWindowMs);
7698
+ this.retentionMs = Math.max(this.windowMs, this.provisioningWindowMs, this.scheduledWindowMs);
7669
7699
  }
7670
7700
  /**
7671
7701
  * ENG-8215: the clamp record, if the configured provisioning max had to be
@@ -7754,14 +7784,23 @@ var RestartBreaker = class {
7754
7784
  const operatorEvents = prior.filter(
7755
7785
  (e) => e.at >= at - this.provisioningWindowMs && isOperatorConfigReason(e.reason)
7756
7786
  );
7787
+ const scheduledEvents = prior.filter(
7788
+ (e) => e.at >= at - this.scheduledWindowMs && isScheduledRestartReason(e.reason)
7789
+ );
7757
7790
  const windowCount = prior.filter((e) => e.at >= at - this.windowMs).length;
7758
7791
  const classCounts = {
7759
7792
  crash: crashCount,
7760
7793
  provisioning: provEvents.length,
7761
7794
  "credential-rotation": credRotCount,
7762
7795
  "self-healing": selfHealCount,
7763
- "operator-config": operatorEvents.length
7796
+ "operator-config": operatorEvents.length,
7797
+ scheduled: scheduledEvents.length
7764
7798
  };
7799
+ const scheduledLoop = scheduledEvents.length > this.scheduledMax ? {
7800
+ count: scheduledEvents.length,
7801
+ windowMs: this.scheduledWindowMs,
7802
+ reason: scheduledEvents[scheduledEvents.length - 1].reason
7803
+ } : void 0;
7765
7804
  const provBucket = maxProvisioningBucket(provEvents);
7766
7805
  const operatorBucket = maxBucketWhere(operatorEvents, isOperatorConfigReason);
7767
7806
  const createdAt = this.newAgentGraceMs > 0 ? this.agentCreatedAt(codeName) : void 0;
@@ -7821,7 +7860,9 @@ var RestartBreaker = class {
7821
7860
  trippedClass,
7822
7861
  maxProvisioningBucket: provBucket.count,
7823
7862
  maxProvisioningBucketKey: provBucket.key,
7824
- ...graceCeilingExceeded ? { newAgentGraceCeilingExceeded: true } : {}
7863
+ ...graceCeilingExceeded ? { newAgentGraceCeilingExceeded: true } : {},
7864
+ // ENG-9138: a co-occurring rollover loop still reports on a real trip.
7865
+ ...scheduledLoop ? { scheduledLoop } : {}
7825
7866
  };
7826
7867
  }
7827
7868
  const suppressedOperator = operatorBucket.count > this.operatorMax;
@@ -7833,6 +7874,7 @@ var RestartBreaker = class {
7833
7874
  classCounts,
7834
7875
  maxProvisioningBucket: provBucket.count,
7835
7876
  maxProvisioningBucketKey: provBucket.key,
7877
+ ...scheduledLoop ? { scheduledLoop } : {},
7836
7878
  ...newAgentGraceActive ? {
7837
7879
  newAgentGraceSuppressedTrip: wouldHaveTripped,
7838
7880
  ...newAgentGraceExpiresAt !== void 0 ? { newAgentGraceExpiresAt } : {},
@@ -8820,6 +8862,63 @@ function provision(input, frameworkId = "claude-code") {
8820
8862
  };
8821
8863
  }
8822
8864
 
8865
+ // src/lib/startup-telemetry.ts
8866
+ var SLOW_STARTUP_WARN_MS = 6e4;
8867
+ function shouldWarnSlowStartup(elapsedMs, opts = {}) {
8868
+ if (!Number.isFinite(elapsedMs) || elapsedMs < 0) return false;
8869
+ const threshold = opts.thresholdMs ?? SLOW_STARTUP_WARN_MS;
8870
+ if (!Number.isFinite(threshold) || threshold < 0) return false;
8871
+ return elapsedMs >= threshold;
8872
+ }
8873
+ function formatStartupElapsed(elapsedMs) {
8874
+ if (!Number.isFinite(elapsedMs) || elapsedMs < 0) return "unknown";
8875
+ const seconds = elapsedMs / 1e3;
8876
+ if (seconds < 60) return `${seconds.toFixed(1)}s`;
8877
+ return `${seconds.toFixed(1)}s (${(seconds / 60).toFixed(1)} min)`;
8878
+ }
8879
+ function describeStartupCompletion(elapsedMs, opts = {}) {
8880
+ const elapsed = formatStartupElapsed(elapsedMs);
8881
+ if (!shouldWarnSlowStartup(elapsedMs, opts)) {
8882
+ return { warn: false, message: `[startup] ready in ${elapsed}` };
8883
+ }
8884
+ const threshold = opts.thresholdMs ?? SLOW_STARTUP_WARN_MS;
8885
+ return {
8886
+ warn: true,
8887
+ message: `[startup] SLOW BOOT \u2014 ready in ${elapsed}, over the ${formatStartupElapsed(threshold)} threshold. MCP bundles are deployed during startup, so agents spawning in this window can come up with no MCP tools (ENG-9129). Check for a build/install running on the host.`
8888
+ };
8889
+ }
8890
+ var SLOW_MODULE_LOAD_WARN_MS = 1e4;
8891
+ function describeWorkerModuleLoad(elapsedMs, opts = {}) {
8892
+ const elapsed = formatStartupElapsed(elapsedMs);
8893
+ const threshold = opts.thresholdMs ?? SLOW_MODULE_LOAD_WARN_MS;
8894
+ if (!shouldWarnSlowStartup(elapsedMs, { thresholdMs: threshold })) {
8895
+ return { warn: false, message: `[startup] worker module loaded in ${elapsed}` };
8896
+ }
8897
+ return {
8898
+ warn: true,
8899
+ message: `[startup] SLOW MODULE LOAD \u2014 worker module took ${elapsed} to import, over the ${formatStartupElapsed(threshold)} threshold. The manager had already written its pidfile and reported itself started, so it looked healthy for this whole window while doing nothing \u2014 and MCP bundles are deployed after this point, so agents spawning meanwhile come up with no MCP tools (ENG-9129). Check for a build, install or filesystem rebuild running on the host.`
8900
+ };
8901
+ }
8902
+ function describeRejection(err) {
8903
+ if (err instanceof Error) return err.stack ?? err.message;
8904
+ if (typeof err === "object" && err !== null) {
8905
+ try {
8906
+ const json = JSON.stringify(err);
8907
+ if (json && json !== "{}") return json;
8908
+ } catch {
8909
+ }
8910
+ }
8911
+ try {
8912
+ return String(err);
8913
+ } catch {
8914
+ return `<unprintable ${typeof err} rejection>`;
8915
+ }
8916
+ }
8917
+ function describeWorkerModuleFailure(err) {
8918
+ const detail = describeRejection(err);
8919
+ return `[startup] FATAL \u2014 the manager worker module failed to load, so this process will never poll. The pidfile it already wrote would otherwise make it look like a healthy manager forever (ENG-9129). Exiting so the supervisor can react. Cause: ${detail}`;
8920
+ }
8921
+
8823
8922
  // src/lib/self-update-pin.ts
8824
8923
  function isValidPinVersion(v) {
8825
8924
  return PIN_VERSION_RE.test(v);
@@ -8905,15 +9004,23 @@ function findOtherManagerPids(pgrepImpl = defaultPgrep, selfPid = process.pid) {
8905
9004
  let out;
8906
9005
  try {
8907
9006
  out = pgrepImpl();
8908
- } catch {
9007
+ } catch (err) {
9008
+ const signal = err?.signal;
9009
+ if (signal) {
9010
+ log(
9011
+ `[startup] duplicate-manager scan timed out after ${PGREP_SCAN_TIMEOUT_MS}ms (killed by ${signal}) \u2014 proceeding without it, since the pidfile check is the primary guard. A process-table read this slow means the host is heavily loaded, which is also when a boot is most likely to stall (ENG-9129).`
9012
+ );
9013
+ }
8909
9014
  return [];
8910
9015
  }
8911
9016
  return out.split("\n").map((line) => parseInt(line.trim(), 10)).filter((pid) => !isNaN(pid) && pid !== selfPid);
8912
9017
  }
9018
+ var PGREP_SCAN_TIMEOUT_MS = 1e4;
8913
9019
  function defaultPgrep() {
8914
9020
  return execFileSync3("pgrep", ["-f", "agt manager start"], {
8915
9021
  encoding: "utf-8",
8916
- stdio: ["ignore", "pipe", "ignore"]
9022
+ stdio: ["ignore", "pipe", "ignore"],
9023
+ timeout: PGREP_SCAN_TIMEOUT_MS
8917
9024
  });
8918
9025
  }
8919
9026
  function readStateFile(configDir) {
@@ -8989,11 +9096,18 @@ function startWatchdog(opts) {
8989
9096
  );
8990
9097
  }
8991
9098
  writePidFile(configDir, process.pid);
9099
+ const moduleLoadStartedAt = process.uptime() * 1e3;
9100
+ log("[startup] loading worker module \u2014 nothing polls, logs or deploys MCP bundles until this resolves");
8992
9101
  void import("./lib/manager-worker.js").then(({ startManager }) => {
9102
+ const { message } = describeWorkerModuleLoad(process.uptime() * 1e3 - moduleLoadStartedAt);
9103
+ log(message);
8993
9104
  startManager({
8994
9105
  intervalMs: opts.intervalMs,
8995
9106
  configDir
8996
9107
  });
9108
+ }).catch((err) => {
9109
+ log(describeWorkerModuleFailure(err));
9110
+ process.exit(1);
8997
9111
  });
8998
9112
  process.on("exit", () => {
8999
9113
  removePidFile(configDir);
@@ -9682,6 +9796,7 @@ export {
9682
9796
  buildConnectivityProbeDeps,
9683
9797
  gatherSessionToolBindProbe,
9684
9798
  provision,
9799
+ describeStartupCompletion,
9685
9800
  executeConnectivityProbe,
9686
9801
  isValidPinVersion,
9687
9802
  resolveEffectivePinRaw,
@@ -9699,4 +9814,4 @@ export {
9699
9814
  managerInstallSystemUnitCommand,
9700
9815
  managerUninstallSystemUnitCommand
9701
9816
  };
9702
- //# sourceMappingURL=chunk-KIOTRBOI.js.map
9817
+ //# sourceMappingURL=chunk-LCKKFDCR.js.map