@integrity-labs/agt-cli 0.28.1146 → 0.28.1148

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 (31) hide show
  1. package/dist/bin/agt.js +21 -13
  2. package/dist/bin/agt.js.map +1 -1
  3. package/dist/{chunk-NBKNLVIB.js → chunk-2PGIZQIJ.js} +3 -3
  4. package/dist/{chunk-XZ6THC7Q.js → chunk-D25NK5E5.js} +5 -1
  5. package/dist/chunk-D25NK5E5.js.map +1 -0
  6. package/dist/{chunk-NS7DXMYO.js → chunk-EZUCUG2W.js} +10 -6
  7. package/dist/{chunk-NS7DXMYO.js.map → chunk-EZUCUG2W.js.map} +1 -1
  8. package/dist/{chunk-FISAZWZ3.js → chunk-JZZW47PT.js} +2 -2
  9. package/dist/{claude-code-updater-3MSALMH3.js → chunk-LAPK7BDU.js} +10 -10
  10. package/dist/{claude-code-updater-3MSALMH3.js.map → chunk-LAPK7BDU.js.map} +1 -1
  11. package/dist/claude-code-updater-NJMCC77Q.js +35 -0
  12. package/dist/{claude-pair-runtime-4GZOYK4O.js → claude-pair-runtime-VQJGKE4W.js} +2 -2
  13. package/dist/lib/manager-worker.js +278 -20
  14. package/dist/lib/manager-worker.js.map +1 -1
  15. package/dist/mcp/direct-chat-channel.js +4 -0
  16. package/dist/mcp/index.js +4 -0
  17. package/dist/mcp/origami.js +4 -0
  18. package/dist/mcp/slack-channel.js +4 -0
  19. package/dist/mcp/telegram-channel.js +4 -0
  20. package/dist/{persistent-session-JKX5IIHF.js → persistent-session-MFHKSBD5.js} +4 -4
  21. package/dist/persistent-session-MFHKSBD5.js.map +1 -0
  22. package/dist/{responsiveness-probe-IWC6K5NQ.js → responsiveness-probe-YLIBVLAR.js} +4 -4
  23. package/dist/{session-auth-dead-U5JSZD5Z.js → session-auth-dead-PUVWMX5P.js} +3 -3
  24. package/package.json +1 -1
  25. package/dist/chunk-XZ6THC7Q.js.map +0 -1
  26. /package/dist/{chunk-NBKNLVIB.js.map → chunk-2PGIZQIJ.js.map} +0 -0
  27. /package/dist/{chunk-FISAZWZ3.js.map → chunk-JZZW47PT.js.map} +0 -0
  28. /package/dist/{persistent-session-JKX5IIHF.js.map → claude-code-updater-NJMCC77Q.js.map} +0 -0
  29. /package/dist/{claude-pair-runtime-4GZOYK4O.js.map → claude-pair-runtime-VQJGKE4W.js.map} +0 -0
  30. /package/dist/{responsiveness-probe-IWC6K5NQ.js.map → responsiveness-probe-YLIBVLAR.js.map} +0 -0
  31. /package/dist/{session-auth-dead-U5JSZD5Z.js.map → session-auth-dead-PUVWMX5P.js.map} +0 -0
