@integrity-labs/agt-cli 0.28.461 → 0.28.462

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-WCYGJANF.js";
43
+ } from "../chunk-UNDFX52P.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.461" : "dev";
4833
+ var cliVersion = true ? "0.28.462" : "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.461" : "dev";
6005
+ var cliVersion2 = true ? "0.28.462" : "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) => {
@@ -4911,7 +4911,7 @@ function requireHost() {
4911
4911
  }
4912
4912
 
4913
4913
  // src/lib/api-client.ts
4914
- var agtCliVersion = true ? "0.28.461" : "dev";
4914
+ var agtCliVersion = true ? "0.28.462" : "dev";
4915
4915
  var lastConfigHash = null;
4916
4916
  function setConfigHash(hash) {
4917
4917
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -5401,13 +5401,24 @@ function recordStaleRotation(codeName, serverKeys, now = Date.now) {
5401
5401
  rotationTimestamps.set(rotationKey(codeName, key), ts);
5402
5402
  }
5403
5403
  }
5404
- function wasRecentlyRotated(codeName, serverKey, withinMs, now = Date.now) {
5404
+ var ROTATION_TRACKING_RETENTION_MS = 15 * 60 * 1e3;
5405
+ function classifyRotation(codeName, serverKey, graceMs, now = Date.now, retentionMs = ROTATION_TRACKING_RETENTION_MS) {
5405
5406
  const k = rotationKey(codeName, serverKey);
5406
5407
  const ts = rotationTimestamps.get(k);
5407
- if (ts === void 0) return false;
5408
- if (now() - ts < withinMs) return true;
5408
+ if (ts === void 0) return "none";
5409
+ const age = now() - ts;
5410
+ if (age < graceMs) return "within-grace";
5411
+ if (age < retentionMs) return "expired";
5409
5412
  rotationTimestamps.delete(k);
5410
- return false;
5413
+ return "none";
5414
+ }
5415
+ function getRotationRecordedAt(codeName, serverKey) {
5416
+ return rotationTimestamps.get(rotationKey(codeName, serverKey));
5417
+ }
5418
+ function clearStaleRotation(codeName, serverKeys) {
5419
+ for (const key of serverKeys) {
5420
+ rotationTimestamps.delete(rotationKey(codeName, key));
5421
+ }
5411
5422
  }
5412
5423
  function parseEnvIntegrationsEntries(content) {
5413
5424
  const entries = /* @__PURE__ */ new Map();
@@ -5706,6 +5717,9 @@ var BACKOFF_GIVE_UP_RESTARTS = 2 * MAX_PRESENCE_RESTART_ATTEMPTS;
5706
5717
  var BACKOFF_STABLE_RESET_MS = 5 * 6e4;
5707
5718
  var DEAD_CHANNEL_MS = 15 * 6e4;
5708
5719
  var presenceReaperState = /* @__PURE__ */ new Map();
5720
+ var MAX_ROTATION_PROMPT_PROBES = 3;
5721
+ var ROTATION_PROMPT_PROBE_INTERVAL_MS = 3e4;
5722
+ var rotationStallState = /* @__PURE__ */ new Map();
5709
5723
  function stateKey(codeName, serverKey) {
5710
5724
  return `${codeName}\0${serverKey}`;
5711
5725
  }
@@ -5718,6 +5732,7 @@ function clearPresenceReaperState(codeName) {
5718
5732
  function clearPresenceReaperStateForKeys(codeName, keys) {
5719
5733
  for (const key of keys) {
5720
5734
  presenceReaperState.delete(stateKey(codeName, key));
5735
+ rotationStallState.delete(stateKey(codeName, key));
5721
5736
  }
5722
5737
  }
5723
5738
  function givenUpMcpServerKeys(codeName) {
@@ -5763,6 +5778,14 @@ function reapMissingMcpSessions(args) {
5763
5778
  rotationGraceMs = DEFAULT_ROTATION_GRACE_MS,
5764
5779
  onGiveUp,
5765
5780
  onRestart,
5781
+ // ENG-8225: unwired callbacks make the prompt-probe path a strict no-op —
5782
+ // `onRotationRespawnStalled` absent means "not armed", which falls through to
5783
+ // the pre-ENG-8225 restart. Legacy callers and tests are unchanged.
5784
+ onRotationRespawnStalled,
5785
+ onRotationRespawnGaveUp,
5786
+ onRotationRespawnOutcome,
5787
+ maxRotationPromptProbes = MAX_ROTATION_PROMPT_PROBES,
5788
+ rotationPromptProbeIntervalMs = ROTATION_PROMPT_PROBE_INTERVAL_MS,
5766
5789
  // ENG-5932: defaults make quarantine a strict no-op (classify everything
5767
5790
  // essential, mode off) so legacy callers/tests are unaffected.
5768
5791
  classifyKey = () => "essential",
@@ -5807,19 +5830,112 @@ function reapMissingMcpSessions(args) {
5807
5830
  const rows = parsePsRows(psOutput);
5808
5831
  const missingRaw = findMissingMcpServers({ rows, codeName, mcpJson, inContainer: isolated });
5809
5832
  const rotationGraced = [];
5833
+ const rotationProbeArmed = [];
5834
+ const rotationProbeWaiting = [];
5835
+ const rotationProbeExhausted = [];
5836
+ const rotationRespawnOutcomes = {};
5837
+ const recordRotationOutcome = (key, outcome) => {
5838
+ rotationRespawnOutcomes[key] = outcome;
5839
+ if (!onRotationRespawnOutcome) return;
5840
+ try {
5841
+ onRotationRespawnOutcome(codeName, key, outcome);
5842
+ } catch (err) {
5843
+ log(
5844
+ `[mcp-presence-reaper] onRotationRespawnOutcome callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
5845
+ );
5846
+ }
5847
+ };
5810
5848
  const missing = [];
5811
5849
  for (const key of missingRaw) {
5812
- if (wasRecentlyRotated(codeName, key, rotationGraceMs, now)) {
5850
+ const rotation = classifyRotation(codeName, key, rotationGraceMs, now);
5851
+ if (rotation === "within-grace") {
5813
5852
  rotationGraced.push(key);
5814
- } else {
5853
+ continue;
5854
+ }
5855
+ if (rotation === "none") {
5856
+ rotationStallState.delete(stateKey(codeName, key));
5857
+ missing.push(key);
5858
+ continue;
5859
+ }
5860
+ const sk = stateKey(codeName, key);
5861
+ const rotationRecordedAt = getRotationRecordedAt(codeName, key) ?? now();
5862
+ const priorStall = rotationStallState.get(sk);
5863
+ const stall = priorStall && priorStall.rotationRecordedAt === rotationRecordedAt ? priorStall : {
5864
+ rotationRecordedAt,
5865
+ probeAttempts: 0,
5866
+ lastProbeAt: null,
5867
+ escalated: false
5868
+ };
5869
+ if (priorStall && priorStall !== stall) {
5870
+ log(
5871
+ `[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)`
5872
+ );
5873
+ }
5874
+ rotationStallState.set(sk, stall);
5875
+ if (stall.escalated) {
5815
5876
  missing.push(key);
5877
+ continue;
5816
5878
  }
5879
+ const spacingElapsed = stall.lastProbeAt === null || now() - stall.lastProbeAt >= rotationPromptProbeIntervalMs;
5880
+ if (!spacingElapsed) {
5881
+ rotationProbeWaiting.push(key);
5882
+ continue;
5883
+ }
5884
+ if (stall.probeAttempts >= maxRotationPromptProbes) {
5885
+ stall.escalated = true;
5886
+ rotationProbeExhausted.push(key);
5887
+ recordRotationOutcome(key, "never-respawned");
5888
+ log(
5889
+ `[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)`
5890
+ );
5891
+ if (onRotationRespawnGaveUp) {
5892
+ try {
5893
+ onRotationRespawnGaveUp(codeName, key, { attempts: stall.probeAttempts });
5894
+ } catch (err) {
5895
+ log(
5896
+ `[mcp-presence-reaper] onRotationRespawnGaveUp callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
5897
+ );
5898
+ }
5899
+ }
5900
+ missing.push(key);
5901
+ continue;
5902
+ }
5903
+ const attempt = stall.probeAttempts + 1;
5904
+ let armed = false;
5905
+ if (onRotationRespawnStalled) {
5906
+ try {
5907
+ armed = onRotationRespawnStalled(codeName, key, {
5908
+ attempt,
5909
+ maxAttempts: maxRotationPromptProbes
5910
+ });
5911
+ } catch (err) {
5912
+ log(
5913
+ `[mcp-presence-reaper] onRotationRespawnStalled callback threw for '${codeName}:${key}' (suppressed; falling back to the pre-ENG-8225 restart path): ${err.message}`
5914
+ );
5915
+ armed = false;
5916
+ }
5917
+ }
5918
+ if (!armed) {
5919
+ missing.push(key);
5920
+ continue;
5921
+ }
5922
+ stall.probeAttempts = attempt;
5923
+ stall.lastProbeAt = now();
5924
+ rotationProbeArmed.push(key);
5925
+ log(
5926
+ `[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)`
5927
+ );
5817
5928
  }
5818
5929
  if (rotationGraced.length > 0) {
5819
5930
  log(
5820
5931
  `[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)`
5821
5932
  );
5822
5933
  }
5934
+ if (rotationProbeWaiting.length > 0) {
5935
+ log(
5936
+ `[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)`
5937
+ );
5938
+ }
5823
5939
  const declared = mcpJson.mcpServers ?? {};
5824
5940
  const liveKeys = /* @__PURE__ */ new Set();
5825
5941
  for (const key of Object.keys(declared)) {
@@ -5827,6 +5943,24 @@ function reapMissingMcpSessions(args) {
5827
5943
  if (typeof entry?.command !== "string") continue;
5828
5944
  if (!missingRaw.includes(key)) liveKeys.add(key);
5829
5945
  }
5946
+ for (const key of liveKeys) {
5947
+ const sk = stateKey(codeName, key);
5948
+ if (classifyRotation(codeName, key, rotationGraceMs, now) === "none") {
5949
+ rotationStallState.delete(sk);
5950
+ continue;
5951
+ }
5952
+ const stall = rotationStallState.get(sk);
5953
+ const viaProbe = (stall?.probeAttempts ?? 0) > 0;
5954
+ recordRotationOutcome(
5955
+ key,
5956
+ viaProbe ? "respawned-after-prompt-probe" : "respawned-on-tool-call"
5957
+ );
5958
+ log(
5959
+ 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)`
5960
+ );
5961
+ clearStaleRotation(codeName, [key]);
5962
+ rotationStallState.delete(sk);
5963
+ }
5830
5964
  for (const key of liveKeys) {
5831
5965
  const state = presenceReaperState.get(stateKey(codeName, key));
5832
5966
  if (state) {
@@ -5850,7 +5984,11 @@ function reapMissingMcpSessions(args) {
5850
5984
  missing,
5851
5985
  restarted: false,
5852
5986
  reason: "healthy",
5853
- rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0
5987
+ rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
5988
+ rotationProbeArmed: rotationProbeArmed.length > 0 ? rotationProbeArmed : void 0,
5989
+ rotationProbeWaiting: rotationProbeWaiting.length > 0 ? rotationProbeWaiting : void 0,
5990
+ rotationProbeExhausted: rotationProbeExhausted.length > 0 ? rotationProbeExhausted : void 0,
5991
+ rotationRespawnOutcomes: Object.keys(rotationRespawnOutcomes).length > 0 ? rotationRespawnOutcomes : void 0
5854
5992
  };
5855
5993
  }
5856
5994
  const nowMs = now();
@@ -5978,6 +6116,10 @@ function reapMissingMcpSessions(args) {
5978
6116
  givenUp,
5979
6117
  backingOff: backingOff.length > 0 ? backingOff : void 0,
5980
6118
  rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
6119
+ rotationProbeArmed: rotationProbeArmed.length > 0 ? rotationProbeArmed : void 0,
6120
+ rotationProbeWaiting: rotationProbeWaiting.length > 0 ? rotationProbeWaiting : void 0,
6121
+ rotationProbeExhausted: rotationProbeExhausted.length > 0 ? rotationProbeExhausted : void 0,
6122
+ rotationRespawnOutcomes: Object.keys(rotationRespawnOutcomes).length > 0 ? rotationRespawnOutcomes : void 0,
5981
6123
  quarantined: quarantined.length > 0 ? quarantined : void 0,
5982
6124
  wouldQuarantine: wouldQuarantine.length > 0 ? wouldQuarantine : void 0,
5983
6125
  genuinelyDead: genuinelyDead.length > 0 ? genuinelyDead : void 0
@@ -6015,6 +6157,10 @@ function reapMissingMcpSessions(args) {
6015
6157
  givenUp: givenUp.length > 0 ? givenUp : void 0,
6016
6158
  backingOff: backingOff.length > 0 ? backingOff : void 0,
6017
6159
  rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
6160
+ rotationProbeArmed: rotationProbeArmed.length > 0 ? rotationProbeArmed : void 0,
6161
+ rotationProbeWaiting: rotationProbeWaiting.length > 0 ? rotationProbeWaiting : void 0,
6162
+ rotationProbeExhausted: rotationProbeExhausted.length > 0 ? rotationProbeExhausted : void 0,
6163
+ rotationRespawnOutcomes: Object.keys(rotationRespawnOutcomes).length > 0 ? rotationRespawnOutcomes : void 0,
6018
6164
  quarantined: quarantined.length > 0 ? quarantined : void 0,
6019
6165
  wouldQuarantine: wouldQuarantine.length > 0 ? wouldQuarantine : void 0,
6020
6166
  genuinelyDead: genuinelyDead.length > 0 ? genuinelyDead : void 0
@@ -7449,4 +7595,4 @@ export {
7449
7595
  managerInstallSystemUnitCommand,
7450
7596
  managerUninstallSystemUnitCommand
7451
7597
  };
7452
- //# sourceMappingURL=chunk-WCYGJANF.js.map
7598
+ //# sourceMappingURL=chunk-UNDFX52P.js.map