@integrity-labs/agt-cli 0.28.1137 → 0.28.1139

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.
Files changed (30) hide show
  1. package/dist/bin/agt.js +5 -5
  2. package/dist/{chunk-LQCWKNFX.js → chunk-7U4VSBOE.js} +2 -2
  3. package/dist/{chunk-WIU4HUHD.js → chunk-DYFE224A.js} +33 -1
  4. package/dist/{chunk-WIU4HUHD.js.map → chunk-DYFE224A.js.map} +1 -1
  5. package/dist/{chunk-HZFUOGUV.js → chunk-PI2WUKWD.js} +4 -4
  6. package/dist/{claude-pair-runtime-OZLZFJZR.js → claude-pair-runtime-BOTE3DRW.js} +2 -2
  7. package/dist/lib/manager-worker.js +261 -16
  8. package/dist/lib/manager-worker.js.map +1 -1
  9. package/dist/mcp/augmented-admin.js +164 -54
  10. package/dist/mcp/augmented-help-kb.js +164 -54
  11. package/dist/mcp/augmented-support.js +164 -54
  12. package/dist/mcp/direct-chat-channel.js +121 -41
  13. package/dist/mcp/index.js +243 -59
  14. package/dist/mcp/origami.js +121 -41
  15. package/dist/mcp/slack-channel.js +121 -41
  16. package/dist/mcp/teams-channel.js +89 -41
  17. package/dist/mcp/telegram-channel.js +121 -41
  18. package/dist/mcp/whatsapp-channel.js +5447 -4340
  19. package/dist/mcp/whatsapp-link.js +5442 -4383
  20. package/dist/mcp/xero.js +6048 -2067
  21. package/dist/{persistent-session-2X3BCGV5.js → persistent-session-6O66QEI4.js} +3 -3
  22. package/dist/{responsiveness-probe-QFLFS5QX.js → responsiveness-probe-WGD3NDWG.js} +3 -3
  23. package/dist/{session-auth-dead-QE7GT5Y5.js → session-auth-dead-6N2AEFKX.js} +2 -2
  24. package/package.json +2 -2
  25. /package/dist/{chunk-LQCWKNFX.js.map → chunk-7U4VSBOE.js.map} +0 -0
  26. /package/dist/{chunk-HZFUOGUV.js.map → chunk-PI2WUKWD.js.map} +0 -0
  27. /package/dist/{claude-pair-runtime-OZLZFJZR.js.map → claude-pair-runtime-BOTE3DRW.js.map} +0 -0
  28. /package/dist/{persistent-session-2X3BCGV5.js.map → persistent-session-6O66QEI4.js.map} +0 -0
  29. /package/dist/{responsiveness-probe-QFLFS5QX.js.map → responsiveness-probe-WGD3NDWG.js.map} +0 -0
  30. /package/dist/{session-auth-dead-QE7GT5Y5.js.map → session-auth-dead-6N2AEFKX.js.map} +0 -0
