@integrity-labs/agt-cli 0.28.758 → 0.28.759

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
@@ -41,7 +41,7 @@ import {
41
41
  success,
42
42
  table,
43
43
  warn
44
- } from "../chunk-ZUCWB2AP.js";
44
+ } from "../chunk-YAONHJW6.js";
45
45
  import {
46
46
  getProjectDir,
47
47
  isSessionResumeDisabled,
@@ -5467,7 +5467,7 @@ import { execFileSync, execSync } from "child_process";
5467
5467
  import { existsSync as existsSync11, realpathSync as realpathSync2 } from "fs";
5468
5468
  import chalk18 from "chalk";
5469
5469
  import ora16 from "ora";
5470
- var cliVersion = true ? "0.28.758" : "dev";
5470
+ var cliVersion = true ? "0.28.759" : "dev";
5471
5471
  async function fetchLatestVersion() {
5472
5472
  const host2 = getHost();
5473
5473
  if (!host2) return null;
@@ -6658,7 +6658,7 @@ function handleError(err) {
6658
6658
  }
6659
6659
 
6660
6660
  // src/bin/agt.ts
6661
- var cliVersion2 = true ? "0.28.758" : "dev";
6661
+ var cliVersion2 = true ? "0.28.759" : "dev";
6662
6662
  var program = new Command();
6663
6663
  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");
6664
6664
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -6436,7 +6436,7 @@ function exchangeFailureKind(err) {
6436
6436
  }
6437
6437
 
6438
6438
  // src/lib/api-client.ts
6439
- var agtCliVersion = true ? "0.28.758" : "dev";
6439
+ var agtCliVersion = true ? "0.28.759" : "dev";
6440
6440
  var lastConfigHash = null;
6441
6441
  function setConfigHash(hash) {
6442
6442
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -10593,4 +10593,4 @@ export {
10593
10593
  managerInstallSystemUnitCommand,
10594
10594
  managerUninstallSystemUnitCommand
10595
10595
  };
10596
- //# sourceMappingURL=chunk-ZUCWB2AP.js.map
10596
+ //# sourceMappingURL=chunk-YAONHJW6.js.map
@@ -58,7 +58,7 @@ import {
58
58
  safeWriteJsonAtomic,
59
59
  setConfigHash,
60
60
  tripClass
61
- } from "../chunk-ZUCWB2AP.js";
61
+ } from "../chunk-YAONHJW6.js";
62
62
  import {
63
63
  getProjectDir as getProjectDir2,
64
64
  getReadyTasks,
@@ -393,6 +393,7 @@ function decideManagedMcpRestart(input) {
393
393
  const { prevStructureHash, structureHash, launchStructureHash, sessionHealthy, flapping } = input;
394
394
  if (prevStructureHash === void 0) return { kind: "skip-first-convergence" };
395
395
  if (structureHash === prevStructureHash) return { kind: "skip-no-change" };
396
+ if (input.deferringPrune === true) return { kind: "skip-prune-deferred" };
396
397
  if (!sessionHealthy) return { kind: "skip-unhealthy" };
397
398
  if (flapping) return { kind: "skip-flapping" };
398
399
  if (launchStructureHash != null && launchStructureHash === structureHash) {
@@ -6451,6 +6452,75 @@ function withScheduleLinkFooter(opts) {
6451
6452
  return appendScheduleLinkFooter(opts.body, url, opts.medium);
6452
6453
  }
6453
6454
 
6455
+ // src/lib/manager/managed-prune-streak.ts
6456
+ var DEFAULT_PRUNE_CONFIRM_CYCLES = 5;
6457
+ function pruneConfirmCycles(env = process.env) {
6458
+ const raw = env["AUGMENTED_MANAGED_PRUNE_CONFIRM_CYCLES"];
6459
+ if (raw === void 0) return DEFAULT_PRUNE_CONFIRM_CYCLES;
6460
+ const trimmed = raw.trim();
6461
+ if (!/^\d+$/.test(trimmed)) return DEFAULT_PRUNE_CONFIRM_CYCLES;
6462
+ const n = Number(trimmed);
6463
+ if (!Number.isSafeInteger(n) || n < 1) return DEFAULT_PRUNE_CONFIRM_CYCLES;
6464
+ return n;
6465
+ }
6466
+ var ManagedPruneStreaks = class {
6467
+ constructor(threshold = DEFAULT_PRUNE_CONFIRM_CYCLES) {
6468
+ this.threshold = threshold;
6469
+ }
6470
+ streaks = /* @__PURE__ */ new Map();
6471
+ /**
6472
+ * Record one poll cycle for one agent.
6473
+ *
6474
+ * MUST BE CALLED ON EVERY CYCLE, including cycles where nothing changed —
6475
+ * see the trap above. `declaredManagedKeys` is what the agent's config
6476
+ * currently declares; `expectedKeys` is what the API says it should have.
6477
+ *
6478
+ * A key present in `expectedKeys` has its streak cleared IMMEDIATELY, which is
6479
+ * what makes a toolkit's reappearance undo the wait rather than merely pause
6480
+ * it.
6481
+ *
6482
+ * Returns a decision per absent key, in the order given, so the caller can log
6483
+ * the deferrals as well as act on the matured ones.
6484
+ */
6485
+ observe(agentId, declaredManagedKeys, expectedKeys) {
6486
+ let perAgent = this.streaks.get(agentId);
6487
+ if (perAgent) {
6488
+ for (const key of expectedKeys) perAgent.delete(key);
6489
+ }
6490
+ const decisions = [];
6491
+ for (const key of declaredManagedKeys) {
6492
+ if (expectedKeys.has(key)) continue;
6493
+ if (!perAgent) {
6494
+ perAgent = /* @__PURE__ */ new Map();
6495
+ this.streaks.set(agentId, perAgent);
6496
+ }
6497
+ const streak = (perAgent.get(key) ?? 0) + 1;
6498
+ perAgent.set(key, streak);
6499
+ decisions.push({ serverKey: key, streak, mature: streak >= this.threshold });
6500
+ }
6501
+ if (perAgent) {
6502
+ const declared = new Set(declaredManagedKeys);
6503
+ for (const key of [...perAgent.keys()]) {
6504
+ if (!declared.has(key)) perAgent.delete(key);
6505
+ }
6506
+ if (perAgent.size === 0) this.streaks.delete(agentId);
6507
+ }
6508
+ return decisions;
6509
+ }
6510
+ /** Current streak for one key. Zero when untracked. */
6511
+ streakFor(agentId, serverKey) {
6512
+ return this.streaks.get(agentId)?.get(serverKey) ?? 0;
6513
+ }
6514
+ /** Drop everything for an agent — used when an agent leaves the host. */
6515
+ forget(agentId) {
6516
+ this.streaks.delete(agentId);
6517
+ }
6518
+ /** Number of agents currently carrying at least one streak (tests/diagnostics). */
6519
+ get trackedAgents() {
6520
+ return this.streaks.size;
6521
+ }
6522
+ };
6523
+
6454
6524
  // src/lib/manager/agent-state.ts
6455
6525
  var agentState = {
6456
6526
  // ---------------------------------------------------------------------------
@@ -6558,7 +6628,19 @@ var agentState = {
6558
6628
  * it, so an unpublish during a restart window leaves the folder until the next
6559
6629
  * unpublish — acceptable for a platform-staff-rare action.
6560
6630
  */
6561
- knownGlobalSkillIds: /* @__PURE__ */ new Map()
6631
+ knownGlobalSkillIds: /* @__PURE__ */ new Map(),
6632
+ /**
6633
+ * ENG-9708: consecutive cycles each managed MCP server key has been ABSENT
6634
+ * from /host/managed-toolkits, per agent. A key must miss N consecutive polls
6635
+ * before it is pruned, so one degraded-but-well-formed response can no longer
6636
+ * destroy fleet state that does not self-heal (2026-08-31: 24 composio_*
6637
+ * servers across 6+ agents, removed in under two minutes, recovered only by a
6638
+ * manual bulk-reconnect).
6639
+ *
6640
+ * In-memory on purpose: a manager restart clears every streak, which delays a
6641
+ * legitimate prune by a few minutes and can never cause a spurious one.
6642
+ */
6643
+ managedPruneStreaks: new ManagedPruneStreaks(pruneConfirmCycles())
6562
6644
  };
6563
6645
  function clearAgentState(agentId, codeName) {
6564
6646
  agentState.knownVersions.delete(agentId);
@@ -6577,6 +6659,7 @@ function clearAgentState(agentId, codeName) {
6577
6659
  agentState.knownMsTeamsBehaviourHashes.delete(agentId);
6578
6660
  agentState.knownSlackBehaviourHashes.delete(agentId);
6579
6661
  agentState.knownGlobalSkillIds.delete(agentId);
6662
+ agentState.managedPruneStreaks.forget(agentId);
6580
6663
  agentState.agentDisplayNames.delete(codeName);
6581
6664
  agentState.codeNameToAgentId.delete(codeName);
6582
6665
  agentState.provisioningAttribution.delete(codeName);
@@ -13385,7 +13468,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
13385
13468
  var lastVersionCheckAt = 0;
13386
13469
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
13387
13470
  var lastResponsivenessProbeAt = 0;
13388
- var agtCliVersion = true ? "0.28.758" : "dev";
13471
+ var agtCliVersion = true ? "0.28.759" : "dev";
13389
13472
  function resolveBrewPath(execFileSync3) {
13390
13473
  try {
13391
13474
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -17532,32 +17615,59 @@ async function processAgent(agent, agentStates, managedToolkits) {
17532
17615
  );
17533
17616
  recordConfigChurnEvent(agent.agent_id, agent.code_name, FLAP_CHANNEL_MANAGED_MCP, mcpMembership);
17534
17617
  }
17618
+ const deferredPruneKeys = [];
17619
+ if (frameworkAdapter.removeMcpServer && frameworkAdapter.readMcpServers) {
17620
+ try {
17621
+ const declaredManaged = Object.keys(
17622
+ frameworkAdapter.readMcpServers(agent.code_name)
17623
+ ).filter(isManagedMcpServerKey);
17624
+ const decisions = agentState.managedPruneStreaks.observe(
17625
+ agent.agent_id,
17626
+ declaredManaged,
17627
+ expectedServerIds
17628
+ );
17629
+ for (const d of decisions) {
17630
+ if (!d.mature) {
17631
+ deferredPruneKeys.push(d.serverKey);
17632
+ log(
17633
+ `[managed-toolkit] ${agent.code_name}: '${d.serverKey}' absent for ${d.streak}/${pruneConfirmCycles()} cycles \u2014 deferring prune (ENG-9708)`
17634
+ );
17635
+ continue;
17636
+ }
17637
+ frameworkAdapter.removeMcpServer(agent.code_name, d.serverKey);
17638
+ log(
17639
+ `[managed-toolkit] Removed stale MCP server '${d.serverKey}' for '${agent.code_name}' after ${d.streak} consecutive absences (ENG-9708)`
17640
+ );
17641
+ managedPruneLedger.record(agent.code_name, d.serverKey);
17642
+ }
17643
+ } catch {
17644
+ }
17645
+ }
17535
17646
  if (mcpHash !== prevMcpHash) {
17536
17647
  for (const e of desiredEntries) {
17537
17648
  frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
17538
17649
  const urlHash = createHash20("sha256").update(e.url).digest("hex").slice(0, 12);
17539
17650
  log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
17540
17651
  }
17541
- if (frameworkAdapter.removeMcpServer && frameworkAdapter.readMcpServers) {
17542
- try {
17543
- for (const key of Object.keys(frameworkAdapter.readMcpServers(agent.code_name))) {
17544
- if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
17545
- frameworkAdapter.removeMcpServer(agent.code_name, key);
17546
- log(`[managed-toolkit] Removed stale MCP server '${key}' for '${agent.code_name}'`);
17547
- managedPruneLedger.record(agent.code_name, key);
17548
- }
17549
- }
17550
- } catch {
17551
- }
17652
+ if (deferredPruneKeys.length === 0) {
17653
+ agentState.knownManagedMcpHashes.set(agent.agent_id, mcpHash);
17654
+ agentState.knownManagedMcpStructure.set(agent.agent_id, structureHash);
17655
+ } else {
17656
+ log(
17657
+ `[managed-toolkit] ${agent.code_name}: holding the managed-MCP baseline and session restart while ${deferredPruneKeys.length} prune(s) await confirmation (${deferredPruneKeys.join(", ")}) \u2014 ENG-9708`
17658
+ );
17552
17659
  }
17553
- agentState.knownManagedMcpHashes.set(agent.agent_id, mcpHash);
17554
- agentState.knownManagedMcpStructure.set(agent.agent_id, structureHash);
17555
17660
  const mcpRestartDecision = fwForMcp === "claude-code" && prevStructureHash !== void 0 && structureHash !== prevStructureHash ? decideManagedMcpRestart({
17556
17661
  prevStructureHash,
17557
17662
  structureHash,
17558
17663
  launchStructureHash: sessionLaunchManagedStructure.get(agent.code_name),
17559
17664
  sessionHealthy: isSessionHealthy(agent.code_name),
17560
- flapping: mcpFlap.flapping
17665
+ flapping: mcpFlap.flapping,
17666
+ // ENG-9708: an unconfirmed prune holds the restart too. The
17667
+ // structural change it would announce ("servers were removed")
17668
+ // has not been applied to the file yet, so restarting now
17669
+ // would churn the session and tell the agent something untrue.
17670
+ deferringPrune: deferredPruneKeys.length > 0
17561
17671
  }) : null;
17562
17672
  if (mcpRestartDecision?.kind === "suppress-already-live") {
17563
17673
  log(