@integrity-labs/agt-cli 0.28.215 → 0.28.216

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
@@ -38,7 +38,7 @@ import {
38
38
  success,
39
39
  table,
40
40
  warn
41
- } from "../chunk-4QCJLG3Q.js";
41
+ } from "../chunk-6DMZIXM6.js";
42
42
  import {
43
43
  CHANNEL_REGISTRY,
44
44
  DEFAULT_FRAMEWORK,
@@ -4826,7 +4826,7 @@ import { execFileSync, execSync } from "child_process";
4826
4826
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4827
4827
  import chalk18 from "chalk";
4828
4828
  import ora16 from "ora";
4829
- var cliVersion = true ? "0.28.215" : "dev";
4829
+ var cliVersion = true ? "0.28.216" : "dev";
4830
4830
  async function fetchLatestVersion() {
4831
4831
  const host2 = getHost();
4832
4832
  if (!host2) return null;
@@ -5840,7 +5840,7 @@ function handleError(err) {
5840
5840
  }
5841
5841
 
5842
5842
  // src/bin/agt.ts
5843
- var cliVersion2 = true ? "0.28.215" : "dev";
5843
+ var cliVersion2 = true ? "0.28.216" : "dev";
5844
5844
  var program = new Command();
5845
5845
  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");
5846
5846
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -5529,7 +5529,7 @@ function requireHost() {
5529
5529
  }
5530
5530
 
5531
5531
  // src/lib/api-client.ts
5532
- var agtCliVersion = true ? "0.28.215" : "dev";
5532
+ var agtCliVersion = true ? "0.28.216" : "dev";
5533
5533
  var lastConfigHash = null;
5534
5534
  function setConfigHash(hash) {
5535
5535
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -7581,4 +7581,4 @@ export {
7581
7581
  managerInstallSystemUnitCommand,
7582
7582
  managerUninstallSystemUnitCommand
7583
7583
  };
7584
- //# sourceMappingURL=chunk-4QCJLG3Q.js.map
7584
+ //# sourceMappingURL=chunk-6DMZIXM6.js.map
@@ -36,7 +36,7 @@ import {
36
36
  requireHost,
37
37
  safeWriteJsonAtomic,
38
38
  setConfigHash
39
- } from "../chunk-4QCJLG3Q.js";
39
+ } from "../chunk-6DMZIXM6.js";
40
40
  import {
41
41
  getProjectDir as getProjectDir2,
42
42
  getReadyTasks,
@@ -685,6 +685,11 @@ var McpFlapDampener = class {
685
685
  now;
686
686
  history = /* @__PURE__ */ new Map();
687
687
  flapping = /* @__PURE__ */ new Set();
688
+ // ENG-7239: timestamp of the most recent poll on which each (agent, channel)
689
+ // was flapping. Drives `flappedWithin` so the caller can exempt a managed-MCP
690
+ // churn restart from the ENG-5441 breaker for a short cool-off after a flap;
691
+ // see that method for the Kylie/dig (2026-06-29) rationale.
692
+ lastFlapAt = /* @__PURE__ */ new Map();
688
693
  constructor(opts = {}) {
689
694
  this.windowMs = opts.windowMs ?? readEnvNumber2("AGT_MCP_FLAP_WINDOW_MS", DEFAULT_WINDOW_MS2);
690
695
  this.minTransitions = opts.minTransitions ?? readEnvNumber2("AGT_MCP_FLAP_MIN_TRANSITIONS", DEFAULT_MIN_TRANSITIONS);
@@ -742,9 +747,11 @@ var McpFlapDampener = class {
742
747
  this.flapping.add(k);
743
748
  onset = true;
744
749
  }
750
+ const nowFlapping = this.flapping.has(k);
751
+ if (nowFlapping) this.lastFlapAt.set(k, at);
745
752
  const reported = tier === "slow" ? slow : fast;
746
753
  return {
747
- flapping: this.flapping.has(k),
754
+ flapping: nowFlapping,
748
755
  distinctStates: reported.distinctStates,
749
756
  transitions: reported.transitions,
750
757
  windowCount: prior.length,
@@ -776,6 +783,27 @@ var McpFlapDampener = class {
776
783
  isFlapping(codeName, channel) {
777
784
  return this.flapping.has(this.key(codeName, channel));
778
785
  }
786
+ /**
787
+ * ENG-7239: true if (codeName, channel) was flapping within the last
788
+ * `withinMs`. Lets the caller exempt a managed-MCP-churn session restart from
789
+ * the ENG-5441 circuit breaker during (and for a short cool-off after) a
790
+ * flap episode.
791
+ *
792
+ * Why a cool-off and not just `isFlapping`: the dampener recovers eagerly, on
793
+ * the first stable poll (ENG-6512), so during an ACTIVE oscillation a single
794
+ * transient poll where the desired set momentarily repeats clears `flapping`
795
+ * for that instant. If the very next poll flips again, that flip's restart
796
+ * would (pre-fix) be treated as a genuine change and counted toward the
797
+ * breaker; three such flips in 10 min auto-paused Kylie (org dig) on
798
+ * 2026-06-29 and left her unreachable on every channel. Keying the breaker
799
+ * exemption off "flapped within the cool-off" instead of the exact-instant
800
+ * `isFlapping` closes that race. Mirrors ENG-5547's single-dead-MCP reaper
801
+ * exemption ("restart, but don't count toward the agent-wide breaker").
802
+ */
803
+ flappedWithin(codeName, channel, withinMs) {
804
+ const at = this.lastFlapAt.get(this.key(codeName, channel));
805
+ return at !== void 0 && this.now() - at <= withinMs;
806
+ }
779
807
  /** Drop all state for an agent (called on deprovision / agent removal). */
780
808
  forget(codeName) {
781
809
  const prefix = `${codeName}\0`;
@@ -785,10 +813,29 @@ var McpFlapDampener = class {
785
813
  for (const k of [...this.flapping]) {
786
814
  if (k.startsWith(prefix)) this.flapping.delete(k);
787
815
  }
816
+ for (const k of [...this.lastFlapAt.keys()]) {
817
+ if (k.startsWith(prefix)) this.lastFlapAt.delete(k);
818
+ }
788
819
  }
789
820
  };
790
821
  var FLAP_CHANNEL_INTEGRATIONS = "integrations";
791
822
  var FLAP_CHANNEL_MANAGED_MCP = "managed-mcp";
823
+ var DEFAULT_FLAP_BREAKER_COOLDOWN_MS = 6e5;
824
+ var MANAGED_MCP_SERVER_PREFIXES = [
825
+ "composio_",
826
+ "one_",
827
+ "pipedream_",
828
+ "nango_",
829
+ "paragon_",
830
+ "composio-",
831
+ "one-",
832
+ "pipedream-",
833
+ "nango-",
834
+ "paragon-"
835
+ ];
836
+ function isManagedMcpServerKey(key) {
837
+ return MANAGED_MCP_SERVER_PREFIXES.some((p) => key.startsWith(p));
838
+ }
792
839
 
793
840
  // src/lib/auto-resume.ts
794
841
  var DEFAULT_QUIET_MS = 18e5;
@@ -5469,6 +5516,10 @@ var pendingRestartVerifications = /* @__PURE__ */ new Map();
5469
5516
  var restartBreaker = new RestartBreaker();
5470
5517
  var reportedTrips = /* @__PURE__ */ new Map();
5471
5518
  var mcpFlapDampener = new McpFlapDampener();
5519
+ var FLAP_BREAKER_COOLDOWN_MS = readEnvNumber2(
5520
+ "AGT_MCP_FLAP_BREAKER_COOLDOWN_MS",
5521
+ DEFAULT_FLAP_BREAKER_COOLDOWN_MS
5522
+ );
5472
5523
  function recordConfigChurnEvent(agentId, codeName, channel, signature) {
5473
5524
  api.post("/host/mcp-config-churn", {
5474
5525
  agent_id: agentId,
@@ -5496,6 +5547,12 @@ async function reportReaperEvent(agentId, reaper, action, reason) {
5496
5547
  }
5497
5548
  }
5498
5549
  function recordRestartForBreaker(codeName, reason) {
5550
+ if (reason === "managed-mcp-churn" && mcpFlapDampener.flappedWithin(codeName, FLAP_CHANNEL_MANAGED_MCP, FLAP_BREAKER_COOLDOWN_MS)) {
5551
+ log(
5552
+ `[mcp-flap-dampener] '${codeName}' managed-MCP churn restart NOT counted toward circuit breaker, managed-MCP set flapped within ${Math.round(FLAP_BREAKER_COOLDOWN_MS / 1e3)}s (ENG-7239)`
5553
+ );
5554
+ return;
5555
+ }
5499
5556
  const result = restartBreaker.record(codeName, reason);
5500
5557
  if (!result.tripped || !result.trip) return;
5501
5558
  const trip = result.trip;
@@ -5752,7 +5809,7 @@ function scheduleSessionRestart(codeName, delayMs, reason, breakerReason = "hot-
5752
5809
  runningMcpHashes.delete(codeName);
5753
5810
  recordRestartForBreaker(codeName, breakerReason);
5754
5811
  log(`[hot-reload] Session stopped for '${codeName}' \u2014 will respawn with ${reason}`);
5755
- if (breakerReason === "hot-reload-mcp" || breakerReason === "bind-remediation" || breakerReason === "channel-set-change") {
5812
+ if (breakerReason === "hot-reload-mcp" || breakerReason === "managed-mcp-churn" || breakerReason === "bind-remediation" || breakerReason === "channel-set-change") {
5756
5813
  const prior = pendingRestartVerifications.get(codeName);
5757
5814
  pendingRestartVerifications.set(codeName, {
5758
5815
  firedAt: Date.now(),
@@ -6064,7 +6121,8 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
6064
6121
  );
6065
6122
  }
6066
6123
  if (decision.restart && !quarantineOnlyDrift && !flapSuppressed) {
6067
- scheduleSessionRestart(codeName, 0, ".mcp.json content change (ENG-4897)");
6124
+ const driftBreakerReason = decision.addedOrRemoved.length > 0 && decision.addedOrRemoved.every(isManagedMcpServerKey) ? "managed-mcp-churn" : "hot-reload-mcp";
6125
+ scheduleSessionRestart(codeName, 0, ".mcp.json content change (ENG-4897)", driftBreakerReason);
6068
6126
  runningMcpHashes.delete(codeName);
6069
6127
  runningMcpServerKeys.delete(codeName);
6070
6128
  } else if (flapSuppressed) {
@@ -6163,7 +6221,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
6163
6221
  var lastVersionCheckAt = 0;
6164
6222
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
6165
6223
  var lastResponsivenessProbeAt = 0;
6166
- var agtCliVersion = true ? "0.28.215" : "dev";
6224
+ var agtCliVersion = true ? "0.28.216" : "dev";
6167
6225
  function resolveBrewPath(execFileSync2) {
6168
6226
  try {
6169
6227
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -8750,20 +8808,8 @@ async function processAgent(agent, agentStates) {
8750
8808
  const { readFileSync: readFileSync15 } = await import("fs");
8751
8809
  const mcpConfig = JSON.parse(readFileSync15(mcpPath, "utf-8"));
8752
8810
  if (mcpConfig.mcpServers) {
8753
- const managedPrefixes = [
8754
- "composio_",
8755
- "one_",
8756
- "pipedream_",
8757
- "nango_",
8758
- "paragon_",
8759
- "composio-",
8760
- "one-",
8761
- "pipedream-",
8762
- "nango-",
8763
- "paragon-"
8764
- ];
8765
8811
  for (const key of Object.keys(mcpConfig.mcpServers)) {
8766
- if (managedPrefixes.some((p) => key.startsWith(p)) && !expectedServerIds.has(key)) {
8812
+ if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
8767
8813
  frameworkAdapter.removeMcpServer(agent.code_name, key);
8768
8814
  log(`[managed-toolkit] Removed stale MCP server '${key}' for '${agent.code_name}'`);
8769
8815
  }