@integrity-labs/agt-cli 0.28.18 → 0.28.20

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  claudeModelAlias,
3
3
  isClaudeFastMode
4
- } from "./chunk-6UXSC4TR.js";
4
+ } from "./chunk-3HZSMDEW.js";
5
5
  import {
6
6
  reapOrphanChannelMcps
7
7
  } from "./chunk-XWVM4KPK.js";
@@ -1487,4 +1487,4 @@ export {
1487
1487
  stopAllSessionsAndWait,
1488
1488
  getProjectDir
1489
1489
  };
1490
- //# sourceMappingURL=chunk-5DIMLYS5.js.map
1490
+ //# sourceMappingURL=chunk-BKO5PJZ7.js.map
@@ -14,7 +14,7 @@ import {
14
14
  registerFramework,
15
15
  resolveAvatarEnvUrl,
16
16
  wrapScheduledTaskPrompt
17
- } from "./chunk-6UXSC4TR.js";
17
+ } from "./chunk-3HZSMDEW.js";
18
18
 
19
19
  // ../../packages/core/dist/integrations/registry.js
20
20
  var INTEGRATION_REGISTRY = [
@@ -8275,4 +8275,4 @@ export {
8275
8275
  managerInstallSystemUnitCommand,
8276
8276
  managerUninstallSystemUnitCommand
8277
8277
  };
8278
- //# sourceMappingURL=chunk-VPMNDODG.js.map
8278
+ //# sourceMappingURL=chunk-U6IPQT2J.js.map
@@ -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-RANQKKKC.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-5BEMWOTM.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-CFUSOOVY.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-LQJ5E4IF.js.map
@@ -22,7 +22,7 @@ import {
22
22
  provisionStopHook,
23
23
  requireHost,
24
24
  safeWriteJsonAtomic
25
- } from "../chunk-VPMNDODG.js";
25
+ } from "../chunk-U6IPQT2J.js";
26
26
  import {
27
27
  getProjectDir as getProjectDir2,
28
28
  getReadyTasks,
@@ -64,7 +64,7 @@ import {
64
64
  takeWatchdogGiveUpCount,
65
65
  takeZombieDetection,
66
66
  transcriptActivityAgeSeconds
67
- } from "../chunk-5DIMLYS5.js";
67
+ } from "../chunk-BKO5PJZ7.js";
68
68
  import {
69
69
  FLAGS_SCHEMA_VERSION,
70
70
  KANBAN_CHECK_COMMAND,
@@ -96,7 +96,7 @@ import {
96
96
  sumTranscriptUsageInWindow,
97
97
  worseConnectivityOutcome,
98
98
  wrapScheduledTaskPrompt
99
- } from "../chunk-6UXSC4TR.js";
99
+ } from "../chunk-3HZSMDEW.js";
100
100
  import {
101
101
  parsePsRows,
102
102
  reapOrphanChannelMcps
@@ -434,6 +434,16 @@ function clearPresenceReaperStateForKeys(codeName, keys) {
434
434
  presenceReaperState.delete(stateKey(codeName, key));
435
435
  }
436
436
  }
