@integrity-labs/agt-cli 0.28.384 → 0.28.386

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.
@@ -100,7 +100,7 @@ async function spawnPairSession(session) {
100
100
  return { ok: true };
101
101
  } catch {
102
102
  }
103
- const { resolveClaudeBinary } = await import("./persistent-session-MDQVHXKX.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-KQHWYEQF.js");
104
104
  const claudeBin = resolveClaudeBinary();
105
105
  const pairEnv = {
106
106
  ...process.env,
@@ -373,4 +373,4 @@ export {
373
373
  startClaudePair,
374
374
  submitClaudePairCode
375
375
  };
376
- //# sourceMappingURL=claude-pair-runtime-OIK6HKPS.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-NVMVUAGS.js.map
@@ -43,7 +43,7 @@ import {
43
43
  resolveEffectivePinRaw,
44
44
  safeWriteJsonAtomic,
45
45
  setConfigHash
46
- } from "../chunk-IXN2AFOX.js";
46
+ } from "../chunk-O37EMHVE.js";
47
47
  import {
48
48
  getProjectDir as getProjectDir2,
49
49
  getReadyTasks,
@@ -145,7 +145,7 @@ import {
145
145
  toOpencodeModel,
146
146
  transcriptActivityAgeSeconds,
147
147
  writeEgressAllowlist
148
- } from "../chunk-MQHDPJZD.js";
148
+ } from "../chunk-LAO2CUZU.js";
149
149
  import {
150
150
  reapOrphanChannelMcps
151
151
  } from "../chunk-XWVM4KPK.js";
@@ -725,6 +725,24 @@ var ChannelQuarantineStore = class {
725
725
  const forAgent = this.load()[codeName];
726
726
  return new Set(forAgent ? Object.keys(forAgent) : []);
727
727
  }
728
+ /**
729
+ * ENG-8018: the full quarantine entries for `codeName` (serverKey → {reason,
730
+ * quarantinedAt}), empty array if none. Unlike {@link getQuarantinedKeys} this
731
+ * carries the reason + timestamp needed to surface the quarantine to the
732
+ * control plane (the manager folds it into the agent heartbeat's diagnostics)
733
+ * and to the agent itself, so an operator sees WHY/WHEN on the webapp badge
734
+ * and the agent reports the real cause instead of confabulating one (ENG-8003).
735
+ * Returns a defensive copy so callers can't mutate the cached store.
736
+ */
737
+ getQuarantineEntries(codeName) {
738
+ const forAgent = this.load()[codeName];
739
+ if (!forAgent) return [];
740
+ return Object.entries(forAgent).map(([serverKey, entry]) => ({
741
+ serverKey,
742
+ reason: entry.reason,
743
+ quarantinedAt: entry.quarantinedAt
744
+ }));
745
+ }
728
746
  /**
729
747
  * Mark `serverKey` quarantined for `codeName`. Idempotent — re-quarantining
730
748
  * an already-quarantined key preserves the original `quarantinedAt` (so the
@@ -8207,6 +8225,7 @@ function hasRevokedResiduals(state7) {
8207
8225
  return state7.gatewayRunning || state7.portAllocated || state7.provisionDirExists;
8208
8226
  }
8209
8227
  var pendingSessionRestarts = /* @__PURE__ */ new Map();
8228
+ var lastRestartWasExternal = /* @__PURE__ */ new Map();
8210
8229
  var pendingRestartVerifications = /* @__PURE__ */ new Map();
8211
8230
  var restartBreaker = new RestartBreaker();
8212
8231
  var reportedTrips = /* @__PURE__ */ new Map();
@@ -8907,6 +8926,7 @@ function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason, gate
8907
8926
  }
8908
8927
  cancelPendingSessionRestart(codeName);
8909
8928
  stopPersistentSession(codeName, log);
8929
+ lastRestartWasExternal.set(codeName, gateReason !== "mcp-presence-reaper");
8910
8930
  runningMcpHashes.delete(codeName);
8911
8931
  runningMcpServerKeys.delete(codeName);
8912
8932
  runningChannelSecretHashes.delete(codeName);
@@ -9093,7 +9113,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9093
9113
  var lastVersionCheckAt = 0;
9094
9114
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9095
9115
  var lastResponsivenessProbeAt = 0;