@@ -13,6 +13,7 @@ import {
13
13
  TunnelWatcher,
14
14
  api,
15
15
  atomicWriteFileSync,
16
+ buildClaudeAgentMatcher,
16
17
  buildConnectivityProbeDeps,
17
18
  buildProbeEnv,
18
19
  classifyEnvIntegrationsDiff,
@@ -76,7 +77,11 @@ import {
76
77
  setConfigHash,
77
78
  shellGuard,
78
79
  tripClass
79
- } from "../chunk-NS7DXMYO.js";
80
+ } from "../chunk-EZUCUG2W.js";
81
+ import {
82
+ CLAUDE_CODE_SECURITY_FLOOR,
83
+ compareVersions
84
+ } from "../chunk-LAPK7BDU.js";
80
85
  import {
81
86
  getProjectDir as getProjectDir2,
82
87
  getReadyTasks,
@@ -163,7 +168,7 @@ import {
163
168
  webFetchBaselineValue,
164
169
  webFetchRestartNeeded,
165
170
  writeEgressAllowlist
166
- } from "../chunk-NBKNLVIB.js";
171
+ } from "../chunk-2PGIZQIJ.js";
167
172
  import {
168
173
  isAgentIdle,
169
174
  isStaleForToday,
@@ -172,7 +177,7 @@ import {
172
177
  sessionTranscriptDir,
173
178
  subagentActivityAgeSeconds,
174
179
  transcriptActivityAgeSeconds
175
- } from "../chunk-FISAZWZ3.js";
180
+ } from "../chunk-JZZW47PT.js";
176
181
  import {
177
182
  classifyUnanswerablePane,
178
183
  focusedConfirmDialogBlock,
@@ -182,6 +187,7 @@ import {
182
187
  simpleTextHash
183
188
  } from "../chunk-B6UYVEMD.js";
184
189
  import {
190
+ parsePsRows,
185
191
  reapOrphanChannelMcps
186
192
  } from "../chunk-XWVM4KPK.js";
187
193
  import {
@@ -287,7 +293,7 @@ import {
287
293
  serializeAccountEnforcementMarker,
288
294
  stabiliseResetInstant,
289
295
  sumTranscriptUsageInWindow
290
- } from "../chunk-XZ6THC7Q.js";
296
+ } from "../chunk-D25NK5E5.js";
291
297
  import {
292
298
  CLAUDE_CONFIG_DIR_ENV,
293
299
  claudeConfigDirFor,
@@ -299,7 +305,7 @@ import "../chunk-DGUM43GV.js";
299
305
 
300
306
  // src/lib/manager-worker.ts
301
307
  import { createHash as createHash20 } from "crypto";
302
- import { readFileSync as readFileSync34, writeFileSync as writeFileSync18, mkdirSync as mkdirSync16, existsSync as existsSync19, rmSync as rmSync8, readdirSync as readdirSync13, statSync as statSync14, unlinkSync as unlinkSync6, renameSync as renameSync11, utimesSync as utimesSync4 } from "fs";
308
+ import { readFileSync as readFileSync34, writeFileSync as writeFileSync18, mkdirSync as mkdirSync16, existsSync as existsSync19, rmSync as rmSync8, readdirSync as readdirSync13, statSync as statSync14, unlinkSync as unlinkSync6, renameSync as renameSync11, utimesSync as utimesSync4, readlinkSync as readlinkSync2 } from "fs";
303
309
 
304
310
  // src/lib/atomic-file-replace.ts
305
311
  import { copyFileSync, renameSync, unlinkSync } from "fs";
@@ -562,6 +568,161 @@ function heartbeatHostNodeFields(observation, pin, pinState) {
562
568
 
563
569
  // src/lib/manager-worker.ts
564
570
  import { execFileSync as syncExecFile } from "child_process";
571
+
572
+ // src/lib/runtime-image-recycle.ts
573
+ var RUNTIME_CLAUDE_CODE_SECURITY_FLOOR = CLAUDE_CODE_SECURITY_FLOOR;
574
+ var RECYCLE_STAGGER_MS = 2 * 6e4;
575
+ var CONTAINER_IMAGE_CACHE_MS = 5 * 6e4;
576
+ var LOCAL_TAG_CACHE_MS = 6e4;
577
+ function isBelowRuntimeFloor(version, floor = RUNTIME_CLAUDE_CODE_SECURITY_FLOOR) {
578
+ if (!version) return false;
579
+ const cmp = compareVersions(version, floor);
580
+ return cmp !== null && cmp < 0;
581
+ }
582
+ function isReplacedExeLink(link) {
583
+ return typeof link === "string" && link.endsWith(" (deleted)");
584
+ }
585
+ function isContainerImageStale(containerImageId, localTagImageId) {
586
+ if (!containerImageId || !localTagImageId) return false;
587
+ return containerImageId.trim() !== localTagImageId.trim();
588
+ }
589
+ function decideImageRecycleAction(i) {
590
+ if (!i.stale || !i.sessionHealthy) return "none";
591
+ if (!i.inWindow && !i.belowFloor) return "defer-window";
592
+ let verdict;
593
+ if (i.idle) {
594
+ verdict = "recycle";
595
+ } else if (i.belowFloor && i.belowFloorBusyForMs >= i.deadlineMs) {
596
+ const inboundFresh = i.recentInboundAgeSec !== null && i.recentInboundAgeSec < i.inboundGraceSec;
597
+ if (inboundFresh && i.inboundHeldMin < i.inboundHoldMaxMin) return "defer-inbound";
598
+ verdict = "recycle-deadline";
599
+ } else {
600
+ return "defer-busy";
601
+ }
602
+ if (i.sinceLastHostRecycleMs !== null && i.sinceLastHostRecycleMs < i.staggerMs) return "defer-stagger";
603
+ if (!i.deviceStateReady) return "defer-device-state";
604
+ return verdict;
605
+ }
606
+ var belowFloorBusySince = /* @__PURE__ */ new Map();
607
+ var inboundHeldSince = /* @__PURE__ */ new Map();
608
+ var containerImageCache = /* @__PURE__ */ new Map();
609
+ var localTagCache = null;
610
+ var hostSessionCache = /* @__PURE__ */ new Map();
611
+ var lastHostRecycleAt = null;
612
+ var lastLoggedDecision = /* @__PURE__ */ new Map();
613
+ function containerImageIdFor(codeName, io) {
614
+ const now = io.now();
615
+ const hit = containerImageCache.get(codeName);
616
+ if (hit && now - hit.at < CONTAINER_IMAGE_CACHE_MS) return hit.imageId;
617
+ const imageId = io.inspectContainerImage(codeName);
618
+ containerImageCache.set(codeName, { at: now, imageId });
619
+ return imageId;
620
+ }
621
+ function hostSessionStateFor(codeName, io) {
622
+ const now = io.now();
623
+ const hit = hostSessionCache.get(codeName);
624
+ if (hit && now - hit.at < CONTAINER_IMAGE_CACHE_MS) return hit;
625
+ const probe = io.hostSessionBinary ? io.hostSessionBinary(codeName) : null;
626
+ const replaced = probe ? isReplacedExeLink(probe.exeLink) : false;
627
+ let version = null;
628
+ if (probe && replaced && io.runningBinaryVersion) {
629
+ const raw = io.runningBinaryVersion(probe.pid);
630
+ version = raw ? raw.match(/\d+\.\d+\.\d+[A-Za-z0-9.+-]*/)?.[0] ?? null : null;
631
+ }
632
+ const state10 = { at: now, pid: probe?.pid ?? null, replaced, version };
633
+ hostSessionCache.set(codeName, state10);
634
+ return state10;
635
+ }
636
+ function localTagImageIdFor(tag, io) {
637
+ const now = io.now();
638
+ if (localTagCache && localTagCache.tag === tag && now - localTagCache.at < LOCAL_TAG_CACHE_MS) {
639
+ return localTagCache.imageId;
640
+ }
641
+ const imageId = io.inspectTagImage(tag);
642
+ localTagCache = { at: now, tag, imageId };
643
+ return imageId;
644
+ }
645
+ function evaluateImageRecycle(args, io) {
646
+ const { codeName } = args;
647
+ const now = io.now();
648
+ if (!args.sessionHealthy) {
649
+ clearEpisode(codeName);
650
+ return "none";
651
+ }
652
+ const mode = args.mode ?? "container";
653
+ let stale;
654
+ let runningVersion;
655
+ let describe;
656
+ if (mode === "host") {
657
+ const host = hostSessionStateFor(codeName, io);
658
+ stale = host.replaced;
659
+ runningVersion = host.version;
660
+ describe = `host session pid ${host.pid ?? "unknown"} runs a replaced claude binary (running ${host.version ?? "unknown"})`;
661
+ } else {
662
+ const containerImage = containerImageIdFor(codeName, io);
663
+ const localImage = localTagImageIdFor(args.imageTag, io);
664
+ stale = isContainerImageStale(containerImage, localImage);
665
+ runningVersion = args.runtimeClaudeVersion;
666
+ const short = (id) => id ? id.replace(/^sha256:/, "").slice(0, 12) : "unknown";
667
+ describe = `runs image ${short(containerImage)}, ${args.imageTag} is ${short(localImage)}`;
668
+ }
669
+ if (!stale) {
670
+ clearEpisode(codeName);
671
+ return "none";
672
+ }
673
+ const belowFloor = isBelowRuntimeFloor(runningVersion, args.floor);
674
+ if (belowFloor && !args.idle) {
675
+ if (!belowFloorBusySince.has(codeName)) belowFloorBusySince.set(codeName, now);
676
+ } else {
677
+ belowFloorBusySince.delete(codeName);
678
+ }
679
+ const busySince = belowFloorBusySince.get(codeName);
680
+ const heldSince = inboundHeldSince.get(codeName);
681
+ const base = {
682
+ stale,
683
+ sessionHealthy: args.sessionHealthy,
684
+ inWindow: args.inWindow,
685
+ belowFloor,
686
+ idle: args.idle,
687
+ belowFloorBusyForMs: busySince === void 0 ? 0 : now - busySince,
688
+ deadlineMs: args.deadlineMs,
689
+ recentInboundAgeSec: args.recentInboundAgeSec,
690
+ inboundGraceSec: args.inboundGraceSec,
691
+ inboundHeldMin: heldSince === void 0 ? 0 : (now - heldSince) / 6e4,
692
+ inboundHoldMaxMin: args.inboundHoldMaxMin,
693
+ sinceLastHostRecycleMs: lastHostRecycleAt === null ? null : now - lastHostRecycleAt,
694
+ staggerMs: RECYCLE_STAGGER_MS,
695
+ deviceStateReady: true
696
+ };
697
+ let decision = decideImageRecycleAction(base);
698
+ if ((decision === "recycle" || decision === "recycle-deadline") && !args.deviceStateReady()) {
699
+ decision = decideImageRecycleAction({ ...base, deviceStateReady: false });
700
+ }
701
+ if (decision === "defer-inbound") {
702
+ if (!inboundHeldSince.has(codeName)) inboundHeldSince.set(codeName, now);
703
+ } else {
704
+ inboundHeldSince.delete(codeName);
705
+ }
706
+ if (lastLoggedDecision.get(codeName) !== decision) {
707
+ lastLoggedDecision.set(codeName, decision);
708
+ const floorNote = belowFloor ? ` running Claude Code ${runningVersion} is below the security floor ${args.floor ?? RUNTIME_CLAUDE_CODE_SECURITY_FLOOR};` : "";
709
+ io.log(`[runtime-image-recycle] '${codeName}' ${describe};${floorNote} decision=${decision}`);
710
+ }
711
+ return decision;
712
+ }
713
+ function noteRecycled(codeName, io) {
714
+ lastHostRecycleAt = io.now();
715
+ clearEpisode(codeName);
716
+ containerImageCache.delete(codeName);
717
+ hostSessionCache.delete(codeName);
718
+ }
719
+ function clearEpisode(codeName) {
720
+ belowFloorBusySince.delete(codeName);
721
+ inboundHeldSince.delete(codeName);
722
+ lastLoggedDecision.delete(codeName);
723
+ }
724
+
725
+ // src/lib/manager-worker.ts
565
726
  import { join as join43, dirname as dirname11, delimiter as pathDelimiter } from "path";
566
727
  import { homedir as homedir17, hostname, platform as platform3 } from "os";
567
728
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -14730,7 +14891,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
14730
14891
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
14731
14892
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
14732
14893
  async function channelInboundActivityAgeSecondsFor(codeName) {
14733
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-IWC6K5NQ.js");
14894
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-YLIBVLAR.js");
14734
14895
  const newest = newestPendingInboundActivityMtimeMs(dirname11(paneLogPath(codeName)));
14735
14896
  if (newest === null) return null;
14736
14897
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -15488,7 +15649,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
15488
15649
  var lastVersionCheckAt = 0;
15489
15650
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
15490
15651
  var lastResponsivenessProbeAt = 0;
15491
- var agtCliVersion = true ? "0.28.1146" : "dev";
15652
+ var agtCliVersion = true ? "0.28.1148" : "dev";
15492
15653
  function resolveBrewPath(execFileSync3) {
15493
15654
  try {
15494
15655
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15630,6 +15791,61 @@ async function ensureToolkitCliInner(toolkitSlug) {
15630
15791
  }
15631
15792
  }
15632
15793
  var isolationImageEnsured = /* @__PURE__ */ new Set();
15794
+ var runtimeImageRecycleIo = {
15795
+ now: () => Date.now(),
15796
+ inspectContainerImage: (codeName) => {
15797
+ try {
15798
+ return syncExecFile("docker", ["inspect", "--format", "{{.Image}}", `agt-${codeName}`], {
15799
+ timeout: 5e3,
15800
+ stdio: ["ignore", "pipe", "ignore"]
15801
+ }).toString().trim() || null;
15802
+ } catch {
15803
+ return null;
15804
+ }
15805
+ },
15806
+ inspectTagImage: (tag) => {
15807
+ try {
15808
+ return syncExecFile("docker", ["image", "inspect", "--format", "{{.Id}}", tag], {
15809
+ timeout: 5e3,
15810
+ stdio: ["ignore", "pipe", "ignore"]
15811
+ }).toString().trim() || null;
15812
+ } catch {
15813
+ return null;
15814
+ }
15815
+ },
15816
+ // Non-isolated sessions: find `claude --name agt-<code>` (not the docker
15817
+ // wrapper) and read its /proc exe link. Linux-only by nature; anywhere /proc
15818
+ // is absent the link read fails, the session reads as current, and nothing
15819
+ // recycles.
15820
+ hostSessionBinary: (codeName) => {
15821
+ try {
15822
+ const matcher = buildClaudeAgentMatcher(codeName);
15823
+ const ps = syncExecFile("ps", ["-eo", "pid,ppid,args"], { timeout: 5e3, maxBuffer: 16 * 1024 * 1024 }).toString();
15824
+ const row = parsePsRows(ps).find((r) => matcher.test(r.args) && !/\bdocker\b/.test(r.args));
15825
+ if (!row) return null;
15826
+ let exeLink = null;
15827
+ try {
15828
+ exeLink = readlinkSync2(`/proc/${row.pid}/exe`);
15829
+ } catch {
15830
+ exeLink = null;
15831
+ }
15832
+ return { pid: row.pid, exeLink };
15833
+ } catch {
15834
+ return null;
15835
+ }
15836
+ },
15837
+ runningBinaryVersion: (pid) => {
15838
+ try {
15839
+ return syncExecFile(`/proc/${pid}/exe`, ["--version"], {
15840
+ timeout: 1e4,
15841
+ stdio: ["ignore", "pipe", "ignore"]
15842
+ }).toString().trim() || null;
15843
+ } catch {
15844
+ return null;
15845
+ }
15846
+ },
15847
+ log: (msg) => log(msg)
15848
+ };
15633
15849
  var isolationImageRetryAfter = /* @__PURE__ */ new Map();
15634
15850
  var isolationImageFailureCount = /* @__PURE__ */ new Map();
15635
15851
  var isolationImageInFlight = /* @__PURE__ */ new Set();
@@ -17208,7 +17424,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
17208
17424
  if (codeNames.length === 0) return;
17209
17425
  void (async () => {
17210
17426
  try {
17211
- const { collectDiagnostics } = await import("../persistent-session-JKX5IIHF.js");
17427
+ const { collectDiagnostics } = await import("../persistent-session-MFHKSBD5.js");
17212
17428
  await api.post("/host/heartbeat", {
17213
17429
  host_id: hostId,
17214
17430
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -17350,7 +17566,7 @@ async function pollCycleInner() {
17350
17566
  }
17351
17567
  try {
17352
17568
  const { detectHostSecurity } = await import("../host-security-KQYYZZVW.js");
17353
- const { collectDiagnostics } = await import("../persistent-session-JKX5IIHF.js");
17569
+ const { collectDiagnostics } = await import("../persistent-session-MFHKSBD5.js");
17354
17570
  const diagCodeNames = [...agentState.persistentSessionAgents];
17355
17571
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor) : void 0;
17356
17572
  let tailscaleHostname;
@@ -17550,7 +17766,7 @@ async function pollCycleInner() {
17550
17766
  setConfigHash(hbResp?.config_hash ?? null);
17551
17767
  try {
17552
17768
  const desiredClaudeCodeVersion = hbResp?.desired_claude_code_version?.trim();
17553
- const { maybeUpdateClaudeCode, shouldRunClaudeCodeUpdater } = await import("../claude-code-updater-3MSALMH3.js");
17769
+ const { maybeUpdateClaudeCode, shouldRunClaudeCodeUpdater } = await import("../claude-code-updater-NJMCC77Q.js");
17554
17770
  if (desiredClaudeCodeVersion && shouldRunClaudeCodeUpdater({
17555
17771
  desiredVersion: desiredClaudeCodeVersion,
17556
17772
  installedVersion: cachedFrameworkVersion,
@@ -17575,7 +17791,7 @@ async function pollCycleInner() {
17575
17791
  collectPanelessActivityProbes,
17576
17792
  getResponsivenessIntervalMs,
17577
17793
  occupancyQualificationClassifications
17578
- } = await import("../responsiveness-probe-IWC6K5NQ.js");
17794
+ } = await import("../responsiveness-probe-YLIBVLAR.js");
17579
17795
  const probeIntervalMs = getResponsivenessIntervalMs();
17580
17796
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
17581
17797
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -17692,7 +17908,7 @@ async function pollCycleInner() {
17692
17908
  collectResponsivenessProbes,
17693
17909
  livePendingInboundOldestAgeSeconds,
17694
17910
  parkPendingInbound
17695
- } = await import("../responsiveness-probe-IWC6K5NQ.js");
17911
+ } = await import("../responsiveness-probe-YLIBVLAR.js");
17696
17912
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-M6AFZWNU.js");
17697
17913
  const wedgeNow = /* @__PURE__ */ new Date();
17698
17914
  const liveAgents = agentState.persistentSessionAgents;
@@ -17978,7 +18194,7 @@ async function pollCycleInner() {
17978
18194
  }
17979
18195
  }
17980
18196
  );
17981
- const { parkPendingInbound } = await import("../responsiveness-probe-IWC6K5NQ.js");
18197
+ const { parkPendingInbound } = await import("../responsiveness-probe-YLIBVLAR.js");
17982
18198
  const { parked, deadLettered } = parkPendingInbound(codeName, new Date(fatalNowMs));
17983
18199
  paneFatalStreak.delete(codeName);
17984
18200
  paneFatalRestarts.set(codeName, [...history, fatalNowMs]);
@@ -18009,7 +18225,7 @@ async function pollCycleInner() {
18009
18225
  TRANSCRIPT_MOUNT_ESCALATION_REFRESH_MS,
18010
18226
  bindSourceEscalations
18011
18227
  } = await import("../transcript-mount-health-OHE6IVJV.js");
18012
- const { transcriptBindSourcesFor, ensureTranscriptBindSources } = await import("../persistent-session-JKX5IIHF.js");
18228
+ const { transcriptBindSourcesFor, ensureTranscriptBindSources } = await import("../persistent-session-MFHKSBD5.js");
18013
18229
  const mountConfig = resolveTranscriptMountConfig();
18014
18230
  const liveAgents = agentState.persistentSessionAgents;
18015
18231
  for (const tracked of [
@@ -18174,7 +18390,7 @@ async function pollCycleInner() {
18174
18390
  }
18175
18391
  try {
18176
18392
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-VR5QGP3J.js");
18177
- const { probeSessionAuth } = await import("../session-auth-dead-U5JSZD5Z.js");
18393
+ const { probeSessionAuth } = await import("../session-auth-dead-PUVWMX5P.js");
18178
18394
  const observations = [];
18179
18395
  const pendingCacheCommits = [];
18180
18396
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -22157,6 +22373,48 @@ async function ensurePersistentSessionUnchecked(agent, tasks, boardItems, refres
22157
22373
  if (rolloverDecision !== "inbound-wait") {
22158
22374
  dayRolloverInboundHold.delete(codeName);
22159
22375
  }
22376
+ if (restartTrigger === null) {
22377
+ try {
22378
+ const recycleMode = isolationMode(codeName) === "docker" ? "container" : "host";
22379
+ const recycleSessionHealthy = isSessionHealthy(codeName);
22380
+ const recycleCurrent = recycleSessionHealthy ? peekCurrentSession(codeName) : null;
22381
+ const recycleIdle = recycleCurrent ? isAgentIdle(projectDir, recycleCurrent.sessionId) : false;
22382
+ const runtimeSample = runtimeVersionFor(codeName);
22383
+ const runtimeClaudeVersion = recycleMode === "container" ? runtimeSample.state === "ok" ? runtimeSample.version : null : hostSessionStateFor(codeName, runtimeImageRecycleIo).version;
22384
+ const recycleBelowFloor = isBelowRuntimeFloor(runtimeClaudeVersion);
22385
+ const recycleDecision = evaluateImageRecycle(
22386
+ {
22387
+ codeName,
22388
+ mode: recycleMode,
22389
+ imageTag: process.env.AGT_ISOLATION_IMAGE || "agt-runtime:latest",
22390
+ sessionHealthy: recycleSessionHealthy && recycleCurrent !== null,
22391
+ inWindow: decideMaintenanceWindowGate({ window: cachedMaintenanceWindow, now: /* @__PURE__ */ new Date() }) === "proceed",
22392
+ runtimeClaudeVersion,
22393
+ idle: recycleIdle,
22394
+ // Only stat the channel dirs when a forced recycle is on the table.
22395
+ recentInboundAgeSec: recycleBelowFloor && !recycleIdle ? await channelInboundActivityAgeSecondsFor(codeName) : null,
22396
+ inboundGraceSec: DAY_ROLLOVER_INBOUND_GRACE_SEC,
22397
+ inboundHoldMaxMin: DAY_ROLLOVER_INBOUND_DEFER_MAX_MIN,
22398
+ deadlineMs: FORCED_UPDATE_HARD_DEADLINE_MS,
22399
+ deviceStateReady: () => deviceStateAllowsRollover(codeName, "force-reset")
22400
+ },
22401
+ runtimeImageRecycleIo
22402
+ );
22403
+ if (recycleDecision === "recycle" || recycleDecision === "recycle-deadline") {
22404
+ const tornDown = stopPersistentSessionAndForgetMcpBaseline(codeName, "runtime-image-update", void 0, void 0, {
22405
+ reason: recycleDecision === "recycle-deadline" ? `${recycleMode === "container" ? "container" : "host session"} below Claude Code security floor ${RUNTIME_CLAUDE_CODE_SECURITY_FLOOR}; busy past the deadline, recycled onto the current ${recycleMode === "container" ? "agt-runtime image" : "claude binary"}` : recycleMode === "container" ? "container running a superseded agt-runtime image; recycled between turns" : "host session running a replaced claude binary; recycled between turns",
22406
+ detail: { runtime_claude_version: runtimeClaudeVersion, decision: recycleDecision, mode: recycleMode }
22407
+ });
22408
+ if (tornDown) {
22409
+ agentState.persistentSessionAgents.delete(codeName);
22410
+ noteRecycled(codeName, runtimeImageRecycleIo);
22411
+ restartTrigger = "runtime-image";
22412
+ }
22413
+ }
22414
+ } catch (err) {
22415
+ log(`[runtime-image-recycle] check failed for '${codeName}' (leaving the session alone): ${err.message}`);
22416
+ }
22417
+ }
22160
22418
  if (restartTrigger === "day-rollover") {
22161
22419
  rotatePaneLogForDayRollover(codeName, log, agentTimezone);
22162
22420
  }
@@ -22532,7 +22790,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash20("sha256")
22532
22790
  break;
22533
22791
  }
22534
22792
  }
22535
- const finalDecision = restartTrigger === "auth-tuple" ? "restart-pending" : restartTrigger === "day-rollover" ? dayRolloverForced ? "day-rollover-forced" : "day-rollover-restart" : dayRolloverDeferred ? "day-rollover-deferred" : "healthy";
22793
+ const finalDecision = restartTrigger === "auth-tuple" || restartTrigger === "runtime-image" ? "restart-pending" : restartTrigger === "day-rollover" ? dayRolloverForced ? "day-rollover-forced" : "day-rollover-restart" : dayRolloverDeferred ? "day-rollover-deferred" : "healthy";
22536
22794
  return {
22537
22795
  decision: finalDecision,
22538
22796
  spawnAttempted: false,
@@ -22772,7 +23030,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
22772
23030
  try {
22773
23031
  const freshId = rotateSessionForWedge(codeName);
22774
23032
  consecutiveWedgeCycles.delete(codeName);
22775
- const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-IWC6K5NQ.js");
23033
+ const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-YLIBVLAR.js");
22776
23034
  const { parked, deadLettered } = parkForRestart(codeName, /* @__PURE__ */ new Date());
22777
23035
  const parkNote = parked > 0 ? `, ${parked} inbound parked` : "";
22778
23036
  const deadNote = deadLettered > 0 ? `, ${deadLettered} undeliverable dead-lettered` : "";
@@ -22815,7 +23073,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
22815
23073
  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}`));
22816
23074
  void (async () => {
22817
23075
  try {
22818
- const { collectDiagnostics } = await import("../persistent-session-JKX5IIHF.js");
23076
+ const { collectDiagnostics } = await import("../persistent-session-MFHKSBD5.js");
22819
23077
  await api.post("/host/heartbeat", {
22820
23078
  host_id: hostId,
22821
23079
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -22866,7 +23124,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
22866
23124
  }
22867
23125
  try {
22868
23126
  const hostId = await getHostId();
22869
- const { collectDiagnostics } = await import("../persistent-session-JKX5IIHF.js");
23127
+ const { collectDiagnostics } = await import("../persistent-session-MFHKSBD5.js");
22870
23128
  await api.post("/host/heartbeat", {
22871
23129
  host_id: hostId,
22872
23130
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor, runtimeVersionFor)
@@ -23539,7 +23797,7 @@ async function processClaudePairSessions(agents) {
23539
23797
  // cannot take it for itself — by the time it runs, the write it is looking
23540
23798
  // for may already have happened.
23541
23799
  snapshotPairConfig
23542
- } = await import("../claude-pair-runtime-4GZOYK4O.js");
23800
+ } = await import("../claude-pair-runtime-VQJGKE4W.js");
23543
23801
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
23544
23802
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
23545
23803
  const killed = await killPairSession(pairTmuxSession(pairId));