@@ -16,7 +16,7 @@ import {
16
16
  parseEnvFileEntries,
17
17
  parseEnvIntegrations,
18
18
  shellQuote
19
- } from "./chunk-LQCWKNFX.js";
19
+ } from "./chunk-7U4VSBOE.js";
20
20
  import {
21
21
  BIND_FAILURE_QUARANTINE_THRESHOLD,
22
22
  INTEGRATIONS_SECTION_END,
@@ -79,7 +79,7 @@ import {
79
79
  sessionTranscriptDir,
80
80
  worseConnectivityOutcome,
81
81
  wrapScheduledTaskPrompt
82
- } from "./chunk-WIU4HUHD.js";
82
+ } from "./chunk-DYFE224A.js";
83
83
  import {
84
84
  classifyUnanswerablePane,
85
85
  isConsumerTermsDialogVisible,
@@ -8076,7 +8076,7 @@ function exchangeFailureKind(err) {
8076
8076
  }
8077
8077
 
8078
8078
  // src/lib/api-client.ts
8079
- var agtCliVersion = true ? "0.28.1137" : "dev";
8079
+ var agtCliVersion = true ? "0.28.1139" : "dev";
8080
8080
  var lastConfigHash = null;
8081
8081
  function setConfigHash(hash) {
8082
8082
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -13227,4 +13227,4 @@ export {
13227
13227
  managerInstallSystemUnitCommand,
13228
13228
  managerUninstallSystemUnitCommand
13229
13229
  };
13230
- //# sourceMappingURL=chunk-HZFUOGUV.js.map
13230
+ //# sourceMappingURL=chunk-PI2WUKWD.js.map
@@ -269,7 +269,7 @@ async function spawnPairSession(session) {
269
269
  return { ok: true };
270
270
  } catch {
271
271
  }
272
- const { resolveClaudeBinary } = await import("./persistent-session-2X3BCGV5.js");
272
+ const { resolveClaudeBinary } = await import("./persistent-session-6O66QEI4.js");
273
273
  const claudeBin = resolveClaudeBinary();
274
274
  const pairEnv = {
275
275
  ...process.env,
@@ -739,4 +739,4 @@ export {
739
739
  startClaudePair,
740
740
  submitClaudePairCode
741
741
  };
742
- //# sourceMappingURL=claude-pair-runtime-OZLZFJZR.js.map
742
+ //# sourceMappingURL=claude-pair-runtime-BOTE3DRW.js.map
@@ -76,7 +76,7 @@ import {
76
76
  setConfigHash,
77
77
  shellGuard,
78
78
  tripClass
79
- } from "../chunk-HZFUOGUV.js";
79
+ } from "../chunk-PI2WUKWD.js";
80
80
  import {
81
81
  getProjectDir as getProjectDir2,
82
82
  getReadyTasks,
@@ -163,7 +163,7 @@ import {
163
163
  webFetchBaselineValue,
164
164
  webFetchRestartNeeded,
165
165
  writeEgressAllowlist
166
- } from "../chunk-LQCWKNFX.js";
166
+ } from "../chunk-7U4VSBOE.js";
167
167
  import {
168
168
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
169
169
  AnchorSessionClient,
@@ -274,7 +274,7 @@ import {
274
274
  subagentActivityAgeSeconds,
275
275
  sumTranscriptUsageInWindow,
276
276
  transcriptActivityAgeSeconds
277
- } from "../chunk-WIU4HUHD.js";
277
+ } from "../chunk-DYFE224A.js";
278
278
  import {
279
279
  classifyUnanswerablePane,
280
280
  focusedConfirmDialogBlock,
@@ -321,6 +321,173 @@ function defaultUnique() {
321
321
  return `${process.pid}-${seq}-${Date.now().toString(36)}`;
322
322
  }
323
323
 
324
+ // src/lib/host-node-pin.ts
325
+ var HOST_NODE_PIN_VERSION_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
326
+ var HOST_NODE_PIN_MIN_MAJOR = 22;
327
+ function normalizeHostNodePinVersion(raw) {
328
+ if (raw == null) return null;
329
+ const v = raw.trim().replace(/^v/, "");
330
+ if (!HOST_NODE_PIN_VERSION_RE.test(v)) return null;
331
+ const major = Number(v.split(".")[0]);
332
+ if (!Number.isInteger(major) || major < HOST_NODE_PIN_MIN_MAJOR) return null;
333
+ return v;
334
+ }
335
+
336
+ // src/lib/host-node-convergence.ts
337
+ var HOST_NODE_RETRY_MS = 15 * 6e4;
338
+ var HOST_NODE_MAX_FAILURES = 3;
339
+ var HostNodeConvergence = class {
340
+ constructor(maxFailures = HOST_NODE_MAX_FAILURES, retryMs = HOST_NODE_RETRY_MS) {
341
+ this.maxFailures = maxFailures;
342
+ this.retryMs = retryMs;
343
+ }
344
+ converged = /* @__PURE__ */ new Set();
345
+ abandoned = /* @__PURE__ */ new Set();
346
+ failures = /* @__PURE__ */ new Map();
347
+ retryAfter = /* @__PURE__ */ new Map();
348
+ /**
349
+ * The ONE pin currently installing, or null. Deliberately a single slot
350
+ * rather than a per-pin set: see {@link shouldAttempt}.
351
+ */
352
+ inFlightPin = null;
353
+ lastDesired = null;
354
+ /**
355
+ * Record what the control plane currently wants, and discard stale
356
+ * conclusions when that changes.
357
+ *
358
+ * Keying per pin is necessary but NOT sufficient, which is the hole CodeRabbit
359
+ * found on #7088. Every state here is a claim about THIS HOST'S DISK at some
360
+ * past moment, and a pin change invalidates all of them: if the fleet moves
361
+ * 22.23.4 -> 22.23.5 and later back to 22.23.4, the disk now holds 22.23.5,
362
+ * while `converged` still asserts 22.23.4 from before. Without this the
363
+ * manager would skip the re-install forever on the strength of a measurement
364
+ * that a later install had already falsified - and 'converged' would once again
365
+ * mean something other than "this host is on the pin", which is the one thing
366
+ * this class exists to keep true.
367
+ *
368
+ * Clearing is cheap to be wrong about in the safe direction: a re-attempt
369
+ * reads `node --version` once and, if the disk already matches, records
370
+ * success immediately.
371
+ *
372
+ * `null` means NO pin is in force — the control plane sent none, or sent one
373
+ * this host refused as malformed. That must be recorded rather than ignored,
374
+ * which was the second hole on #7088: skipping the call left `lastDesired`
375
+ * pointing at the last pin seen, so clearing A and later restoring it looked
376
+ * like no change at all. Any drift on disk during the cleared window —
377
+ * an OS patch, an AMI replacement, a manual install — then survived
378
+ * indefinitely, because the restored pin's stale 'converged' suppressed the
379
+ * one disk read that would have caught it.
380
+ */
381
+ noteDesiredPin(pin) {
382
+ if (this.lastDesired === pin) return;
383
+ this.lastDesired = pin;
384
+ this.converged.clear();
385
+ this.abandoned.clear();
386
+ this.failures.clear();
387
+ this.retryAfter.clear();
388
+ }
389
+ /** The pin last passed to {@link noteDesiredPin}, for reporting. */
390
+ desiredPin() {
391
+ return this.lastDesired;
392
+ }
393
+ stateOf(pin, now = Date.now()) {
394
+ if (this.converged.has(pin)) return "converged";
395
+ if (this.abandoned.has(pin)) return "abandoned";
396
+ if ((this.retryAfter.get(pin) ?? 0) > now) return "cooling";
397
+ return "ready";
398
+ }
399
+ /**
400
+ * Whether to start an attempt now. False while ANY attempt is running —
401
+ * across pins, not just this one.
402
+ *
403
+ * Per-pin gating was the hole CodeRabbit found on #7088, and it is reachable
404
+ * because the caller is fire-and-forget while a `dnf install` may run for ten
405
+ * minutes. Sequence: A is converged; the pin moves to B and an install
406
+ * starts; the pin reverts to A. {@link noteDesiredPin} clears A's stale
407
+ * conclusion (correctly), a per-pin gate does not see B's attempt, so an A
408
+ * attempt starts WHILE B is installing. It reads the disk, which still says
409
+ * A, and records A as converged. B's install then lands, leaving B on disk
410
+ * with `converged` asserting A — and because that is terminal, every later
411
+ * poll skips the check. The host sits off-pin permanently while this class
412
+ * claims otherwise, which is the exact failure it was written to prevent.
413
+ *
414
+ * Serializing costs nothing in the steady state: convergence is a once-per
415
+ * -pin event, and a skipped poll retries ~10s later.
416
+ */
417
+ shouldAttempt(pin, now = Date.now()) {
418
+ if (this.inFlightPin !== null) return false;
419
+ return this.stateOf(pin, now) === "ready";
420
+ }
421
+ /** The pin currently installing, or null. For reporting and for tests. */
422
+ inFlight() {
423
+ return this.inFlightPin;
424
+ }
425
+ beginAttempt(pin) {
426
+ this.inFlightPin = pin;
427
+ }
428
+ endAttempt(pin) {
429
+ if (this.inFlightPin === pin) this.inFlightPin = null;
430
+ }
431
+ /**
432
+ * The on-disk version now equals the pin. Clears any failure history.
433
+ *
434
+ * A success for a pin that is no longer the desired one is DISCARDED. An
435
+ * attempt can outlive the pin that started it, and 'converged' is terminal —
436
+ * so recording it would suppress all future attempts on the strength of a
437
+ * measurement taken against a pin nobody wants any more. Dropping it costs
438
+ * one extra disk read if that pin is ever requested again.
439
+ *
440
+ * The guard is inactive until {@link noteDesiredPin} has been called at least
441
+ * once, so a caller that does not track a desired pin is unaffected.
442
+ */
443
+ recordSuccess(pin) {
444
+ if (this.lastDesired !== null && pin !== this.lastDesired) return;
445
+ this.converged.add(pin);
446
+ this.abandoned.delete(pin);
447
+ this.failures.delete(pin);
448
+ this.retryAfter.delete(pin);
449
+ }
450
+ /**
451
+ * An attempt failed. Returns the resulting state so the caller can log the
452
+ * right thing: 'cooling' means it will retry, 'abandoned' means it will not.
453
+ */
454
+ recordFailure(pin, now = Date.now()) {
455
+ const count = (this.failures.get(pin) ?? 0) + 1;
456
+ this.failures.set(pin, count);
457
+ if (count >= this.maxFailures) {
458
+ this.abandoned.add(pin);
459
+ this.retryAfter.delete(pin);
460
+ return "abandoned";
461
+ }
462
+ this.retryAfter.set(pin, now + this.retryMs);
463
+ return "cooling";
464
+ }
465
+ attemptCount(pin) {
466
+ return this.failures.get(pin) ?? 0;
467
+ }
468
+ /**
469
+ * Every pin this host stopped trying to reach. A non-empty result means "a
470
+ * patch the control plane asked for is NOT applied and nothing is retrying".
471
+ *
472
+ * BE PRECISE ABOUT WHO CAN SEE THAT, because this module's header argues for
473
+ * telling abandonment apart from success "from outside" and Obi Wan correctly
474
+ * pointed out (#7088) that the claim outruns the code. Nothing in production
475
+ * reads this method, `stateOf`, `desiredPin` or `inFlight`: the state is not
476
+ * reported to the API, the heartbeat, or any console surface. The only signal
477
+ * that leaves the host is one `[host-node] … GIVING UP` line in manager.log.
478
+ *
479
+ * So what this class actually buys today is that the DISTINCTION EXISTS and
480
+ * cannot collapse — `converged` can never come to mean "gave up", which is
481
+ * the `ensureIsolationImage` defect the header cites. Surfacing it is a
482
+ * separate, unbuilt piece of work (ENG-11617 acceptance criterion 6), and it
483
+ * matters most for a BYO host in a customer account, which is the one
484
+ * population we cannot inspect any other way.
485
+ */
486
+ abandonedPins() {
487
+ return [...this.abandoned];
488
+ }
489
+ };
490
+
324
491
  // src/lib/manager-worker.ts
325
492
  import { execFileSync as syncExecFile } from "child_process";
326
493
  import { join as join43, dirname as dirname11, delimiter as pathDelimiter } from "path";
@@ -14491,7 +14658,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
14491
14658
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
14492
14659
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
14493
14660
  async function channelInboundActivityAgeSecondsFor(codeName) {
14494
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-QFLFS5QX.js");
14661
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-WGD3NDWG.js");
14495
14662
  const newest = newestPendingInboundActivityMtimeMs(dirname11(paneLogPath(codeName)));
14496
14663
  if (newest === null) return null;
14497
14664
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -15249,7 +15416,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
15249
15416
  var lastVersionCheckAt = 0;
15250
15417
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
15251
15418
  var lastResponsivenessProbeAt = 0;
15252
- var agtCliVersion = true ? "0.28.1137" : "dev";
15419
+ var agtCliVersion = true ? "0.28.1139" : "dev";
15253
15420
  function resolveBrewPath(execFileSync3) {
15254
15421
  try {
15255
15422
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15413,6 +15580,81 @@ function recordIsolationImageFailure(imageUri, reason) {
15413
15580
  isolationImageRetryAfter.set(imageUri, Date.now() + ISOLATION_IMAGE_RETRY_MS);
15414
15581
  }
15415
15582
  }
15583
+ var hostNodeConvergence = new HostNodeConvergence();
15584
+ var hostNodeInvalidLogged = /* @__PURE__ */ new Set();
15585
+ var hostNodeNoDnfLogged = false;
15586
+ function recordHostNodeFailure(pin, reason) {
15587
+ const outcome = hostNodeConvergence.recordFailure(pin);
15588
+ const attempts = hostNodeConvergence.attemptCount(pin);
15589
+ if (outcome === "abandoned") {
15590
+ log(
15591
+ `[host-node] ${reason} \u2014 GIVING UP after ${attempts} attempts. Host Node is NOT on ${pin} and nothing will retry until the manager restarts. This host remains unpatched (ENG-11617).`
15592
+ );
15593
+ } else {
15594
+ log(`[host-node] ${reason} (attempt ${attempts}/${HOST_NODE_MAX_FAILURES}, retrying in ${HOST_NODE_RETRY_MS / 6e4}m)`);
15595
+ }
15596
+ }
15597
+ async function diskNodeVersion() {
15598
+ const probe = await runAsync("node", ["--version"], { timeout: 15e3 });
15599
+ if (probe.code !== 0) return null;
15600
+ const m = /^v?(\d+\.\d+\.\d+)/.exec(probe.stdout.trim());
15601
+ return m?.[1] ?? null;
15602
+ }
15603
+ async function ensureHostNodeVersion(rawPin) {
15604
+ const pin = normalizeHostNodePinVersion(rawPin);
15605
+ if (!pin) {
15606
+ const raw = rawPin == null ? "" : String(rawPin).trim();
15607
+ if (raw !== "" && !hostNodeInvalidLogged.has(raw)) {
15608
+ hostNodeInvalidLogged.add(raw);
15609
+ log(`[host-node] ignoring malformed desired_node_version '${raw.slice(0, 40)}' - not an exact MAJOR.MINOR.PATCH at or above major ${HOST_NODE_PIN_MIN_MAJOR}`);
15610
+ }
15611
+ hostNodeConvergence.noteDesiredPin(null);
15612
+ return;
15613
+ }
15614
+ hostNodeConvergence.noteDesiredPin(pin);
15615
+ if (!hostNodeConvergence.shouldAttempt(pin)) return;
15616
+ hostNodeConvergence.beginAttempt(pin);
15617
+ try {
15618
+ const current = await diskNodeVersion();
15619
+ if (current === pin) {
15620
+ hostNodeConvergence.recordSuccess(pin);
15621
+ return;
15622
+ }
15623
+ const { execFileSync: execFileSync3 } = await import("child_process");
15624
+ try {
15625
+ execFileSync3("which", ["dnf"], { timeout: 5e3, stdio: "pipe" });
15626
+ } catch {
15627
+ if (!hostNodeNoDnfLogged) {
15628
+ hostNodeNoDnfLogged = true;
15629
+ log(`[host-node] 'dnf' not found - cannot converge host Node onto ${pin} on this platform (leaving ${current ?? "unknown"} in place)`);
15630
+ }
15631
+ hostNodeConvergence.recordSuccess(pin);
15632
+ return;
15633
+ }
15634
+ const asRoot = typeof process.getuid === "function" && process.getuid() === 0;
15635
+ const pkg = `nodejs-${pin}`;
15636
+ const dnfArgs = ["-y", "--refresh", "install", pkg];
15637
+ const bin = asRoot ? "dnf" : "sudo";
15638
+ const argv = asRoot ? dnfArgs : ["-n", "dnf", ...dnfArgs];
15639
+ log(`[host-node] converging host Node ${current ?? "unknown"} -> ${pin}\u2026`);
15640
+ const res = await runAsync(bin, argv, { timeout: 6e5 });
15641
+ if (res.code !== 0) {
15642
+ recordHostNodeFailure(pin, `install of ${pkg} failed (exit ${res.code}): ${(res.stderr || res.stdout).slice(0, 300)}`);
15643
+ return;
15644
+ }
15645
+ const after = await diskNodeVersion();
15646
+ if (after !== pin) {
15647
+ recordHostNodeFailure(pin, `installer reported success but on-disk Node is ${after ?? "unreadable"}, not ${pin}`);
15648
+ return;
15649
+ }
15650
+ log(`[host-node] host Node is now ${pin} on disk. Running processes keep the previous binary until they restart - this does NOT restart the manager (ENG-11617).`);
15651
+ hostNodeConvergence.recordSuccess(pin);
15652
+ } catch (err) {
15653
+ recordHostNodeFailure(pin, `install attempt threw: ${isolationImageErrDetail(err)}`);
15654
+ } finally {
15655
+ hostNodeConvergence.endAttempt(pin);
15656
+ }
15657
+ }
15416
15658
  async function ensureIsolationImage(imageUri) {
15417
15659
  if (process.env.AGT_ISOLATION_IMAGE) return;
15418
15660
  if (!imageUri || isolationImageEnsured.has(imageUri)) return;
@@ -16879,7 +17121,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
16879
17121
  if (codeNames.length === 0) return;
16880
17122
  void (async () => {
16881
17123
  try {
16882
- const { collectDiagnostics } = await import("../persistent-session-2X3BCGV5.js");
17124
+ const { collectDiagnostics } = await import("../persistent-session-6O66QEI4.js");
16883
17125
  await api.post("/host/heartbeat", {
16884
17126
  host_id: hostId,
16885
17127
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -17021,7 +17263,7 @@ async function pollCycleInner() {
17021
17263
  }
17022
17264
  try {
17023
17265
  const { detectHostSecurity } = await import("../host-security-KQYYZZVW.js");
17024
- const { collectDiagnostics } = await import("../persistent-session-2X3BCGV5.js");
17266
+ const { collectDiagnostics } = await import("../persistent-session-6O66QEI4.js");
17025
17267
  const diagCodeNames = [...agentState.persistentSessionAgents];
17026
17268
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor) : void 0;
17027
17269
  let tailscaleHostname;
@@ -17229,7 +17471,7 @@ async function pollCycleInner() {
17229
17471
  collectPanelessActivityProbes,
17230
17472
  getResponsivenessIntervalMs,
17231
17473
  occupancyQualificationClassifications
17232
- } = await import("../responsiveness-probe-QFLFS5QX.js");
17474
+ } = await import("../responsiveness-probe-WGD3NDWG.js");
17233
17475
  const probeIntervalMs = getResponsivenessIntervalMs();
17234
17476
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
17235
17477
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -17346,7 +17588,7 @@ async function pollCycleInner() {
17346
17588
  collectResponsivenessProbes,
17347
17589
  livePendingInboundOldestAgeSeconds,
17348
17590
  parkPendingInbound
17349
- } = await import("../responsiveness-probe-QFLFS5QX.js");
17591
+ } = await import("../responsiveness-probe-WGD3NDWG.js");
17350
17592
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-M6AFZWNU.js");
17351
17593
  const wedgeNow = /* @__PURE__ */ new Date();
17352
17594
  const liveAgents = agentState.persistentSessionAgents;
@@ -17632,7 +17874,7 @@ async function pollCycleInner() {
17632
17874
  }
17633
17875
  }
17634
17876
  );
17635
- const { parkPendingInbound } = await import("../responsiveness-probe-QFLFS5QX.js");
17877
+ const { parkPendingInbound } = await import("../responsiveness-probe-WGD3NDWG.js");
17636
17878
  const { parked, deadLettered } = parkPendingInbound(codeName, new Date(fatalNowMs));
17637
17879
  paneFatalStreak.delete(codeName);
17638
17880
  paneFatalRestarts.set(codeName, [...history, fatalNowMs]);
@@ -17663,7 +17905,7 @@ async function pollCycleInner() {
17663
17905
  TRANSCRIPT_MOUNT_ESCALATION_REFRESH_MS,
17664
17906
  bindSourceEscalations
17665
17907
  } = await import("../transcript-mount-health-OHE6IVJV.js");
17666
- const { transcriptBindSourcesFor, ensureTranscriptBindSources } = await import("../persistent-session-2X3BCGV5.js");
17908
+ const { transcriptBindSourcesFor, ensureTranscriptBindSources } = await import("../persistent-session-6O66QEI4.js");
17667
17909
  const mountConfig = resolveTranscriptMountConfig();
17668
17910
  const liveAgents = agentState.persistentSessionAgents;
17669
17911
  for (const tracked of [
@@ -17828,7 +18070,7 @@ async function pollCycleInner() {
17828
18070
  }
17829
18071
  try {
17830
18072
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-VR5QGP3J.js");
17831
- const { probeSessionAuth } = await import("../session-auth-dead-QE7GT5Y5.js");
18073
+ const { probeSessionAuth } = await import("../session-auth-dead-6N2AEFKX.js");
17832
18074
  const observations = [];
17833
18075
  const pendingCacheCommits = [];
17834
18076
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -20633,6 +20875,9 @@ async function processAgent(agent, agentStates, managedToolkits) {
20633
20875
  log(`[isolation-image] unexpected managed-pull failure: ${isolationImageErrDetail(err)}`);
20634
20876
  });
20635
20877
  }
20878
+ void ensureHostNodeVersion(refreshAny.desired_node_version).catch((err) => {
20879
+ log(`[host-node] unexpected convergence failure: ${isolationImageErrDetail(err)}`);
20880
+ });
20636
20881
  const agentFw2 = agentFrameworkCache.get(agent.code_name) ?? DEFAULT_FRAMEWORK;
20637
20882
  if (agentFw2 === "claude-code" && installedIntegrationSkills.length > 0 && isSessionHealthy(agent.code_name)) {
20638
20883
  const names = installedIntegrationSkills.join(", ");
@@ -22423,7 +22668,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
22423
22668
  try {
22424
22669
  const freshId = rotateSessionForWedge(codeName);
22425
22670
  consecutiveWedgeCycles.delete(codeName);
22426
- const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-QFLFS5QX.js");
22671
+ const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-WGD3NDWG.js");
22427
22672
  const { parked, deadLettered } = parkForRestart(codeName, /* @__PURE__ */ new Date());
22428
22673
  const parkNote = parked > 0 ? `, ${parked} inbound parked` : "";
22429
22674
  const deadNote = deadLettered > 0 ? `, ${deadLettered} undeliverable dead-lettered` : "";
@@ -22466,7 +22711,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
22466
22711
  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}`));
22467
22712
  void (async () => {
22468
22713
  try {
22469
- const { collectDiagnostics } = await import("../persistent-session-2X3BCGV5.js");
22714
+ const { collectDiagnostics } = await import("../persistent-session-6O66QEI4.js");
22470
22715
  await api.post("/host/heartbeat", {
22471
22716
  host_id: hostId,
22472
22717
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -22517,7 +22762,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
22517
22762
  }
22518
22763
  try {
22519
22764
  const hostId = await getHostId();
22520
- const { collectDiagnostics } = await import("../persistent-session-2X3BCGV5.js");
22765
+ const { collectDiagnostics } = await import("../persistent-session-6O66QEI4.js");
22521
22766
  await api.post("/host/heartbeat", {
22522
22767
  host_id: hostId,
22523
22768
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -23190,7 +23435,7 @@ async function processClaudePairSessions(agents) {
23190
23435
  // cannot take it for itself — by the time it runs, the write it is looking
23191
23436
  // for may already have happened.
23192
23437
  snapshotPairConfig
23193
- } = await import("../claude-pair-runtime-OZLZFJZR.js");
23438
+ } = await import("../claude-pair-runtime-BOTE3DRW.js");
23194
23439
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
23195
23440
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
23196
23441
  const killed = await killPairSession(pairTmuxSession(pairId));