9096
- var agtCliVersion = true ? "0.28.384" : "dev";
9116
+ var agtCliVersion = true ? "0.28.386" : "dev";
9097
9117
  function resolveBrewPath(execFileSync2) {
9098
9118
  try {
9099
9119
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -10083,6 +10103,9 @@ function channelQuarantineStore() {
10083
10103
  }
10084
10104
  return _channelQuarantineStore;
10085
10105
  }
10106
+ function quarantineEntriesFor(codeName) {
10107
+ return channelQuarantineStore().getQuarantineEntries(codeName);
10108
+ }
10086
10109
  var _hostFlagStore = null;
10087
10110
  function hostFlagStore() {
10088
10111
  if (!_hostFlagStore) {
@@ -10222,10 +10245,10 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
10222
10245
  if (codeNames.length === 0) return;
10223
10246
  void (async () => {
10224
10247
  try {
10225
- const { collectDiagnostics } = await import("../persistent-session-MDQVHXKX.js");
10248
+ const { collectDiagnostics } = await import("../persistent-session-KQHWYEQF.js");
10226
10249
  await api.post("/host/heartbeat", {
10227
10250
  host_id: hostId,
10228
- agent_diagnostics: collectDiagnostics(codeNames)
10251
+ agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor)
10229
10252
  });
10230
10253
  } catch (err) {
10231
10254
  log(`[restart] post-respawn diagnostics flush failed: ${err.message}`);
@@ -10321,9 +10344,9 @@ async function pollCycle() {
10321
10344
  }
10322
10345
  try {
10323
10346
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
10324
- const { collectDiagnostics } = await import("../persistent-session-MDQVHXKX.js");
10347
+ const { collectDiagnostics } = await import("../persistent-session-KQHWYEQF.js");
10325
10348
  const diagCodeNames = [...agentState.persistentSessionAgents];
10326
- const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
10349
+ const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor) : void 0;
10327
10350
  let tailscaleHostname;
10328
10351
  try {
10329
10352
  const { execSync: es } = await import("child_process");
@@ -10476,7 +10499,7 @@ async function pollCycle() {
10476
10499
  const {
10477
10500
  collectResponsivenessProbes,
10478
10501
  getResponsivenessIntervalMs
10479
- } = await import("../responsiveness-probe-CG7XM7GL.js");
10502
+ } = await import("../responsiveness-probe-5X6WVKUZ.js");
10480
10503
  const probeIntervalMs = getResponsivenessIntervalMs();
10481
10504
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
10482
10505
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -10508,7 +10531,7 @@ async function pollCycle() {
10508
10531
  collectResponsivenessProbes,
10509
10532
  livePendingInboundOldestAgeSeconds,
10510
10533
  parkPendingInbound
10511
- } = await import("../responsiveness-probe-CG7XM7GL.js");
10534
+ } = await import("../responsiveness-probe-5X6WVKUZ.js");
10512
10535
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
10513
10536
  const wedgeNow = /* @__PURE__ */ new Date();
10514
10537
  const liveAgents = agentState.persistentSessionAgents;
@@ -12723,6 +12746,14 @@ async function processAgent(agent, agentStates) {
12723
12746
  // but-not-yet-live MCP, so don't let the reaper re-fire + inflate the
12724
12747
  // McpReaperRestartsHourly metric into a false flapping alarm.
12725
12748
  restartPending: pendingSessionRestarts.has(agent.code_name),
12749
+ // ENG-8013 (storm-awareness): tell the reaper whether THIS session
12750
+ // generation was born from an external respawn (integration-change /
12751
+ // channel-set-change / day-rollover / wedge / operator restart, or a
12752
+ // storm) vs the reaper's own recovery restart. When external, a missing
12753
+ // channel MCP is churn collateral and must not accrue quarantine budget,
12754
+ // the durable fix for the ENG-8003 storm-quarantine incident. Absent map
12755
+ // entry (first boot) → false → count normally.
12756
+ currentGenerationExternallyStarted: lastRestartWasExternal.get(agent.code_name) ?? false,
12726
12757
  // ENG-5441 / ENG-5547: a mcp-presence-reaper restart counts against
12727
12758
  // the agent-wide breaker ONLY when several distinct MCP servers are
12728
12759
  // missing at once — a thrashing collection that rotates restarts of
@@ -12844,6 +12875,14 @@ async function processAgent(agent, agentStates) {
12844
12875
  "quarantine",
12845
12876
  `optional channel '${serverKey}' dead past restart budget + dwell \u2014 quarantined`
12846
12877
  );
12878
+ void injectMessage(
12879
+ codeName,
12880
+ "system",
12881
+ `Your "${serverKey}" channel has been quarantined by the host runtime: it repeatedly failed to start (no live MCP process) past the recovery budget, so it has been pulled from your active channel set. You cannot send or receive on ${serverKey} until an operator re-enables it. This is an infrastructure fault, not a policy or allowlist decision. If anyone asks why you are quiet on ${serverKey}, tell them the channel is quarantined and ask them to have an operator re-enable it. Do not guess at other causes.`,
12882
+ { task_name: "channel-quarantine" },
12883
+ log
12884
+ ).catch(() => {
12885
+ });
12847
12886
  }
12848
12887
  }
12849
12888
  });
@@ -13703,10 +13742,10 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
13703
13742
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
13704
13743
  void (async () => {
13705
13744
  try {
13706
- const { collectDiagnostics } = await import("../persistent-session-MDQVHXKX.js");
13745
+ const { collectDiagnostics } = await import("../persistent-session-KQHWYEQF.js");
13707
13746
  await api.post("/host/heartbeat", {
13708
13747
  host_id: hostId,
13709
- agent_diagnostics: collectDiagnostics([codeName])
13748
+ agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor)
13710
13749
  });
13711
13750
  } catch (err) {
13712
13751
  log(`[restart-lane] post-respawn diagnostics flush failed for '${codeName}': ${err.message}`);
@@ -13753,10 +13792,10 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
13753
13792
  }
13754
13793
  try {
13755
13794
  const hostId = await getHostId();
13756
- const { collectDiagnostics } = await import("../persistent-session-MDQVHXKX.js");
13795
+ const { collectDiagnostics } = await import("../persistent-session-KQHWYEQF.js");
13757
13796
  await api.post("/host/heartbeat", {
13758
13797
  host_id: hostId,
13759
- agent_diagnostics: collectDiagnostics([codeName])
13798
+ agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor)
13760
13799
  });
13761
13800
  } catch (err) {
13762
13801
  log(`[fast-mcp-respawn] post-respawn diagnostics flush failed for '${codeName}': ${err.message}`);
@@ -14206,7 +14245,7 @@ async function processClaudePairSessions(agents) {
14206
14245
  killPairSession,
14207
14246
  pairTmuxSession,
14208
14247
  finalizeClaudePairOnboarding
14209
- } = await import("../claude-pair-runtime-OIK6HKPS.js");
14248
+ } = await import("../claude-pair-runtime-NVMVUAGS.js");
14210
14249
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
14211
14250
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
14212
14251
  const killed = await killPairSession(pairTmuxSession(pairId));