@integrity-labs/agt-cli 0.27.125 → 0.27.127

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.
@@ -17,7 +17,7 @@ import {
17
17
  provisionStopHook,
18
18
  requireHost,
19
19
  safeWriteJsonAtomic
20
- } from "../chunk-QHKJELRV.js";
20
+ } from "../chunk-RKM5IRRT.js";
21
21
  import {
22
22
  getProjectDir as getProjectDir2,
23
23
  getReadyTasks,
@@ -56,7 +56,7 @@ import {
56
56
  stopPersistentSession,
57
57
  takeWatchdogGiveUpCount,
58
58
  takeZombieDetection
59
- } from "../chunk-AZEYTJ4L.js";
59
+ } from "../chunk-QON5CU3L.js";
60
60
  import {
61
61
  KANBAN_CHECK_COMMAND,
62
62
  SUPPRESS_SENTINEL,
@@ -83,7 +83,7 @@ import {
83
83
  resolveDmTarget,
84
84
  worseConnectivityOutcome,
85
85
  wrapScheduledTaskPrompt
86
- } from "../chunk-Z6JJRLHH.js";
86
+ } from "../chunk-MH7HA6QV.js";
87
87
  import {
88
88
  parsePsRows,
89
89
  reapOrphanChannelMcps
@@ -127,6 +127,22 @@ function channelSecretValueHash(envEntries, channelSecretKeys) {
127
127
  return createHash("sha256").update(basis).digest("hex").slice(0, 16);
128
128
  }
129
129
 
130
+ // src/lib/mcp-restart-verify.ts
131
+ var RESTART_VERIFY_DEADLINE_MS = 9e4;
132
+ var RESTART_VERIFY_MAX_ATTEMPTS = 3;
133
+ function decidePostRestartVerification(pending, sessionStartedAt, sessionHealthy, sessionRespawnId, now, opts = {}) {
134
+ const deadlineMs = opts.deadlineMs ?? RESTART_VERIFY_DEADLINE_MS;
135
+ const maxAttempts = opts.maxAttempts ?? RESTART_VERIFY_MAX_ATTEMPTS;
136
+ if (sessionStartedAt !== null && sessionStartedAt > pending.firedAt && sessionHealthy && sessionRespawnId !== null) {
137
+ return { kind: "verified" };
138
+ }
139
+ if (now - pending.firedAt < deadlineMs) {
140
+ return { kind: "waiting" };
141
+ }
142
+ const attempt = pending.attempts + 1;
143
+ return { kind: "unverified", attempt, final: attempt >= maxAttempts };
144
+ }
145
+
130
146
  // src/lib/integration-hash.ts
131
147
  import { createHash as createHash2 } from "crypto";
132
148
  function canonicalize(value) {
@@ -3959,6 +3975,7 @@ function hasRevokedResiduals(state6) {
3959
3975
  return state6.gatewayRunning || state6.portAllocated || state6.provisionDirExists;
3960
3976
  }
3961
3977
  var pendingSessionRestarts = /* @__PURE__ */ new Map();
3978
+ var pendingRestartVerifications = /* @__PURE__ */ new Map();
3962
3979
  var restartBreaker = new RestartBreaker();
3963
3980
  var reportedTrips = /* @__PURE__ */ new Map();
3964
3981
  var mcpFlapDampener = new McpFlapDampener();
@@ -4101,11 +4118,19 @@ function scheduleSessionRestart(codeName, delayMs, reason, breakerReason = "hot-
4101
4118
  runningMcpHashes.delete(codeName);
4102
4119
  recordRestartForBreaker(codeName, breakerReason);
4103
4120
  log(`[hot-reload] Session stopped for '${codeName}' \u2014 will respawn with ${reason}`);
4121
+ if (breakerReason === "hot-reload-mcp") {
4122
+ const prior = pendingRestartVerifications.get(codeName);
4123
+ pendingRestartVerifications.set(codeName, {
4124
+ firedAt: Date.now(),
4125
+ attempts: prior?.attempts ?? 0
4126
+ });
4127
+ }
4104
4128
  }, delayMs);
4105
4129
  timer.unref?.();
4106
4130
  pendingSessionRestarts.set(codeName, timer);
4107
4131
  }