437
+ function givenUpMcpServerKeys(codeName) {
438
+ const prefix = `${codeName}\0`;
439
+ const out = /* @__PURE__ */ new Set();
440
+ for (const [key, st] of presenceReaperState) {
441
+ if (key.startsWith(prefix) && st.attempts >= MAX_PRESENCE_RESTART_ATTEMPTS) {
442
+ out.add(key.slice(prefix.length));
443
+ }
444
+ }
445
+ return out;
446
+ }
437
447
  function findMissingMcpServers(args) {
438
448
  const { rows, codeName, mcpJson } = args;
439
449
  const servers = mcpJson?.mcpServers ?? {};
@@ -1099,6 +1109,69 @@ function hydrateAutoResumeMarkers(saved) {
1099
1109
  return map;
1100
1110
  }
1101
1111
 
1112
+ // src/lib/resume-reconciler.ts
1113
+ var DEFAULT_MIN_HEALTHY_CYCLES = 2;
1114
+ var DEFAULT_BACKOFF_RESET_MS2 = 864e5;
1115
+ function readResumeReconcilerConfig(env = process.env) {
1116
+ const raw = (env["AGT_RESUME_RECONCILER_ENABLED"] ?? "").trim().toLowerCase();
1117
+ const minCycles = readEnvNumber("AGT_RESUME_MIN_HEALTHY_CYCLES", DEFAULT_MIN_HEALTHY_CYCLES);
1118
+ return {
1119
+ enabled: raw === "1" || raw === "true" || raw === "yes" || raw === "on",
1120
+ // A fractional / <1 override is meaningless for a cycle count — floor to an
1121
+ // integer and demand at least 1, so "healthy" always means "observed at
1122
+ // least once". readEnvNumber already rejects NaN / <=0 to the default.
1123
+ minHealthyCycles: Math.max(1, Math.floor(minCycles)),
1124
+ backoffResetMs: readEnvNumber("AGT_AUTO_RESUME_BACKOFF_RESET_MS", DEFAULT_BACKOFF_RESET_MS2)
1125
+ };
1126
+ }
1127
+ function decideResumeReconcile(opts) {
1128
+ const { trip, marker, health, config: config2, now } = opts;
1129
+ if (!config2.enabled) return { action: "hold", reason: "disabled" };
1130
+ if (!trip) return { action: "hold", reason: "no-trip" };
1131
+ if (marker && now - marker.autoResumedAt < config2.backoffResetMs) {
1132
+ if (trip.trippedAt === marker.trippedAt) {
1133
+ return { action: "hold", reason: "not-healthy-yet" };
1134
+ }
1135
+ return { action: "latch-unstable", trippedAt: trip.trippedAt };
1136
+ }
1137
+ if (!health.dependencyStatusActive) {
1138
+ return { action: "hold", reason: "not-healthy-yet", precondition: "status" };
1139
+ }
1140
+ if (!health.mcpPresent) {
1141
+ return { action: "hold", reason: "not-healthy-yet", precondition: "mcp" };
1142
+ }
1143
+ if (health.connectivityOkCycles < config2.minHealthyCycles) {
1144
+ return {
1145
+ action: "hold",
1146
+ reason: "not-healthy-yet",
1147
+ precondition: "connectivity",
1148
+ cyclesShort: config2.minHealthyCycles - Math.max(0, health.connectivityOkCycles)
1149
+ };
1150
+ }
1151
+ return { action: "resume", healthyCycles: health.connectivityOkCycles };
1152
+ }
1153
+
1154
+ // src/lib/resume-reconciler-inputs.ts
1155
+ function deriveMcpPresent(declaredKeys, givenUpKeys) {
1156
+ if (declaredKeys === null) return false;
1157
+ for (const key of declaredKeys) {
1158
+ if (givenUpKeys.has(key)) return false;
1159
+ }
1160
+ return true;
1161
+ }
1162
+ function parseResumeHealthResponse(raw) {
1163
+ if (!raw || typeof raw !== "object") return null;
1164
+ const r = raw;
1165
+ if (typeof r.dependencyStatusActive !== "boolean") return null;
1166
+ if (typeof r.connectivityOkCycles !== "number" || !Number.isFinite(r.connectivityOkCycles)) {
1167
+ return null;
1168
+ }
1169
+ return {
1170
+ dependencyStatusActive: r.dependencyStatusActive,
1171
+ connectivityOkCycles: r.connectivityOkCycles
1172
+ };
1173
+ }
1174
+
1102
1175
  // src/lib/model-change-respawn.ts
1103
1176
  function shouldRespawnForModelChange(input) {
1104
1177
  const { previousModel, primaryModel, framework, sessionHealthy } = input;
@@ -4741,6 +4814,10 @@ var killPausedCodeNames = /* @__PURE__ */ new Set();
4741
4814
  var BACK_ONLINE_GREETING_GUIDANCE = " When you reconnect, if you tell anyone you are back, start that message with a \u{1F44B} wave emoji and do not use a \u{1F7E2} green-light emoji.";
4742
4815
  function maybeAutoResume(agent) {
4743
4816
  const codeName = agent.code_name;
4817
+ if (hostFlagStore().getBoolean("resume-reconciler")) {
4818
+ void maybeResumeReconcile(agent);
4819
+ return;
4820
+ }
4744
4821
  if (autoResumeInFlight.has(codeName)) return;
4745
4822
  const trip = restartBreaker.getTrip(codeName);
4746
4823
  if (trip && autoResumeStandDowns.has(`${codeName}:${trip.trippedAt}`)) return;
@@ -4792,6 +4869,92 @@ function maybeAutoResume(agent) {
4792
4869
  log(`[auto-resume] agent=${codeName} POST failed (will retry next poll): ${err.message}`);
4793
4870
  });
4794
4871
  }
4872
+ function logResumeReconcileHoldOnce(codeName, trippedAt, reason, detail) {
4873
+ const key = `${codeName}:${trippedAt}`;
4874
+ if (autoResumeLoggedSkips.get(key) !== reason) {
4875
+ autoResumeLoggedSkips.set(key, reason);
4876
+ log(`[resume-reconciler] agent=${codeName} decision=hold reason=${reason}${detail ? ` (${detail})` : ""} (ENG-6383)`);
4877
+ }
4878
+ }
4879
+ async function maybeResumeReconcile(agent) {
4880
+ const codeName = agent.code_name;
4881
+ if (autoResumeInFlight.has(codeName)) return;
4882
+ const trip = restartBreaker.getTrip(codeName);
4883
+ if (!trip) return;
4884
+ const standDownKey = `${codeName}:${trip.trippedAt}`;
4885
+ if (autoResumeStandDowns.has(standDownKey)) return;
4886
+ autoResumeInFlight.add(codeName);
4887
+ try {
4888
+ const config2 = { ...readResumeReconcilerConfig(), enabled: hostFlagStore().getBoolean("resume-reconciler") };
4889
+ const trippedAt = trip.trippedAt;
4890
+ let serverHealth = null;
4891
+ try {
4892
+ const raw = await api.get(
4893
+ `/host/circuit-breaker/resume-health?agent_id=${encodeURIComponent(agent.agent_id)}`
4894
+ );
4895
+ serverHealth = parseResumeHealthResponse(raw);
4896
+ } catch (err) {
4897
+ logResumeReconcileHoldOnce(codeName, trippedAt, "health-fetch-failed", err.message.slice(0, 80));
4898
+ return;
4899
+ }
4900
+ if (!serverHealth) {
4901
+ logResumeReconcileHoldOnce(codeName, trippedAt, "health-unparseable");
4902
+ return;
4903
+ }
4904
+ const declaredKeys = projectMcpKeys(codeName, getProjectDir(codeName));
4905
+ const mcpPresent = deriveMcpPresent(declaredKeys, givenUpMcpServerKeys(codeName));
4906
+ const decision = decideResumeReconcile({
4907
+ trip,
4908
+ marker: autoResumeMarkers.get(codeName),
4909
+ health: { ...serverHealth, mcpPresent },
4910
+ config: config2,
4911
+ now: Date.now()
4912
+ });
4913
+ if (decision.action === "hold") {
4914
+ const reason = decision.precondition ? `not-healthy-yet:${decision.precondition}` : decision.reason;
4915
+ const detail = decision.cyclesShort ? `${decision.cyclesShort}-short` : void 0;
4916
+ logResumeReconcileHoldOnce(codeName, trippedAt, reason, detail);
4917
+ return;
4918
+ }
4919
+ if (decision.action === "latch-unstable") {
4920
+ autoResumeStandDowns.add(standDownKey);
4921
+ const key = `${codeName}:${trippedAt}`;
4922
+ if (autoResumeLoggedSkips.get(key) !== "latch-unstable") {
4923
+ autoResumeLoggedSkips.set(key, "latch-unstable");
4924
+ log(
4925
+ `[resume-reconciler] agent=${codeName} decision=latch-unstable \u2014 auto-resumed once then re-tripped within the backoff window; latching to mandatory-manual (NOT resuming; restart counter untouched; agent stays paused with its circuit-breaker alert open) (ENG-6383)`
4926
+ );
4927
+ }
4928
+ return;
4929
+ }
4930
+ log(
4931
+ `[resume-reconciler] agent=${codeName} decision=resume healthyCycles=${decision.healthyCycles} trippedAt=${new Date(trippedAt).toISOString()} (ENG-6383)`
4932
+ );
4933
+ const res = await api.post(
4934
+ "/host/circuit-breaker/auto-resume",
4935
+ { agent_id: agent.agent_id, tripped_at: new Date(trippedAt).toISOString() }
4936
+ );
4937
+ if (res.resumed) {
4938
+ autoResumeMarkers.set(codeName, { trippedAt, autoResumedAt: Date.now() });
4939
+ restartBreaker.clear(codeName);
4940
+ reportedTrips.delete(codeName);
4941
+ state6 = {
4942
+ ...state6,
4943
+ circuitBreakerTrips: restartBreaker.serialize(),
4944
+ circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
4945
+ };
4946
+ send({ type: "state-update", state: state6 });
4947
+ log(`[resume-reconciler] agent=${codeName} resumed \u2014 a re-trip within the backoff window will latch unstable (ENG-6383)`);
4948
+ } else {
4949
+ autoResumeStandDowns.add(standDownKey);
4950
+ log(`[resume-reconciler] agent=${codeName} not applied (reason=${res.reason ?? "unknown"}) \u2014 standing down for this trip (ENG-6383)`);
4951
+ }
4952
+ } catch (err) {
4953
+ log(`[resume-reconciler] agent=${codeName} reconcile failed (will retry next poll): ${err.message}`);
4954
+ } finally {
4955
+ autoResumeInFlight.delete(codeName);
4956
+ }
4957
+ }
4795
4958
  function scheduleSessionRestart(codeName, delayMs, reason, breakerReason = "hot-reload-mcp") {
4796
4959
  const existing = pendingSessionRestarts.get(codeName);
4797
4960
  if (existing) {
@@ -5266,7 +5429,7 @@ var cachedMaintenanceWindow = null;
5266
5429
  var lastVersionCheckAt = 0;
5267
5430
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
5268
5431
  var lastResponsivenessProbeAt = 0;
5269
- var agtCliVersion = true ? "0.28.18" : "dev";
5432
+ var agtCliVersion = true ? "0.28.20" : "dev";
5270
5433
  function resolveBrewPath(execFileSync4) {
5271
5434
  try {
5272
5435
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -6375,7 +6538,7 @@ async function pollCycle() {
6375
6538
  }
6376
6539
  try {
6377
6540
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
6378
- const { collectDiagnostics } = await import("../persistent-session-RANQKKKC.js");
6541
+ const { collectDiagnostics } = await import("../persistent-session-5BEMWOTM.js");
6379
6542
  const diagCodeNames = [...agentState.persistentSessionAgents];
6380
6543
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
6381
6544
  let tailscaleHostname;
@@ -6469,7 +6632,7 @@ async function pollCycle() {
6469
6632
  const {
6470
6633
  collectResponsivenessProbes,
6471
6634
  getResponsivenessIntervalMs
6472
- } = await import("../responsiveness-probe-KQP6MY5N.js");
6635
+ } = await import("../responsiveness-probe-JP4HLFYU.js");
6473
6636
  const probeIntervalMs = getResponsivenessIntervalMs();
6474
6637
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
6475
6638
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -6501,7 +6664,7 @@ async function pollCycle() {
6501
6664
  collectResponsivenessProbes,
6502
6665
  livePendingInboundOldestAgeSeconds,
6503
6666
  parkPendingInbound
6504
- } = await import("../responsiveness-probe-KQP6MY5N.js");
6667
+ } = await import("../responsiveness-probe-JP4HLFYU.js");
6505
6668
  const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
6506
6669
  const wedgeNow = /* @__PURE__ */ new Date();
6507
6670
  const liveAgents = agentState.persistentSessionAgents;
@@ -11092,7 +11255,7 @@ async function processClaudePairSessions(agents) {
11092
11255
  killPairSession,
11093
11256
  pairTmuxSession,
11094
11257
  finalizeClaudePairOnboarding
11095
- } = await import("../claude-pair-runtime-CFUSOOVY.js");
11258
+ } = await import("../claude-pair-runtime-LQJ5E4IF.js");
11096
11259
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
11097
11260
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
11098
11261
  const killed = await killPairSession(pairTmuxSession(pairId));