@integrity-labs/agt-cli 0.28.983 → 0.28.985

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.
@@ -258,7 +258,7 @@ async function spawnPairSession(session) {
258
258
  return { ok: true };
259
259
  } catch {
260
260
  }
261
- const { resolveClaudeBinary } = await import("./persistent-session-YKEY6ACQ.js");
261
+ const { resolveClaudeBinary } = await import("./persistent-session-UA7UEZUB.js");
262
262
  const claudeBin = resolveClaudeBinary();
263
263
  const pairEnv = {
264
264
  ...process.env,
@@ -580,4 +580,4 @@ export {
580
580
  startClaudePair,
581
581
  submitClaudePairCode
582
582
  };
583
- //# sourceMappingURL=claude-pair-runtime-Q4MJOZ2J.js.map
583
+ //# sourceMappingURL=claude-pair-runtime-4E4UQNRR.js.map
@@ -63,7 +63,7 @@ import {
63
63
  safeWriteJsonAtomic,
64
64
  setConfigHash,
65
65
  tripClass
66
- } from "../chunk-62VGEG77.js";
66
+ } from "../chunk-XPCEY3CL.js";
67
67
  import {
68
68
  getProjectDir as getProjectDir2,
69
69
  getReadyTasks,
@@ -140,7 +140,7 @@ import {
140
140
  takeZombieDetection,
141
141
  toOpencodeModel,
142
142
  writeEgressAllowlist
143
- } from "../chunk-2JWJZSMK.js";
143
+ } from "../chunk-V5HXKGU3.js";
144
144
  import {
145
145
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
146
146
  AnchorSessionClient,
@@ -238,7 +238,7 @@ import {
238
238
  subagentActivityAgeSeconds,
239
239
  sumTranscriptUsageInWindow,
240
240
  transcriptActivityAgeSeconds
241
- } from "../chunk-3GL4ZO25.js";
241
+ } from "../chunk-IOV5NDDZ.js";
242
242
  import "../chunk-DHWNVVX4.js";