4108
4132
  function cancelPendingSessionRestart(codeName) {
4133
+ pendingRestartVerifications.delete(codeName);
4109
4134
  const existing = pendingSessionRestarts.get(codeName);
4110
4135
  if (!existing) return;
4111
4136
  clearTimeout(existing);
@@ -4113,6 +4138,37 @@ function cancelPendingSessionRestart(codeName) {
4113
4138
  deferLogThrottle.delete(codeName);
4114
4139
  log(`[hot-reload] Cancelled pending restart timer for '${codeName}' (another teardown path is handling it)`);
4115
4140
  }
4141
+ function verifyPendingRestarts(now) {
4142
+ if (pendingRestartVerifications.size === 0) return;
4143
+ for (const [codeName, pending] of pendingRestartVerifications) {
4144
+ const healthy = isSessionHealthy(codeName);
4145
+ const session = getSessionState(codeName);
4146
+ const startedAt = session?.startedAt ?? null;
4147
+ const respawnId = session?.currentSessionId ?? null;
4148
+ const outcome = decidePostRestartVerification(pending, startedAt, healthy, respawnId, now);
4149
+ switch (outcome.kind) {
4150
+ case "verified":
4151
+ pendingRestartVerifications.delete(codeName);
4152
+ log(`[restart-verify] '${codeName}' respawned healthy after MCP change \u2014 tools bound (ENG-6174)`);
4153
+ break;
4154
+ case "waiting":
4155
+ break;
4156
+ case "unverified":
4157
+ if (outcome.final) {
4158
+ pendingRestartVerifications.delete(codeName);
4159
+ log(
4160
+ `[restart-verify] ERROR '${codeName}' did NOT respawn healthy after ${outcome.attempt} attempts following an MCP change \u2014 the live session may be stuck on its pre-restart tools. Check session health / manager.log; a manual session restart may be required (ENG-6174)`
4161
+ );
4162
+ } else {
4163
+ pendingRestartVerifications.set(codeName, { firedAt: now, attempts: outcome.attempt });
4164
+ log(
4165
+ `[restart-verify] WARN '${codeName}' not yet respawned healthy after MCP change (attempt ${outcome.attempt}) \u2014 still watching (ENG-6174)`
4166
+ );
4167
+ }
4168
+ break;
4169
+ }
4170
+ }
4171
+ }
4116
4172
  var RESTART_DEFER_RECHECK_MS = 6e4;
4117
4173
  var DEFER_LOG_THROTTLE_MS = 6e5;
4118
4174
  var deferLogThrottle = /* @__PURE__ */ new Map();
@@ -4438,7 +4494,7 @@ var cachedMaintenanceWindow = null;
4438
4494
  var lastVersionCheckAt = 0;
4439
4495
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
4440
4496
  var lastResponsivenessProbeAt = 0;
4441
- var agtCliVersion = true ? "0.27.125" : "dev";
4497
+ var agtCliVersion = true ? "0.27.127" : "dev";
4442
4498
  function resolveBrewPath(execFileSync4) {
4443
4499
  try {
4444
4500
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -5631,7 +5687,7 @@ async function pollCycle() {
5631
5687
  }
5632
5688
  try {
5633
5689
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
5634
- const { collectDiagnostics } = await import("../persistent-session-YWGDREIZ.js");
5690
+ const { collectDiagnostics } = await import("../persistent-session-PJQZYG2L.js");
5635
5691
  const diagCodeNames = [...agentState.persistentSessionAgents];
5636
5692
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
5637
5693
  let tailscaleHostname;
@@ -5718,12 +5774,12 @@ async function pollCycle() {
5718
5774
  const {
5719
5775
  collectResponsivenessProbes,
5720
5776
  getResponsivenessIntervalMs
5721
- } = await import("../responsiveness-probe-6YYCQAFI.js");
5777
+ } = await import("../responsiveness-probe-MGMZQSP7.js");
5722
5778
  const probeIntervalMs = getResponsivenessIntervalMs();
5723
5779
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
5724
5780
  const probeCodeNames = [...agentState.persistentSessionAgents];
5725
5781
  if (probeCodeNames.length > 0) {
5726
- const { takeAcpxExecFailureCount, creditAcpxExecFailureCount } = await import("../persistent-session-YWGDREIZ.js");
5782
+ const { takeAcpxExecFailureCount, creditAcpxExecFailureCount } = await import("../persistent-session-PJQZYG2L.js");
5727
5783
  const drainedGiveUps = /* @__PURE__ */ new Map();
5728
5784
  const drainedAcpxFailures = /* @__PURE__ */ new Map();
5729
5785
  const probes = collectResponsivenessProbes(probeCodeNames).map((p) => {
@@ -5757,7 +5813,7 @@ async function pollCycle() {
5757
5813
  collectResponsivenessProbes,
5758
5814
  livePendingInboundOldestAgeSeconds,
5759
5815
  deadLetterPendingInbound
5760
- } = await import("../responsiveness-probe-6YYCQAFI.js");
5816
+ } = await import("../responsiveness-probe-MGMZQSP7.js");
5761
5817
  const wedgeNow = /* @__PURE__ */ new Date();
5762
5818
  const liveAgents = agentState.persistentSessionAgents;
5763
5819
  for (const tracked of consecutiveWedgeCycles.keys()) {
@@ -6106,6 +6162,7 @@ async function pollCycle() {
6106
6162
  log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
6107
6163
  consecutivePollFailures = 0;
6108
6164
  }
6165
+ verifyPendingRestarts(Date.now());
6109
6166
  send({ type: "state-update", state: state5 });
6110
6167
  } catch (err) {
6111
6168
  state5.errorCount++;
@@ -10253,7 +10310,7 @@ async function processClaudePairSessions(agents) {
10253
10310
  killPairSession,
10254
10311
  pairTmuxSession,
10255
10312
  finalizeClaudePairOnboarding
10256
- } = await import("../claude-pair-runtime-ERGB26MZ.js");
10313
+ } = await import("../claude-pair-runtime-QNOWFDJ7.js");
10257
10314
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
10258
10315
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
10259
10316
  const killed = await killPairSession(pairTmuxSession(pairId));