@integrity-labs/agt-cli 0.28.409 → 0.28.411

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.
@@ -25,7 +25,7 @@ import {
25
25
  resolveConnectivityProbe,
26
26
  worseConnectivityOutcome,
27
27
  wrapScheduledTaskPrompt
28
- } from "./chunk-YLLK4G3B.js";
28
+ } from "./chunk-D4MDIZ3T.js";
29
29
  import {
30
30
  parsePsRows
31
31
  } from "./chunk-XWVM4KPK.js";
@@ -6247,7 +6247,7 @@ function requireHost() {
6247
6247
  }
6248
6248
 
6249
6249
  // src/lib/api-client.ts
6250
- var agtCliVersion = true ? "0.28.409" : "dev";
6250
+ var agtCliVersion = true ? "0.28.411" : "dev";
6251
6251
  var lastConfigHash = null;
6252
6252
  function setConfigHash(hash) {
6253
6253
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -8751,4 +8751,4 @@ export {
8751
8751
  managerInstallSystemUnitCommand,
8752
8752
  managerUninstallSystemUnitCommand
8753
8753
  };
8754
- //# sourceMappingURL=chunk-QEWIZCQC.js.map
8754
+ //# sourceMappingURL=chunk-JIT5ORMO.js.map
@@ -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-W5QSIM7F.js");
103
+ const { resolveClaudeBinary } = await import("./persistent-session-YE2Q5LSK.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-XGPENQB7.js.map
376
+ //# sourceMappingURL=claude-pair-runtime-253N4REG.js.map
@@ -45,7 +45,7 @@ import {
45
45
  resolveEffectivePinRaw,
46
46
  safeWriteJsonAtomic,
47
47
  setConfigHash
48
- } from "../chunk-QEWIZCQC.js";
48
+ } from "../chunk-JIT5ORMO.js";
49
49
  import {
50
50
  getProjectDir as getProjectDir2,
51
51
  getReadyTasks,
@@ -85,6 +85,7 @@ import {
85
85
  getFramework,
86
86
  getIntegration,
87
87
  getLastFailureContext,
88
+ getOpencodeSessionState,
88
89
  getProjectDir,
89
90
  getSessionState,
90
91
  hashFile,
@@ -147,7 +148,7 @@ import {
147
148
  toOpencodeModel,
148
149
  transcriptActivityAgeSeconds,
149
150
  writeEgressAllowlist
150
- } from "../chunk-YLLK4G3B.js";
151
+ } from "../chunk-D4MDIZ3T.js";
151
152
  import {
152
153
  reapOrphanChannelMcps
153
154
  } from "../chunk-XWVM4KPK.js";
@@ -1588,6 +1589,10 @@ function decideMaintenanceWindowGate(opts) {
1588
1589
  function shouldConsumeForcedUpdate(outcome) {
1589
1590
  return outcome !== "failed";
1590
1591
  }
1592
+ var FORCED_UPDATE_DEFER_ESCALATE_AFTER = 20;
1593
+ function shouldRelaxForcedUpdateIdleGate(deferStreak) {
1594
+ return deferStreak >= FORCED_UPDATE_DEFER_ESCALATE_AFTER;
1595
+ }
1591
1596
  function decideForcedUpdate(opts) {
1592
1597
  const { requestedAt, lastProcessedAt, hostBusy } = opts;
1593
1598
  if (!requestedAt) return "none";
@@ -8931,14 +8936,20 @@ function restartGateFor(codeName, reason) {
8931
8936
  now: /* @__PURE__ */ new Date()
8932
8937
  });
8933
8938
  }
8934
- function isHostBusyForForcedUpdate() {
8939
+ function isHostBusyForForcedUpdate(opts) {
8935
8940
  const now = /* @__PURE__ */ new Date();
8936
8941
  for (const agent of state6.agents) {
8937
8942
  const decision = decideRestartGate({
8938
8943
  window: null,
8939
8944
  paneLogAgeSeconds: paneLogAgeSecondsFor(agent.codeName),
8940
8945
  transcriptAgeSeconds: transcriptAgeSecondsFor(agent.codeName),
8941
- inboundAgeSeconds: inboundAgeSecondsFor(agent.codeName),
8946
+ // ENG-8079: past the defer-streak threshold, drop the inbound-quiet axis
8947
+ // and gate on progress alone. Passing null means "no signal" for that axis
8948
+ // (see decideRestartGate), so only transcript/pane progress governs — an
8949
+ // agent mid-turn still defers, an agent that merely received a message in
8950
+ // the last 5 minutes no longer does. On a 14-agent host the inbound axis
8951
+ // is what made the gate un-satisfiable in practice.
8952
+ inboundAgeSeconds: opts?.relaxed ? null : inboundAgeSecondsFor(agent.codeName),
8942
8953
  now,
8943
8954
  env: {}
8944
8955
  });
@@ -8946,6 +8957,66 @@ function isHostBusyForForcedUpdate() {
8946
8957
  }
8947
8958
  return false;
8948
8959
  }
8960
+ function runPendingForcedUpdate() {
8961
+ const requestedUpdateAt = cachedUpdateRequestedAt;
8962
+ const relaxed = shouldRelaxForcedUpdateIdleGate(forcedUpdateDeferStreak);
8963
+ const forcedUpdate = decideForcedUpdate({
8964
+ requestedAt: requestedUpdateAt,
8965
+ lastProcessedAt: state6.lastUpdateRequestProcessedAt ?? null,
8966
+ hostBusy: isHostBusyForForcedUpdate({ relaxed })
8967
+ });
8968
+ if (forcedUpdate !== "defer-idle") forcedUpdateDeferStreak = 0;
8969
+ if (forcedUpdate === "none" || forcedUpdate === "already-handled") return false;
8970
+ if (forcedUpdate === "defer-idle") {
8971
+ forcedUpdateDeferStreak += 1;
8972
+ if (forcedUpdateDeferStreak === 1 || forcedUpdateDeferStreak % 10 === 0) {
8973
+ log(
8974
+ `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 deferring: an agent is mid-turn / recently active (consecutive defers: ${forcedUpdateDeferStreak}). Retrying next poll.`
8975
+ );
8976
+ }
8977
+ if (forcedUpdateDeferStreak === FORCED_UPDATE_DEFER_ESCALATE_AFTER) {
8978
+ log(
8979
+ `[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).`
8980
+ );
8981
+ }
8982
+ return false;
8983
+ }
8984
+ if (selfUpdateInFlight) {
8985
+ log(
8986
+ `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 deferring: a self-update is already in flight. Retrying next poll.`
8987
+ );
8988
+ return false;
8989
+ }
8990
+ log(
8991
+ `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 running window-bypassing self-update now (host idle${relaxed ? ", relaxed gate" : ""}).`
8992
+ );
8993
+ const prevProcessedAt = state6.lastUpdateRequestProcessedAt ?? null;
8994
+ void checkAndUpdateCli({ force: true }).then((outcome) => {
8995
+ if (!shouldConsumeForcedUpdate(outcome)) {
8996
+ log(
8997
+ `[self-update] "Update CLI now" did not complete (no install ran) \u2014 leaving the request pending; the next idle poll retries.`
8998
+ );
8999
+ return;
9000
+ }
9001
+ log(
9002
+ 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.`
9003
+ );
9004
+ state6.lastUpdateRequestProcessedAt = requestedUpdateAt;
9005
+ try {
9006
+ atomicWriteFileSync(getStateFile(), JSON.stringify(state6, null, 2));
9007
+ } catch (err) {
9008
+ state6.lastUpdateRequestProcessedAt = prevProcessedAt;
9009
+ log(
9010
+ `[self-update] failed to persist update-request ack; retrying next poll: ${err.message}`
9011
+ );
9012
+ }
9013
+ }).catch((err) => {
9014
+ log(
9015
+ `[self-update] forced check failed: ${err.message} \u2014 leaving the request pending for retry.`
9016
+ );
9017
+ });
9018
+ return true;
9019
+ }
8949
9020
  var runningMcpHashes = /* @__PURE__ */ new Map();
8950
9021
  var anchorSessionLifecycle = new AnchorSessionLifecycle();
8951
9022
  var runningMcpServerKeys = /* @__PURE__ */ new Map();
@@ -9307,7 +9378,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
9307
9378
  var lastVersionCheckAt = 0;
9308
9379
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
9309
9380
  var lastResponsivenessProbeAt = 0;
9310
- var agtCliVersion = true ? "0.28.409" : "dev";
9381
+ var agtCliVersion = true ? "0.28.411" : "dev";
9311
9382
  function resolveBrewPath(execFileSync2) {
9312
9383
  try {
9313
9384
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -9776,6 +9847,8 @@ var selfUpdatePinInvalidLogged = false;
9776
9847
  var restartAfterUpgrade = false;
9777
9848
  var pendingUpgradeVersion = null;
9778
9849
  var selfUpdateInFlight = false;
9850
+ var cachedUpdateRequestedAt = null;
9851
+ var forcedUpdateDeferStreak = 0;
9779
9852
  async function checkAndUpdateCli(opts) {
9780
9853
  const force = opts?.force ?? false;
9781
9854
  if (selfUpdateInFlight) return "failed";
@@ -9954,27 +10027,41 @@ async function checkAndUpdateCliViaNpm(force = false) {
9954
10027
  );
9955
10028
  return await installAgtCliViaNpm(pinDecision.version, `pin=${pinDecision.version}`);
9956
10029
  }
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" }
10030
+ const REGISTRY_TIMEOUT_MS = 1e4;
10031
+ const registryAttempts = force ? 2 : 1;
10032
+ let latest = null;
10033
+ let lastFetchErr = "";
10034
+ for (let attempt = 1; attempt <= registryAttempts; attempt++) {
10035
+ try {
10036
+ const res = await fetch(
10037
+ `https://registry.npmjs.org/@integrity-labs/agt-cli/${encodeURIComponent(channel)}`,
10038
+ {
10039
+ signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS),
10040
+ headers: { Accept: "application/json" }
10041
+ }
10042
+ );
10043
+ if (!res.ok) {
10044
+ log(`[self-update] npm registry returned ${res.status} (channel=${channel})`);
10045
+ return "failed";
10046
+ }
10047
+ const body = await res.json();
10048
+ if (!body.version) {
10049
+ log(`[self-update] npm registry response missing version field`);
10050
+ return "failed";
10051
+ }
10052
+ latest = body.version;
10053
+ break;
10054
+ } catch (err) {
10055
+ lastFetchErr = err.message;
10056
+ if (attempt < registryAttempts) {
10057
+ log(
10058
+ `[self-update] npm registry fetch failed (attempt ${attempt}/${registryAttempts}): ${lastFetchErr} \u2014 retrying.`
10059
+ );
9964
10060
  }
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
10061
  }
9975
- latest = body.version;
9976
- } catch (err) {
9977
- log(`[self-update] npm registry fetch failed: ${err.message}`);
10062
+ }
10063
+ if (latest === null) {
10064
+ log(`[self-update] npm registry fetch failed: ${lastFetchErr}`);
9978
10065
  return "failed";
9979
10066
  }
9980
10067
  const urgentTarget = await fetchUrgentDistTagVersion();
@@ -10439,7 +10526,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
10439
10526
  if (codeNames.length === 0) return;
10440
10527
  void (async () => {
10441
10528
  try {
10442
- const { collectDiagnostics } = await import("../persistent-session-W5QSIM7F.js");
10529
+ const { collectDiagnostics } = await import("../persistent-session-YE2Q5LSK.js");
10443
10530
  await api.post("/host/heartbeat", {
10444
10531
  host_id: hostId,
10445
10532
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor)
@@ -10515,7 +10602,15 @@ async function pollCycle() {
10515
10602
  } catch (err) {
10516
10603
  log(`[restart-handler] processRestartFlags threw: ${err.message}`);
10517
10604
  }
10518
- checkAndUpdateCli().catch((err) => log(`[self-update] Check failed: ${err.message}`));
10605
+ let forcedUpdateKicked = false;
10606
+ try {
10607
+ forcedUpdateKicked = runPendingForcedUpdate();
10608
+ } catch (err) {
10609
+ log(`[self-update] runPendingForcedUpdate threw: ${err.message}`);
10610
+ }
10611
+ if (!forcedUpdateKicked) {
10612
+ checkAndUpdateCli().catch((err) => log(`[self-update] Check failed: ${err.message}`));
10613
+ }
10519
10614
  maybeUpgradeClaudeCode().catch((err) => log(`[claude-code-upgrade] Check failed: ${err.message}`));
10520
10615
  try {
10521
10616
  registeredAgentsCache.clear();
@@ -10538,7 +10633,7 @@ async function pollCycle() {
10538
10633
  }
10539
10634
  try {
10540
10635
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
10541
- const { collectDiagnostics } = await import("../persistent-session-W5QSIM7F.js");
10636
+ const { collectDiagnostics } = await import("../persistent-session-YE2Q5LSK.js");
10542
10637
  const diagCodeNames = [...agentState.persistentSessionAgents];
10543
10638
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor) : void 0;
10544
10639
  let tailscaleHostname;
@@ -10625,49 +10720,7 @@ async function pollCycle() {
10625
10720
  cachedDesiredAgtCliVersion = hbResp.desired_agt_cli_version ?? null;
10626
10721
  }
10627
10722
  }
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
- }
10723
+ cachedUpdateRequestedAt = hbResp?.update_requested_at ?? null;
10671
10724
  if (hbResp?.feature_flags) {
10672
10725
  hostFlagStore().applyHeartbeat(hbResp.feature_flags, hbResp.flags_schema_version);
10673
10726
  }
@@ -10693,7 +10746,7 @@ async function pollCycle() {
10693
10746
  const {
10694
10747
  collectResponsivenessProbes,
10695
10748
  getResponsivenessIntervalMs
10696
- } = await import("../responsiveness-probe-B7XCQQN4.js");
10749
+ } = await import("../responsiveness-probe-QO4EHFCH.js");
10697
10750
  const probeIntervalMs = getResponsivenessIntervalMs();
10698
10751
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
10699
10752
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -10725,7 +10778,7 @@ async function pollCycle() {
10725
10778
  collectResponsivenessProbes,
10726
10779
  livePendingInboundOldestAgeSeconds,
10727
10780
  parkPendingInbound
10728
- } = await import("../responsiveness-probe-B7XCQQN4.js");
10781
+ } = await import("../responsiveness-probe-QO4EHFCH.js");
10729
10782
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
10730
10783
  const wedgeNow = /* @__PURE__ */ new Date();
10731
10784
  const liveAgents = agentState.persistentSessionAgents;
@@ -13445,6 +13498,7 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
13445
13498
  };
13446
13499
  if (openRouterRaw?.auth_token) serveEnv["OPENROUTER_API_KEY"] = openRouterRaw.auth_token;
13447
13500
  if (anthropicRaw?.api_key) serveEnv["ANTHROPIC_API_KEY"] = anthropicRaw.api_key;
13501
+ const startedAtBefore = getOpencodeSessionState(codeName)?.startedAt ?? null;
13448
13502
  const session = await startOpencodeSession({
13449
13503
  codeName,
13450
13504
  agentId: agent.agent_id,
@@ -13458,18 +13512,19 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
13458
13512
  log
13459
13513
  });
13460
13514
  agentState.persistentSessionAgents.add(codeName);
13515
+ const spawnAttempted = session.startedAt !== startedAtBefore;
13461
13516
  if (session.status === "crashed") {
13462
13517
  const tail = session.lastFailureTail ?? readOpencodePaneLogTail(codeName);
13463
13518
  return {
13464
13519
  decision: "spawn",
13465
- spawnAttempted: true,
13520
+ spawnAttempted,
13466
13521
  sessionHealthyAfter: isOpencodeSessionHealthy(codeName),
13467
13522
  detail: tail ? `opencode spawn failed: ${tail.slice(0, 200)}` : "opencode spawn failed"
13468
13523
  };
13469
13524
  }
13470
13525
  return {
13471
13526
  decision: "spawn",
13472
- spawnAttempted: true,
13527
+ spawnAttempted,
13473
13528
  sessionHealthyAfter: isOpencodeSessionHealthy(codeName),
13474
13529
  detail: `opencode serve on 127.0.0.1:${session.port ?? "?"}`
13475
13530
  };
@@ -14027,7 +14082,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
14027
14082
  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}`));
14028
14083
  void (async () => {
14029
14084
  try {
14030
- const { collectDiagnostics } = await import("../persistent-session-W5QSIM7F.js");
14085
+ const { collectDiagnostics } = await import("../persistent-session-YE2Q5LSK.js");
14031
14086
  await api.post("/host/heartbeat", {
14032
14087
  host_id: hostId,
14033
14088
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor)
@@ -14077,7 +14132,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
14077
14132
  }
14078
14133
  try {
14079
14134
  const hostId = await getHostId();
14080
- const { collectDiagnostics } = await import("../persistent-session-W5QSIM7F.js");
14135
+ const { collectDiagnostics } = await import("../persistent-session-YE2Q5LSK.js");
14081
14136
  await api.post("/host/heartbeat", {
14082
14137
  host_id: hostId,
14083
14138
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor)
@@ -14530,7 +14585,7 @@ async function processClaudePairSessions(agents) {
14530
14585
  killPairSession,
14531
14586
  pairTmuxSession,
14532
14587
  finalizeClaudePairOnboarding
14533
- } = await import("../claude-pair-runtime-XGPENQB7.js");
14588
+ } = await import("../claude-pair-runtime-253N4REG.js");
14534
14589
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
14535
14590
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
14536
14591
  const killed = await killPairSession(pairTmuxSession(pairId));