243
243
  import {
244
244
  reapOrphanChannelMcps
@@ -5440,6 +5440,28 @@ function classifyPollError(message) {
5440
5440
  return "other";
5441
5441
  }
5442
5442
 
5443
+ // src/lib/idle-poll-backoff.ts
5444
+ var IDLE_POLL_BACKOFF_CAP_MS = 5 * 6e4;
5445
+ var IDLE_POLL_BACKOFF_CAP_ENV = "AGT_IDLE_POLL_BACKOFF_CAP_MS";
5446
+ var IDLE_POLL_BACKOFF_MAX_CAP_MS = 10 * 6e4;
5447
+ function resolveIdlePollBackoffCapMs(env = process.env) {
5448
+ const raw = (env[IDLE_POLL_BACKOFF_CAP_ENV] ?? "").trim();
5449
+ if (raw === "") return IDLE_POLL_BACKOFF_CAP_MS;
5450
+ const parsed = Number(raw);
5451
+ if (!Number.isFinite(parsed)) return IDLE_POLL_BACKOFF_CAP_MS;
5452
+ const floored = Math.floor(parsed);
5453
+ if (floored <= 0) return IDLE_POLL_BACKOFF_CAP_MS;
5454
+ return Math.min(floored, IDLE_POLL_BACKOFF_MAX_CAP_MS);
5455
+ }
5456
+ function currentIdlePollBackoffMs(consecutiveEmptyAgentPolls2, intervalMs, capMs = IDLE_POLL_BACKOFF_CAP_MS) {
5457
+ if (!Number.isFinite(consecutiveEmptyAgentPolls2) || consecutiveEmptyAgentPolls2 <= 0) return 0;
5458
+ if (!Number.isFinite(intervalMs) || intervalMs <= 0) return 0;
5459
+ if (!Number.isFinite(capMs) || capMs <= 0) return 0;
5460
+ const exponent = Math.min(consecutiveEmptyAgentPolls2, 32);
5461
+ const expDelay = intervalMs * Math.pow(2, exponent);
5462
+ return Math.min(capMs, expDelay);
5463
+ }
5464
+
5443
5465
  // src/lib/per-agent-key.ts
5444
5466
  async function resolvePerAgentApiKey(opts) {
5445
5467
  const isPool = opts.hostKind === "pool";
@@ -13270,7 +13292,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
13270
13292
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
13271
13293
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
13272
13294
  async function channelInboundActivityAgeSecondsFor(codeName) {
13273
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-2T6AKR7I.js");
13295
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-WFCKAA6B.js");
13274
13296
  const newest = newestPendingInboundActivityMtimeMs(dirname11(paneLogPath(codeName)));
13275
13297
  if (newest === null) return null;
13276
13298
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -14013,7 +14035,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
14013
14035
  var lastVersionCheckAt = 0;
14014
14036
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
14015
14037
  var lastResponsivenessProbeAt = 0;
14016
- var agtCliVersion = true ? "0.28.983" : "dev";
14038
+ var agtCliVersion = true ? "0.28.985" : "dev";
14017
14039
  function resolveBrewPath(execFileSync2) {
14018
14040
  try {
14019
14041
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15603,6 +15625,16 @@ function forceRefreshFetch() {
15603
15625
  forceNextExchange();
15604
15626
  }
15605
15627
  var consecutivePollFailures = 0;
15628
+ var consecutiveEmptyAgentPolls = 0;
15629
+ var idlePollBackoffCapMs = resolveIdlePollBackoffCapMs();
15630
+ function currentEffectivePollDelayMs() {
15631
+ const intervalMs = config?.intervalMs ?? 0;
15632
+ return Math.max(
15633
+ intervalMs,
15634
+ currentPollBackoffMs(consecutivePollFailures),
15635
+ currentIdlePollBackoffMs(consecutiveEmptyAgentPolls, intervalMs, idlePollBackoffCapMs)
15636
+ );
15637
+ }
15606
15638
  function reorderRestartedFirst(agents, restartedIds) {
15607
15639
  if (restartedIds.size === 0) return agents;
15608
15640
  const restarted = [];
@@ -15619,7 +15651,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
15619
15651
  if (codeNames.length === 0) return;
15620
15652
  void (async () => {
15621
15653
  try {
15622
- const { collectDiagnostics } = await import("../persistent-session-YKEY6ACQ.js");
15654
+ const { collectDiagnostics } = await import("../persistent-session-UA7UEZUB.js");
15623
15655
  await api.post("/host/heartbeat", {
15624
15656
  host_id: hostId,
15625
15657
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -15761,7 +15793,7 @@ async function pollCycleInner() {
15761
15793
  }
15762
15794
  try {
15763
15795
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
15764
- const { collectDiagnostics } = await import("../persistent-session-YKEY6ACQ.js");
15796
+ const { collectDiagnostics } = await import("../persistent-session-UA7UEZUB.js");
15765
15797
  const diagCodeNames = [...agentState.persistentSessionAgents];
15766
15798
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
15767
15799
  let tailscaleHostname;
@@ -15937,7 +15969,7 @@ async function pollCycleInner() {
15937
15969
  collectPanelessActivityProbes,
15938
15970
  getResponsivenessIntervalMs,
15939
15971
  occupancyQualificationClassifications
15940
- } = await import("../responsiveness-probe-2T6AKR7I.js");
15972
+ } = await import("../responsiveness-probe-WFCKAA6B.js");
15941
15973
  const probeIntervalMs = getResponsivenessIntervalMs();
15942
15974
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
15943
15975
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -16054,7 +16086,7 @@ async function pollCycleInner() {
16054
16086
  collectResponsivenessProbes,
16055
16087
  livePendingInboundOldestAgeSeconds,
16056
16088
  parkPendingInbound
16057
- } = await import("../responsiveness-probe-2T6AKR7I.js");
16089
+ } = await import("../responsiveness-probe-WFCKAA6B.js");
16058
16090
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
16059
16091
  const wedgeNow = /* @__PURE__ */ new Date();
16060
16092
  const liveAgents = agentState.persistentSessionAgents;
@@ -16210,7 +16242,7 @@ async function pollCycleInner() {
16210
16242
  }
16211
16243
  try {
16212
16244
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
16213
- const { probeSessionAuth } = await import("../session-auth-dead-S2XOP5EO.js");
16245
+ const { probeSessionAuth } = await import("../session-auth-dead-LY6ZNPYR.js");
16214
16246
  const observations = [];
16215
16247
  const pendingCacheCommits = [];
16216
16248
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -16280,6 +16312,21 @@ async function pollCycleInner() {
16280
16312
  }
16281
16313
  const data = await api.post("/host/agents", { host_id: hostId });
16282
16314
  const agents = data.agents ?? [];
16315
+ if (agents.length > 0) {
16316
+ if (consecutiveEmptyAgentPolls > 0) {
16317
+ log(
16318
+ `[idle-backoff] ${agents.length} agent(s) assigned after ${consecutiveEmptyAgentPolls} empty poll(s), resuming normal interval`
16319
+ );
16320
+ }
16321
+ consecutiveEmptyAgentPolls = 0;
16322
+ } else {
16323
+ consecutiveEmptyAgentPolls += 1;
16324
+ if (consecutiveEmptyAgentPolls === 1) {
16325
+ log(
16326
+ `[idle-backoff] no agents assigned to this host \u2014 backing off the poll loop toward a ${idlePollBackoffCapMs}ms cap`
16327
+ );
16328
+ }
16329
+ }
16283
16330
  {
16284
16331
  const seen = /* @__PURE__ */ new Set();
16285
16332
  for (const agent of agents) {
@@ -16664,10 +16711,7 @@ async function pollCycleInner() {
16664
16711
  log(`Poll error: ${message}`);
16665
16712
  send({ type: "error", message });
16666
16713
  consecutivePollFailures += 1;
16667
- const scheduledDelayMs = Math.max(
16668
- config?.intervalMs ?? 0,
16669
- currentPollBackoffMs(consecutivePollFailures)
16670
- );
16714
+ const scheduledDelayMs = currentEffectivePollDelayMs();
16671
16715
  const errorClass = classifyPollError(message);
16672
16716
  log(`[poll-backoff] attempt=${consecutivePollFailures} class=${errorClass} next_retry_ms=${scheduledDelayMs}`);
16673
16717
  }
@@ -20486,7 +20530,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
20486
20530
  try {
20487
20531
  const freshId = rotateSessionForWedge(codeName);
20488
20532
  consecutiveWedgeCycles.delete(codeName);
20489
- const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-2T6AKR7I.js");
20533
+ const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-WFCKAA6B.js");
20490
20534
  const { parked, deadLettered } = parkForRestart(codeName, /* @__PURE__ */ new Date());
20491
20535
  const parkNote = parked > 0 ? `, ${parked} inbound parked` : "";
20492
20536
  const deadNote = deadLettered > 0 ? `, ${deadLettered} undeliverable dead-lettered` : "";
@@ -20529,7 +20573,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
20529
20573
  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}`));
20530
20574
  void (async () => {
20531
20575
  try {
20532
- const { collectDiagnostics } = await import("../persistent-session-YKEY6ACQ.js");
20576
+ const { collectDiagnostics } = await import("../persistent-session-UA7UEZUB.js");
20533
20577
  await api.post("/host/heartbeat", {
20534
20578
  host_id: hostId,
20535
20579
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -20580,7 +20624,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
20580
20624
  }
20581
20625
  try {
20582
20626
  const hostId = await getHostId();
20583
- const { collectDiagnostics } = await import("../persistent-session-YKEY6ACQ.js");
20627
+ const { collectDiagnostics } = await import("../persistent-session-UA7UEZUB.js");
20584
20628
  await api.post("/host/heartbeat", {
20585
20629
  host_id: hostId,
20586
20630
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -20623,7 +20667,8 @@ function ensureRealtimeAssignStarted(agentStates) {
20623
20667
  onAssign: (payload) => {
20624
20668
  refreshFetchGate.forceNext(payload.agent_id);
20625
20669
  memoriesFetchGate.forceNext(payload.agent_id);
20626
- log(`[realtime] Agent ${payload.agent_id} assigned \u2014 will pick up next cycle`);
20670
+ log(`[realtime] Agent ${payload.agent_id} assigned`);
20671
+ triggerEarlyPoll(`agent ${payload.agent_id} assigned to this host`);
20627
20672
  markAgentForFreshMemorySync(payload.agent_id);
20628
20673
  },
20629
20674
  onUnassign: (payload) => {
@@ -20838,6 +20883,7 @@ function triggerEarlyPoll(reason) {
20838
20883
  pollTimer = null;
20839
20884
  }
20840
20885
  log(`[realtime] Triggering early poll: ${reason}`);
20886
+ consecutiveEmptyAgentPolls = 0;
20841
20887
  forceRefreshFetch();
20842
20888
  pollTimer = setTimeout(() => {
20843
20889
  void pollCycleAndReschedule(pollCycle, {
@@ -21222,7 +21268,7 @@ async function processClaudePairSessions(agents) {
21222
21268
  killPairSession,
21223
21269
  pairTmuxSession,
21224
21270
  finalizeClaudePairOnboarding
21225
- } = await import("../claude-pair-runtime-Q4MJOZ2J.js");
21271
+ } = await import("../claude-pair-runtime-4E4UQNRR.js");
21226
21272
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
21227
21273
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
21228
21274
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -21973,10 +22019,25 @@ async function postLivenessHeartbeat() {
21973
22019
  // The delay actually in force, so the monitor compares against reality
21974
22020
  // rather than a constant — a manager riding out an API outage under the
21975
22021
  // ENG-5041 backoff must not be paged as stopped.
21976
- poll_backoff_ms: Math.max(
21977
- config.intervalMs,
21978
- currentPollBackoffMs(consecutivePollFailures)
21979
- )
22022
+ //
22023
+ // ENG-10760 made this load-bearing rather than merely tidy. It now also
22024
+ // carries the idle backoff, and `poll-loop-liveness.ts` allows
22025
+ // `poll_backoff_ms * 30` of silence: at the default 10s interval that is
22026
+ // exactly 300,000ms, so an agent-less host sleeping for its 5-minute cap
22027
+ // would sit precisely on the `poll_loop_stopped` boundary if this reported
22028
+ // the interval instead. Reporting the real delay is what keeps a
22029
+ // backed-off host distinguishable from a dead one.
22030
+ //
22031
+ // THE TRADE THIS MAKES, STATED PLAINLY. The allowance scales with what we
22032
+ // report, so an idle host at the 5-minute cap widens its own
22033
+ // `poll_loop_stopped` latency from 5 minutes to 2.5 hours. That is
22034
+ // accepted rather than overlooked: a wedged poll loop on a host with no
22035
+ // agents starves nobody, a genuinely dead HOST is still caught in ~2
22036
+ // minutes by `host_down` off the untouched 30s liveness heartbeat, and the
22037
+ // first thing anyone does with such a host — assign it an agent — surfaces
22038
+ // the wedge immediately. A host with agents is unaffected, because the
22039
+ // idle term is 0 for it.
22040
+ poll_backoff_ms: currentEffectivePollDelayMs()
21980
22041
  });
21981
22042
  } catch (err) {
21982
22043
  log(`[liveness] fast heartbeat failed: ${err.message}`);
@@ -22049,8 +22110,7 @@ function scheduleNext() {
22049
22110
  restartNow();
22050
22111
  return;
22051
22112
  }
22052
- const backoffMs = currentPollBackoffMs(consecutivePollFailures);
22053
- const delayMs = Math.max(config.intervalMs, backoffMs);
22113
+ const delayMs = currentEffectivePollDelayMs();
22054
22114
  pollTimer = setTimeout(() => {
22055
22115
  if (restartAfterUpgrade) {
22056
22116
  restartNow();