@integrity-labs/agt-cli 0.28.352 → 0.28.353

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-BERN3B42.js";
43
+ } from "../chunk-5MZMXKVD.js";
44
44
  import {
45
45
  AnchorSessionClient,
46
46
  CHANNEL_REGISTRY,
@@ -4829,7 +4829,7 @@ import { execFileSync, execSync } from "child_process";
4829
4829
  import { existsSync as existsSync10, realpathSync as realpathSync2 } from "fs";
4830
4830
  import chalk18 from "chalk";
4831
4831
  import ora16 from "ora";
4832
- var cliVersion = true ? "0.28.352" : "dev";
4832
+ var cliVersion = true ? "0.28.353" : "dev";
4833
4833
  async function fetchLatestVersion() {
4834
4834
  const host2 = getHost();
4835
4835
  if (!host2) return null;
@@ -6001,7 +6001,7 @@ function handleError(err) {
6001
6001
  }
6002
6002
 
6003
6003
  // src/bin/agt.ts
6004
- var cliVersion2 = true ? "0.28.352" : "dev";
6004
+ var cliVersion2 = true ? "0.28.353" : "dev";
6005
6005
  var program = new Command();
6006
6006
  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");
6007
6007
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -7111,7 +7111,7 @@ function requireHost() {
7111
7111
  }
7112
7112
 
7113
7113
  // src/lib/api-client.ts
7114
- var agtCliVersion = true ? "0.28.352" : "dev";
7114
+ var agtCliVersion = true ? "0.28.353" : "dev";
7115
7115
  var lastConfigHash = null;
7116
7116
  function setConfigHash(hash) {
7117
7117
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -9561,4 +9561,4 @@ export {
9561
9561
  managerInstallSystemUnitCommand,
9562
9562
  managerUninstallSystemUnitCommand
9563
9563
  };
9564
- //# sourceMappingURL=chunk-BERN3B42.js.map
9564
+ //# sourceMappingURL=chunk-5MZMXKVD.js.map
@@ -47,7 +47,7 @@ import {
47
47
  safeWriteJsonAtomic,
48
48
  setConfigHash,
49
49
  toOpencodeModel
50
- } from "../chunk-BERN3B42.js";
50
+ } from "../chunk-5MZMXKVD.js";
51
51
  import {
52
52
  getProjectDir as getProjectDir2,
53
53
  getReadyTasks,
@@ -807,6 +807,19 @@ function countByClass(events) {
807
807
  }
808
808
  return { crash, provisioning, "credential-rotation": credentialRotation };
809
809
  }
810
+ function maxProvisioningBucket(events) {
811
+ const buckets = /* @__PURE__ */ new Map();
812
+ for (const e of events) {
813
+ if (!isProvisioningReloadReason(e.reason)) continue;
814
+ const key = e.integrationKey ?? "";
815
+ buckets.set(key, (buckets.get(key) ?? 0) + 1);
816
+ }
817
+ let best = { count: 0, key: "" };
818
+ for (const [key, count] of buckets) {
819
+ if (count > best.count) best = { count, key };
820
+ }
821
+ return best;
822
+ }
810
823
  var DEFAULT_MAX = 2;
811
824
  var DEFAULT_WINDOW_MS = 6e5;
812
825
  var DEFAULT_PROVISIONING_MAX = 5;
@@ -863,20 +876,25 @@ var RestartBreaker = class {
863
876
  * without double-counting events. Callers may still record the reason
864
877
  * via the decision-log for forensics.
865
878
  */
866
- record(codeName, reason) {
879
+ record(codeName, reason, integrationKey) {
867
880
  const existing = this.trips.get(codeName);
868
881
  if (existing) {
882
+ const bucket = maxProvisioningBucket(existing.eventsAtTrip);
869
883
  return {
870
884
  tripped: false,
871
885
  trip: existing,
872
886
  windowCount: existing.eventsAtTrip.length,
873
- classCounts: countByClass(existing.eventsAtTrip)
887
+ classCounts: countByClass(existing.eventsAtTrip),
888
+ maxProvisioningBucket: bucket.count,
889
+ maxProvisioningBucketKey: bucket.key
874
890
  };
875
891
  }
876
892
  const at = this.now();
877
893
  const retentionCutoff = at - this.retentionMs;
878
894
  const prior = (this.events.get(codeName) ?? []).filter((e) => e.at >= retentionCutoff);
879
- prior.push({ reason, at });
895
+ prior.push(
896
+ isProvisioningReloadReason(reason) && integrationKey ? { reason, at, integrationKey } : { reason, at }
897
+ );
880
898
  this.events.set(codeName, prior);
881
899
  const crashCount = prior.filter(
882
900
  (e) => e.at >= at - this.windowMs && restartReasonClass(e.reason) === "crash"
@@ -893,6 +911,7 @@ var RestartBreaker = class {
893
911
  provisioning: provEvents.length,
894
912
  "credential-rotation": credRotCount
895
913
  };
914
+ const provBucket = maxProvisioningBucket(provEvents);
896
915
  let trippedClass;
897
916
  let tripEvents;
898
917
  let tripWindowMs = this.windowMs;
@@ -902,22 +921,36 @@ var RestartBreaker = class {
902
921
  (e) => e.at >= at - this.windowMs && restartReasonClass(e.reason) === "crash"
903
922
  );
904
923
  tripWindowMs = this.windowMs;
905
- } else if (provEvents.length > this.provisioningMax) {
924
+ } else if (provBucket.count > this.provisioningMax) {
906
925
  trippedClass = "provisioning";
907
- tripEvents = provEvents;
926
+ tripEvents = provEvents.filter((e) => (e.integrationKey ?? "") === provBucket.key);
908
927
  tripWindowMs = this.provisioningWindowMs;
909
928
  }
910
929
  if (trippedClass && tripEvents) {
911
930
  const trip = {
912
931
  trippedAt: at,
913
932
  eventsAtTrip: [...tripEvents],
914
- statusMessage: formatStatusMessage(tripEvents, tripWindowMs, trippedClass)
933
+ statusMessage: formatStatusMessage(tripEvents, tripWindowMs, trippedClass, provBucket.key)
915
934
  };
916
935
  this.trips.set(codeName, trip);
917
936
  this.events.delete(codeName);
918
- return { tripped: true, trip, windowCount, classCounts, trippedClass };
937
+ return {
938
+ tripped: true,
939
+ trip,
940
+ windowCount,
941
+ classCounts,
942
+ trippedClass,
943
+ maxProvisioningBucket: provBucket.count,
944
+ maxProvisioningBucketKey: provBucket.key
945
+ };
919
946
  }
920
- return { tripped: false, windowCount, classCounts };
947
+ return {
948
+ tripped: false,
949
+ windowCount,
950
+ classCounts,
951
+ maxProvisioningBucket: provBucket.count,
952
+ maxProvisioningBucketKey: provBucket.key
953
+ };
921
954
  }
922
955
  /** Operator-initiated reset: drops the trip + the events log for this agent. */
923
956
  clear(codeName) {
@@ -948,14 +981,15 @@ var RestartBreaker = class {
948
981
  return (this.events.get(codeName) ?? []).filter((e) => e.at >= cutoff).length;
949
982
  }
950
983
  };
951
- function formatStatusMessage(events, windowMs, klass = "crash") {
984
+ function formatStatusMessage(events, windowMs, klass = "crash", integrationKey = "") {
952
985
  const last = events[events.length - 1];
953
986
  const windowLabel = windowMs < 6e4 ? `${Math.round(windowMs / 1e3)}s` : `${(windowMs / 6e4).toFixed(1).replace(/\.0$/, "")}min`;
954
987
  const reasonCounts = /* @__PURE__ */ new Map();
955
988
  for (const e of events) reasonCounts.set(e.reason, (reasonCounts.get(e.reason) ?? 0) + 1);
956
989
  const breakdown = Array.from(reasonCounts.entries()).map(([r, n]) => `${r}=${n}`).join(", ");
957
990
  const classLabel = klass === "provisioning" ? "provisioning-reload " : "";
958
- return `Circuit breaker tripped: ${events.length} ${classLabel}restarts in ${windowLabel} (${breakdown}); most recent=${last.reason} at ${new Date(last.at).toISOString()}`;
991
+ const integrationLabel = klass === "provisioning" && integrationKey ? ` for integration '${integrationKey}'` : "";
992
+ return `Circuit breaker tripped: ${events.length} ${classLabel}restarts in ${windowLabel}${integrationLabel} (${breakdown}); most recent=${last.reason} at ${new Date(last.at).toISOString()}`;
959
993
  }
960
994
 
961
995
  // src/lib/mcp-flap-dampener.ts
@@ -3769,6 +3803,17 @@ var agentState = {
3769
3803
  knownModels: /* @__PURE__ */ new Map(),
3770
3804
  knownTasksHashes: /* @__PURE__ */ new Map(),
3771
3805
  knownIntegrationHashes: /* @__PURE__ */ new Map(),
3806
+ /**
3807
+ * ENG-7812: the integration MEMBERSHIP (sorted `definition_id`s, credential/
3808
+ * config churn ignored) observed on the previous refresh. Diffed against the
3809
+ * current membership each poll to derive which integration(s) an
3810
+ * `integration-change` / `hot-reload-mcp` restart is attributable to, which the
3811
+ * restart breaker buckets its provisioning trip by (so onboarding many distinct
3812
+ * integrations doesn't auto-pause a healthy new agent). In-memory only: a
3813
+ * manager restart re-seeds it on the first poll (that first delta is treated as
3814
+ * "no attribution", which is safe: it only widens, never narrows, the trip).
3815
+ */
3816
+ knownIntegrationMembership: /* @__PURE__ */ new Map(),
3772
3817
  knownManagedMcpHashes: /* @__PURE__ */ new Map(),
3773
3818
  knownManagedMcpStructure: /* @__PURE__ */ new Map(),
3774
3819
  /**
@@ -3804,6 +3849,16 @@ var agentState = {
3804
3849
  // ---------------------------------------------------------------------------
3805
3850
  agentDisplayNames: /* @__PURE__ */ new Map(),
3806
3851
  codeNameToAgentId: /* @__PURE__ */ new Map(),
3852
+ /**
3853
+ * ENG-7812: the most recent integration-membership delta for this agent, used
3854
+ * to attribute a provisioning-reload restart to the integration that caused it.
3855
+ * Set when a poll observes an integration add/remove (the changed
3856
+ * `definition_id`s, `+`-joined); read by `recordRestartForBreaker` when a
3857
+ * provisioning restart fires shortly after. `at` bounds its freshness to the
3858
+ * breaker's provisioning window so a stale delta can't mis-attribute a much
3859
+ * later reprovision (those fall through to the shared unattributed bucket).
3860
+ */
3861
+ provisioningAttribution: /* @__PURE__ */ new Map(),
3807
3862
  /**
3808
3863
  * Set of code_names with a live persistent session managed by this worker.
3809
3864
  * Borderline ownership: written from pollCycle and the persistent-session
@@ -3841,6 +3896,7 @@ function clearAgentState(agentId, codeName) {
3841
3896
  agentState.knownModels.delete(agentId);
3842
3897
  agentState.knownTasksHashes.delete(agentId);
3843
3898
  agentState.knownIntegrationHashes.delete(agentId);
3899
+ agentState.knownIntegrationMembership.delete(agentId);
3844
3900
  agentState.knownManagedMcpHashes.delete(agentId);
3845
3901
  agentState.knownManagedMcpStructure.delete(agentId);
3846
3902
  agentState.knownSenderPolicyHashes.delete(agentId);
@@ -3849,6 +3905,7 @@ function clearAgentState(agentId, codeName) {
3849
3905
  agentState.knownGlobalSkillIds.delete(agentId);
3850
3906
  agentState.agentDisplayNames.delete(codeName);
3851
3907
  agentState.codeNameToAgentId.delete(codeName);
3908
+ agentState.provisioningAttribution.delete(codeName);
3852
3909
  let channelCacheMutated = false;
3853
3910
  for (const key of agentState.knownChannelConfigHashes.keys()) {
3854
3911
  if (key.startsWith(`${agentId}:`)) {
@@ -8472,6 +8529,10 @@ var pendingRestartVerifications = /* @__PURE__ */ new Map();
8472
8529
  var restartBreaker = new RestartBreaker();
8473
8530
  var reportedTrips = /* @__PURE__ */ new Map();
8474
8531
  var RESTART_BREAKER_PROVISIONING_MAX = readEnvNumber("AGT_RESTART_BREAKER_PROVISIONING_MAX", 5);
8532
+ var RESTART_BREAKER_PROVISIONING_WINDOW_MS = readEnvNumber(
8533
+ "AGT_RESTART_BREAKER_PROVISIONING_WINDOW_MS",
8534
+ 18e5
8535
+ );
8475
8536
  var mcpFlapDampener = new McpFlapDampener();
8476
8537
  var FLAP_BREAKER_COOLDOWN_MS = readEnvNumber2(
8477
8538
  "AGT_MCP_FLAP_BREAKER_COOLDOWN_MS",
@@ -8510,11 +8571,19 @@ function recordRestartForBreaker(codeName, reason) {
8510
8571
  );
8511
8572
  return;
8512
8573
  }
8513
- const result = restartBreaker.record(codeName, reason);
8574
+ let integrationKey;
8575
+ if (isProvisioningReloadReason(reason)) {
8576
+ const attribution = agentState.provisioningAttribution.get(codeName);
8577
+ if (attribution && Date.now() - attribution.at <= RESTART_BREAKER_PROVISIONING_WINDOW_MS) {
8578
+ integrationKey = attribution.key;
8579
+ }
8580
+ }
8581
+ const result = restartBreaker.record(codeName, reason, integrationKey);
8514
8582
  if (!result.tripped || !result.trip) {
8515
- if (isProvisioningReloadReason(reason) && result.classCounts.provisioning >= RESTART_BREAKER_PROVISIONING_MAX) {
8583
+ if (isProvisioningReloadReason(reason) && result.maxProvisioningBucket >= RESTART_BREAKER_PROVISIONING_MAX) {
8584
+ const culprit = result.maxProvisioningBucketKey ? ` [${result.maxProvisioningBucketKey}]` : "";
8516
8585
  log(
8517
- `[restart-breaker] agent=${codeName} provisioning-reload churn at the trip bar: ${result.classCounts.provisioning} provisioning restarts in window (reason=${reason}); one more will auto-pause the agent \u2014 a freshly-added integration is likely churning (ENG-7560)`
8586
+ `[restart-breaker] agent=${codeName} provisioning-reload churn at the trip bar: ${result.maxProvisioningBucket} restarts for one integration${culprit} in window (reason=${reason}); one more will auto-pause the agent; that integration is likely churning (ENG-7560, ENG-7812)`
8518
8587
  );
8519
8588
  }
8520
8589
  return;
@@ -9342,7 +9411,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9342
9411
  var lastVersionCheckAt = 0;
9343
9412
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9344
9413
  var lastResponsivenessProbeAt = 0;
9345
- var agtCliVersion = true ? "0.28.352" : "dev";
9414
+ var agtCliVersion = true ? "0.28.353" : "dev";
9346
9415
  function resolveBrewPath(execFileSync2) {
9347
9416
  try {
9348
9417
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -12231,6 +12300,25 @@ async function processAgent(agent, agentStates) {
12231
12300
  }
12232
12301
  }
12233
12302
  }
12303
+ const currentIntIds = integrations.map((i) => i.definition_id);
12304
+ const prevIntIds = agentState.knownIntegrationMembership.get(agent.agent_id) ?? [];
12305
+ const prevIntSet = new Set(prevIntIds);
12306
+ const curIntSet = new Set(currentIntIds);
12307
+ const changedIntIds = Array.from(
12308
+ /* @__PURE__ */ new Set([
12309
+ ...currentIntIds.filter((id) => !prevIntSet.has(id)),
12310
+ // added
12311
+ ...prevIntIds.filter((id) => !curIntSet.has(id))
12312
+ // removed
12313
+ ])
12314
+ ).sort();
12315
+ if (changedIntIds.length > 0) {
12316
+ agentState.provisioningAttribution.set(agent.code_name, {
12317
+ key: changedIntIds.join("+"),
12318
+ at: Date.now()
12319
+ });
12320
+ }
12321
+ agentState.knownIntegrationMembership.set(agent.agent_id, currentIntIds);
12234
12322
  if (integrations.length > 0) {
12235
12323
  const intHash = computeIntegrationsHash(integrations);
12236
12324
  const prevIntHash = agentState.knownIntegrationHashes.get(agent.agent_id);