@integrity-labs/agt-cli 0.28.408 → 0.28.410

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.
@@ -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-WYGZTU4O.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-W5QSIM7F.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-WWTKA7QC.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-XGPENQB7.js.map
@@ -45,7 +45,7 @@ import {
45
45
  resolveEffectivePinRaw,
46
46
  safeWriteJsonAtomic,
47
47
  setConfigHash
48
- } from "../chunk-DRWBCTED.js";
48
+ } from "../chunk-QZEA7FWB.js";
49
49
  import {
50
50
  getProjectDir as getProjectDir2,
51
51
  getReadyTasks,
@@ -147,7 +147,7 @@ import {
147
147
  toOpencodeModel,
148
148
  transcriptActivityAgeSeconds,
149
149
  writeEgressAllowlist
150
- } from "../chunk-G7M7OFCG.js";
150
+ } from "../chunk-YLLK4G3B.js";
151
151
  import {
152
152
  reapOrphanChannelMcps
153
153
  } from "../chunk-XWVM4KPK.js";
@@ -1207,7 +1207,7 @@ function isManagedMcpServerKey(key) {
1207
1207
  function isRemoteMcpEntry(value) {
1208
1208
  if (!value || typeof value !== "object") return false;
1209
1209
  const e = value;
1210
- return e.type === "http" || e.type === "sse" || typeof e.url === "string" && e.command === void 0;
1210
+ return e.type === "http" || e.type === "sse" || e.type === "remote" || typeof e.url === "string" && e.command === void 0;
1211
1211
  }
1212
1212
  function orphanedRemoteMcpKeys(mcpServers, expectedIntegrationKeys) {
1213
1213
  const orphans = [];
@@ -1588,6 +1588,10 @@ function decideMaintenanceWindowGate(opts) {
1588
1588
  function shouldConsumeForcedUpdate(outcome) {
1589
1589
  return outcome !== "failed";
1590
1590
  }
1591
+ var FORCED_UPDATE_DEFER_ESCALATE_AFTER = 20;
1592
+ function shouldRelaxForcedUpdateIdleGate(deferStreak) {
1593
+ return deferStreak >= FORCED_UPDATE_DEFER_ESCALATE_AFTER;
1594
+ }
1591
1595
  function decideForcedUpdate(opts) {
1592
1596
  const { requestedAt, lastProcessedAt, hostBusy } = opts;
1593
1597
  if (!requestedAt) return "none";
@@ -8931,14 +8935,20 @@ function restartGateFor(codeName, reason) {
8931
8935
  now: /* @__PURE__ */ new Date()
8932
8936
  });
8933
8937
  }
8934
- function isHostBusyForForcedUpdate() {
8938
+ function isHostBusyForForcedUpdate(opts) {
8935
8939
  const now = /* @__PURE__ */ new Date();
8936
8940
  for (const agent of state6.agents) {
8937
8941
  const decision = decideRestartGate({
8938
8942
  window: null,
8939
8943
  paneLogAgeSeconds: paneLogAgeSecondsFor(agent.codeName),
8940
8944
  transcriptAgeSeconds: transcriptAgeSecondsFor(agent.codeName),
8941
- inboundAgeSeconds: inboundAgeSecondsFor(agent.codeName),
8945
+ // ENG-8079: past the defer-streak threshold, drop the inbound-quiet axis
8946
+ // and gate on progress alone. Passing null means "no signal" for that axis
8947
+ // (see decideRestartGate), so only transcript/pane progress governs — an
8948
+ // agent mid-turn still defers, an agent that merely received a message in
8949
+ // the last 5 minutes no longer does. On a 14-agent host the inbound axis
8950
+ // is what made the gate un-satisfiable in practice.
8951
+ inboundAgeSeconds: opts?.relaxed ? null : inboundAgeSecondsFor(agent.codeName),
8942
8952
  now,
8943
8953
  env: {}
8944
8954
  });
@@ -8946,6 +8956,66 @@ function isHostBusyForForcedUpdate() {
8946
8956
  }
8947
8957
  return false;
8948
8958
  }
8959
+ function runPendingForcedUpdate() {
8960
+ const requestedUpdateAt = cachedUpdateRequestedAt;
8961
+ const relaxed = shouldRelaxForcedUpdateIdleGate(forcedUpdateDeferStreak);
8962
+ const forcedUpdate = decideForcedUpdate({
8963
+ requestedAt: requestedUpdateAt,
8964
+ lastProcessedAt: state6.lastUpdateRequestProcessedAt ?? null,
8965
+ hostBusy: isHostBusyForForcedUpdate({ relaxed })
8966
+ });
8967
+ if (forcedUpdate !== "defer-idle") forcedUpdateDeferStreak = 0;
8968
+ if (forcedUpdate === "none" || forcedUpdate === "already-handled") return false;
8969
+ if (forcedUpdate === "defer-idle") {
8970
+ forcedUpdateDeferStreak += 1;
8971
+ if (forcedUpdateDeferStreak === 1 || forcedUpdateDeferStreak % 10 === 0) {
8972
+ log(
8973
+ `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 deferring: an agent is mid-turn / recently active (consecutive defers: ${forcedUpdateDeferStreak}). Retrying next poll.`
8974
+ );
8975
+ }
8976
+ if (forcedUpdateDeferStreak === FORCED_UPDATE_DEFER_ESCALATE_AFTER) {
8977
+ log(
8978
+ `[self-update] WARN "Update CLI now" has deferred ${forcedUpdateDeferStreak} consecutive polls waiting for an idle host. Relaxing the idle gate to the progress axis only (an agent actively producing tokens still defers; "received a message in the last 5 minutes" no longer does).`
8979
+ );
8980
+ }
8981
+ return false;
8982
+ }
8983
+ if (selfUpdateInFlight) {
8984
+ log(
8985
+ `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 deferring: a self-update is already in flight. Retrying next poll.`
8986
+ );
8987
+ return false;
8988
+ }
8989
+ log(
8990
+ `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 running window-bypassing self-update now (host idle${relaxed ? ", relaxed gate" : ""}).`
8991
+ );
8992
+ const prevProcessedAt = state6.lastUpdateRequestProcessedAt ?? null;
8993
+ void checkAndUpdateCli({ force: true }).then((outcome) => {
8994
+ if (!shouldConsumeForcedUpdate(outcome)) {
8995
+ log(
8996
+ `[self-update] "Update CLI now" did not complete (no install ran) \u2014 leaving the request pending; the next idle poll retries.`
8997
+ );
8998
+ return;
8999
+ }
9000
+ log(
9001
+ outcome === "updated" ? `[self-update] "Update CLI now" completed \u2014 upgrade installed; manager restart scheduled.` : `[self-update] "Update CLI now" completed \u2014 host was already on the latest in-channel build; nothing to install.`
9002
+ );
9003
+ state6.lastUpdateRequestProcessedAt = requestedUpdateAt;
9004
+ try {
9005
+ atomicWriteFileSync(getStateFile(), JSON.stringify(state6, null, 2));
9006
+ } catch (err) {
9007
+ state6.lastUpdateRequestProcessedAt = prevProcessedAt;
9008
+ log(
9009
+ `[self-update] failed to persist update-request ack; retrying next poll: ${err.message}`
9010
+ );
9011
+ }
9012
+ }).catch((err) => {
9013
+ log(
9014
+ `[self-update] forced check failed: ${err.message} \u2014 leaving the request pending for retry.`
9015
+ );
9016
+ });
9017
+ return true;
9018
+ }
8949
9019
  var runningMcpHashes = /* @__PURE__ */ new Map();
8950
9020
  var anchorSessionLifecycle = new AnchorSessionLifecycle();
8951
9021
  var runningMcpServerKeys = /* @__PURE__ */ new Map();
@@ -9307,7 +9377,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9307
9377
  var lastVersionCheckAt = 0;
9308
9378
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9309
9379
  var lastResponsivenessProbeAt = 0;
9310
- var agtCliVersion = true ? "0.28.408" : "dev";
9380
+ var agtCliVersion = true ? "0.28.410" : "dev";
9311
9381
  function resolveBrewPath(execFileSync2) {
9312
9382
  try {
9313
9383
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -9776,6 +9846,8 @@ var selfUpdatePinInvalidLogged = false;
9776
9846
  var restartAfterUpgrade = false;
9777
9847
  var pendingUpgradeVersion = null;
9778
9848
  var selfUpdateInFlight = false;
9849
+ var cachedUpdateRequestedAt = null;
9850
+ var forcedUpdateDeferStreak = 0;
9779
9851
  async function checkAndUpdateCli(opts) {
9780
9852
  const force = opts?.force ?? false;
9781
9853
  if (selfUpdateInFlight) return "failed";
@@ -9954,27 +10026,41 @@ async function checkAndUpdateCliViaNpm(force = false) {
9954
10026
  );
9955
10027
  return await installAgtCliViaNpm(pinDecision.version, `pin=${pinDecision.version}`);
9956
10028
  }
9957
- let latest;
9958
- try {
9959
- const res = await fetch(
9960
- `https://registry.npmjs.org/@integrity-labs/agt-cli/${encodeURIComponent(channel)}`,
9961
- {
9962
- signal: AbortSignal.timeout(1e4),
9963
- headers: { Accept: "application/json" }
10029
+ const REGISTRY_TIMEOUT_MS = 1e4;
10030
+ const registryAttempts = force ? 2 : 1;
10031
+ let latest = null;
10032
+ let lastFetchErr = "";
10033
+ for (let attempt = 1; attempt <= registryAttempts; attempt++) {
10034
+ try {
10035
+ const res = await fetch(
10036
+ `https://registry.npmjs.org/@integrity-labs/agt-cli/${encodeURIComponent(channel)}`,
10037
+ {
10038
+ signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS),
10039
+ headers: { Accept: "application/json" }
10040
+ }
10041
+ );
10042
+ if (!res.ok) {
10043
+ log(`[self-update] npm registry returned ${res.status} (channel=${channel})`);
10044
+ return "failed";
10045
+ }
10046
+ const body = await res.json();
10047
+ if (!body.version) {
10048
+ log(`[self-update] npm registry response missing version field`);
10049
+ return "failed";
10050
+ }
10051
+ latest = body.version;
10052
+ break;
10053
+ } catch (err) {
10054
+ lastFetchErr = err.message;
10055
+ if (attempt < registryAttempts) {
10056
+ log(
10057
+ `[self-update] npm registry fetch failed (attempt ${attempt}/${registryAttempts}): ${lastFetchErr} \u2014 retrying.`
10058
+ );
9964
10059
  }
9965
- );
9966
- if (!res.ok) {
9967
- log(`[self-update] npm registry returned ${res.status} (channel=${channel})`);
9968
- return "failed";
9969
- }
9970
- const body = await res.json();
9971
- if (!body.version) {
9972
- log(`[self-update] npm registry response missing version field`);
9973
- return "failed";
9974
10060
  }
9975
- latest = body.version;
9976
- } catch (err) {
9977
- log(`[self-update] npm registry fetch failed: ${err.message}`);
10061
+ }
10062
+ if (latest === null) {
10063
+ log(`[self-update] npm registry fetch failed: ${lastFetchErr}`);
9978
10064
  return "failed";
9979
10065
  }
9980
10066
  const urgentTarget = await fetchUrgentDistTagVersion();
@@ -10439,7 +10525,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
10439
10525
  if (codeNames.length === 0) return;
10440
10526
  void (async () => {
10441
10527
  try {
10442
- const { collectDiagnostics } = await import("../persistent-session-WYGZTU4O.js");
10528
+ const { collectDiagnostics } = await import("../persistent-session-W5QSIM7F.js");
10443
10529
  await api.post("/host/heartbeat", {
10444
10530
  host_id: hostId,
10445
10531
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor)
@@ -10515,7 +10601,15 @@ async function pollCycle() {
10515
10601
  } catch (err) {
10516
10602
  log(`[restart-handler] processRestartFlags threw: ${err.message}`);
10517
10603
  }
10518
- checkAndUpdateCli().catch((err) => log(`[self-update] Check failed: ${err.message}`));
10604
+ let forcedUpdateKicked = false;
10605
+ try {
10606
+ forcedUpdateKicked = runPendingForcedUpdate();
10607
+ } catch (err) {
10608
+ log(`[self-update] runPendingForcedUpdate threw: ${err.message}`);
10609
+ }
10610
+ if (!forcedUpdateKicked) {
10611
+ checkAndUpdateCli().catch((err) => log(`[self-update] Check failed: ${err.message}`));
10612
+ }
10519
10613
  maybeUpgradeClaudeCode().catch((err) => log(`[claude-code-upgrade] Check failed: ${err.message}`));
10520
10614
  try {
10521
10615
  registeredAgentsCache.clear();
@@ -10538,7 +10632,7 @@ async function pollCycle() {
10538
10632
  }
10539
10633
  try {
10540
10634
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
10541
- const { collectDiagnostics } = await import("../persistent-session-WYGZTU4O.js");
10635
+ const { collectDiagnostics } = await import("../persistent-session-W5QSIM7F.js");
10542
10636
  const diagCodeNames = [...agentState.persistentSessionAgents];
10543
10637
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor) : void 0;
10544
10638
  let tailscaleHostname;
@@ -10625,49 +10719,7 @@ async function pollCycle() {
10625
10719
  cachedDesiredAgtCliVersion = hbResp.desired_agt_cli_version ?? null;
10626
10720
  }
10627
10721
  }
10628
- const requestedUpdateAt = hbResp?.update_requested_at ?? null;
10629
- const forcedUpdate = decideForcedUpdate({
10630
- requestedAt: requestedUpdateAt,
10631
- lastProcessedAt: state6.lastUpdateRequestProcessedAt ?? null,
10632
- hostBusy: isHostBusyForForcedUpdate()
10633
- });
10634
- if (forcedUpdate === "defer-idle") {
10635
- log(
10636
- `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 deferring: an agent is mid-turn / recently active. Retrying next poll.`
10637
- );
10638
- } else if (forcedUpdate === "consume") {
10639
- if (selfUpdateInFlight) {
10640
- log(
10641
- `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 deferring: a self-update is already in flight. Retrying next poll.`
10642
- );
10643
- } else {
10644
- log(
10645
- `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 running window-bypassing self-update now (host idle).`
10646
- );
10647
- const prevProcessedAt = state6.lastUpdateRequestProcessedAt ?? null;
10648
- void checkAndUpdateCli({ force: true }).then((outcome) => {
10649
- if (!shouldConsumeForcedUpdate(outcome)) {
10650
- log(
10651
- `[self-update] "Update CLI now" did not complete (no install ran) \u2014 leaving the request pending; the next idle heartbeat retries.`
10652
- );
10653
- return;
10654
- }
10655
- state6.lastUpdateRequestProcessedAt = requestedUpdateAt;
10656
- try {
10657
- atomicWriteFileSync(getStateFile(), JSON.stringify(state6, null, 2));
10658
- } catch (err) {
10659
- state6.lastUpdateRequestProcessedAt = prevProcessedAt;
10660
- log(
10661
- `[self-update] failed to persist update-request ack; retrying next heartbeat: ${err.message}`
10662
- );
10663
- }
10664
- }).catch((err) => {
10665
- log(
10666
- `[self-update] forced check failed: ${err.message} \u2014 leaving the request pending for retry.`
10667
- );
10668
- });
10669
- }
10670
- }
10722
+ cachedUpdateRequestedAt = hbResp?.update_requested_at ?? null;
10671
10723
  if (hbResp?.feature_flags) {
10672
10724
  hostFlagStore().applyHeartbeat(hbResp.feature_flags, hbResp.flags_schema_version);
10673
10725
  }
@@ -10693,7 +10745,7 @@ async function pollCycle() {
10693
10745
  const {
10694
10746
  collectResponsivenessProbes,
10695
10747
  getResponsivenessIntervalMs
10696
- } = await import("../responsiveness-probe-Z6XRHNMB.js");
10748
+ } = await import("../responsiveness-probe-B7XCQQN4.js");
10697
10749
  const probeIntervalMs = getResponsivenessIntervalMs();
10698
10750
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
10699
10751
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -10725,7 +10777,7 @@ async function pollCycle() {
10725
10777
  collectResponsivenessProbes,
10726
10778
  livePendingInboundOldestAgeSeconds,
10727
10779
  parkPendingInbound
10728
- } = await import("../responsiveness-probe-Z6XRHNMB.js");
10780
+ } = await import("../responsiveness-probe-B7XCQQN4.js");
10729
10781
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
10730
10782
  const wedgeNow = /* @__PURE__ */ new Date();
10731
10783
  const liveAgents = agentState.persistentSessionAgents;
@@ -12364,21 +12416,18 @@ async function processAgent(agent, agentStates) {
12364
12416
  else promptBindProbeAttempts.delete(agent.code_name);
12365
12417
  }
12366
12418
  }
12367
- if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
12368
- const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
12369
- if (mcpPath && existsSync11(mcpPath)) {
12370
- try {
12371
- const cfg = JSON.parse(readFileSync18(mcpPath, "utf-8"));
12372
- const expectedRemoteKeys = new Set(
12373
- integrations.map((i) => i.definition_id)
12374
- );
12375
- for (const key of orphanedRemoteMcpKeys(cfg.mcpServers ?? {}, expectedRemoteKeys)) {
12376
- frameworkAdapter.removeMcpServer(agent.code_name, key);
12377
- log(`[hot-reload] Removed orphaned remote MCP server '${key}' for '${agent.code_name}' (integration removed)`);
12378
- }
12379
- } catch (err) {
12380
- log(`[hot-reload] Failed to prune orphaned remote MCP servers for '${agent.code_name}': ${err.message}`);
12419
+ if (frameworkAdapter.removeMcpServer && frameworkAdapter.readMcpServers) {
12420
+ try {
12421
+ const declaredServers = frameworkAdapter.readMcpServers(agent.code_name);
12422
+ const expectedRemoteKeys = new Set(
12423
+ integrations.map((i) => i.definition_id)
12424
+ );
12425
+ for (const key of orphanedRemoteMcpKeys(declaredServers, expectedRemoteKeys)) {
12426
+ frameworkAdapter.removeMcpServer(agent.code_name, key);
12427
+ log(`[hot-reload] Removed orphaned remote MCP server '${key}' for '${agent.code_name}' (integration removed)`);
12381
12428
  }
12429
+ } catch (err) {
12430
+ log(`[hot-reload] Failed to prune orphaned remote MCP servers for '${agent.code_name}': ${err.message}`);
12382
12431
  }
12383
12432
  }
12384
12433
  const currentIntIds = integrations.map((i) => i.definition_id);
@@ -12560,22 +12609,15 @@ async function processAgent(agent, agentStates) {
12560
12609
  const urlHash = createHash14("sha256").update(e.url).digest("hex").slice(0, 12);
12561
12610
  log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
12562
12611
  }
12563
- if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
12564
- const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
12565
- if (mcpPath) {
12566
- try {
12567
- const { readFileSync: readFileSync19 } = await import("fs");
12568
- const mcpConfig = JSON.parse(readFileSync19(mcpPath, "utf-8"));
12569
- if (mcpConfig.mcpServers) {
12570
- for (const key of Object.keys(mcpConfig.mcpServers)) {
12571
- if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
12572
- frameworkAdapter.removeMcpServer(agent.code_name, key);
12573
- log(`[managed-toolkit] Removed stale MCP server '${key}' for '${agent.code_name}'`);
12574
- }
12575
- }
12612
+ if (frameworkAdapter.removeMcpServer && frameworkAdapter.readMcpServers) {
12613
+ try {
12614
+ for (const key of Object.keys(frameworkAdapter.readMcpServers(agent.code_name))) {
12615
+ if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
12616
+ frameworkAdapter.removeMcpServer(agent.code_name, key);
12617
+ log(`[managed-toolkit] Removed stale MCP server '${key}' for '${agent.code_name}'`);
12576
12618
  }
12577
- } catch {
12578
12619
  }
12620
+ } catch {
12579
12621
  }
12580
12622
  }
12581
12623
  agentState.knownManagedMcpHashes.set(agent.agent_id, mcpHash);
@@ -14037,7 +14079,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
14037
14079
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
14038
14080
  void (async () => {
14039
14081
  try {
14040
- const { collectDiagnostics } = await import("../persistent-session-WYGZTU4O.js");
14082
+ const { collectDiagnostics } = await import("../persistent-session-W5QSIM7F.js");
14041
14083
  await api.post("/host/heartbeat", {
14042
14084
  host_id: hostId,
14043
14085
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor)
@@ -14087,7 +14129,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
14087
14129
  }
14088
14130
  try {
14089
14131
  const hostId = await getHostId();
14090
- const { collectDiagnostics } = await import("../persistent-session-WYGZTU4O.js");
14132
+ const { collectDiagnostics } = await import("../persistent-session-W5QSIM7F.js");
14091
14133
  await api.post("/host/heartbeat", {
14092
14134
  host_id: hostId,
14093
14135
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor)
@@ -14540,7 +14582,7 @@ async function processClaudePairSessions(agents) {
14540
14582
  killPairSession,
14541
14583
  pairTmuxSession,
14542
14584
  finalizeClaudePairOnboarding
14543
- } = await import("../claude-pair-runtime-WWTKA7QC.js");
14585
+ } = await import("../claude-pair-runtime-XGPENQB7.js");
14544
14586
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
14545
14587
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
14546
14588
  const killed = await killPairSession(pairTmuxSession(pairId));