@integrity-labs/agt-cli 0.27.150-test.15 → 0.27.151

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.
@@ -15,10 +15,11 @@ import {
15
15
  provisionAutoKanbanProgressHook,
16
16
  provisionIsolationHook,
17
17
  provisionOrientHook,
18
+ provisionSessionStateHook,
18
19
  provisionStopHook,
19
20
  requireHost,
20
21
  safeWriteJsonAtomic
21
- } from "../chunk-24FTY53Z.js";
22
+ } from "../chunk-EI2FRRGG.js";
22
23
  import {
23
24
  getProjectDir as getProjectDir2,
24
25
  getReadyTasks,
@@ -37,10 +38,13 @@ import {
37
38
  getSessionState,
38
39
  injectMessage,
39
40
  injectMessageWithStatus,
41
+ isAgentIdle,
40
42
  isAgentPromptReady,
41
43
  isSessionHealthy,
44
+ isStaleForToday,
42
45
  paneLogPath,
43
46
  parseEnvIntegrations,
47
+ peekCurrentSession,
44
48
  prepareForRespawn,
45
49
  probeMcpEnvSubstitution,
46
50
  readPaneLogTail,
@@ -49,16 +53,19 @@ import {
49
53
  rotateSessionForWedge,
50
54
  sanitizeMcpJson,
51
55
  sendToAgent,
56
+ sessionTranscriptDir,
52
57
  startPersistentSession,
53
58
  stopAllSessionsAndWait,
54
59
  stopPersistentSession,
55
60
  takeWatchdogGiveUpCount,
56
- takeZombieDetection
57
- } from "../chunk-7GKJZBTB.js";
61
+ takeZombieDetection,
62
+ transcriptActivityAgeSeconds
63
+ } from "../chunk-UF6HY7TN.js";
58
64
  import {
59
65
  KANBAN_CHECK_COMMAND,
60
66
  MAX_AVATAR_ENV_URL_BYTES,
61
67
  SUPPRESS_SENTINEL,
68
+ StreamEncoder,
62
69
  appendDmFooter,
63
70
  attributeTranscriptUsageByRun,
64
71
  classifyActor,
@@ -83,13 +90,7 @@ import {
83
90
  resolveDmTarget,
84
91
  worseConnectivityOutcome,
85
92
  wrapScheduledTaskPrompt
86
- } from "../chunk-WOOYOAPG.js";
87
- import {
88
- isAgentIdle,
89
- isStaleForToday,
90
- peekCurrentSession,
91
- sessionTranscriptDir
92
- } from "../chunk-354FAVQR.js";
93
+ } from "../chunk-K5XUY6PS.js";
93
94
  import {
94
95
  parsePsRows,
95
96
  reapOrphanChannelMcps
@@ -100,8 +101,8 @@ import { createHash as createHash3 } from "crypto";
100
101
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, appendFileSync, mkdirSync as mkdirSync4, chmodSync, existsSync as existsSync5, rmSync as rmSync2, readdirSync as readdirSync4, statSync as statSync3, unlinkSync, copyFileSync } from "fs";
101
102
  import https from "https";
102
103
  import { execFileSync as syncExecFile } from "child_process";
103
- import { join as join8, dirname as dirname3 } from "path";
104
- import { homedir as homedir4 } from "os";
104
+ import { join as join9, dirname as dirname3 } from "path";
105
+ import { homedir as homedir5 } from "os";
105
106
  import { fileURLToPath } from "url";
106
107
 
107
108
  // src/lib/mcp-config-drift.ts
@@ -1515,6 +1516,7 @@ function isUrgentUpgrade(opts) {
1515
1516
  }
1516
1517
  var RESTART_IDLE_THRESHOLD_SECONDS = 120;
1517
1518
  var RESTART_INBOUND_QUIET_SECONDS = 300;
1519
+ var RESTART_TRANSCRIPT_STALE_SECONDS = 60;
1518
1520
  var GATEABLE_RESTART_REASONS = /* @__PURE__ */ new Set([
1519
1521
  "model-change",
1520
1522
  "channel-set-change",
@@ -1533,9 +1535,11 @@ function decideRestartGate(opts) {
1533
1535
  }
1534
1536
  const paneThreshold = opts.idleThresholdSeconds ?? RESTART_IDLE_THRESHOLD_SECONDS;
1535
1537
  const inboundThreshold = opts.inboundQuietSeconds ?? RESTART_INBOUND_QUIET_SECONDS;
1536
- const paneBusy = opts.paneLogAgeSeconds !== null && opts.paneLogAgeSeconds < paneThreshold;
1538
+ const transcriptThreshold = opts.transcriptStaleSeconds ?? RESTART_TRANSCRIPT_STALE_SECONDS;
1539
+ const transcriptAge = opts.transcriptAgeSeconds ?? null;
1540
+ const progressBusy = transcriptAge !== null ? transcriptAge < transcriptThreshold : opts.paneLogAgeSeconds !== null && opts.paneLogAgeSeconds < paneThreshold;
1537
1541
  const inboundBusy = opts.inboundAgeSeconds !== null && opts.inboundAgeSeconds < inboundThreshold;
1538
- if (paneBusy || inboundBusy) return "defer-idle";
1542
+ if (progressBusy || inboundBusy) return "defer-idle";
1539
1543
  return "proceed";
1540
1544
  }
1541
1545
 
@@ -1682,6 +1686,148 @@ var DirectChatSpawnGate = class {
1682
1686
  }
1683
1687
  };
1684
1688
 
1689
+ // src/lib/artifact-stream.ts
1690
+ import { join as join2 } from "path";
1691
+ import { homedir } from "os";
1692
+ import { readdir, stat, readFile } from "fs/promises";
1693
+ var ARTIFACT_STREAM_ENV = "AUGMENTED_ARTIFACT_STREAM_ENABLED";
1694
+ var ARTEFACT_ENTRY_FILE = "index.html";
1695
+ function isArtifactStreamingEnabled(env = process.env) {
1696
+ const v = env[ARTIFACT_STREAM_ENV];
1697
+ return v === "true" || v === "1";
1698
+ }
1699
+ function errMessage(err) {
1700
+ return err instanceof Error ? err.message : String(err);
1701
+ }
1702
+ var ArtifactStreamSession = class {
1703
+ constructor(agentId, clientToken, deps, meta = {}) {
1704
+ this.agentId = agentId;
1705
+ this.clientToken = clientToken;
1706
+ this.deps = deps;
1707
+ this.meta = meta;
1708
+ this.encoder = new StreamEncoder(deps.now ? { now: deps.now } : {});
1709
+ }
1710
+ encoder;
1711
+ draftId = null;
1712
+ creating = null;
1713
+ /** The draft id once created, else null. */
1714
+ get id() {
1715
+ return this.draftId;
1716
+ }
1717
+ /**
1718
+ * Create the draft on first use (idempotent via client_token — a manager
1719
+ * restart returns the same draft). Concurrent callers share one in-flight
1720
+ * request. Returns null on failure (best-effort; the next snapshot retries).
1721
+ */
1722
+ async ensureDraft() {
1723
+ if (this.draftId) return this.draftId;
1724
+ if (!this.creating) {
1725
+ this.creating = this.deps.api.post("/host/artifacts/draft", {
1726
+ agent_id: this.agentId,
1727
+ client_token: this.clientToken,
1728
+ kind: this.meta.kind ?? "other",
1729
+ ...this.meta.title ? { title: this.meta.title } : {}
1730
+ }).then((res) => {
1731
+ this.draftId = res?.id ?? null;
1732
+ return this.draftId;
1733
+ }).catch((err) => {
1734
+ this.deps.log?.(`[artifact-stream] draft create failed (${this.clientToken}): ${errMessage(err)}`);
1735
+ return null;
1736
+ }).finally(() => {
1737
+ this.creating = null;
1738
+ });
1739
+ }
1740
+ return this.creating;
1741
+ }
1742
+ /** Encode one snapshot and relay its frame(s). Best-effort, never throws. */
1743
+ async pushSnapshot(content) {
1744
+ const id = await this.ensureDraft();
1745
+ if (!id) return;
1746
+ const frames = this.encoder.encode(content);
1747
+ for (const frame of frames) {
1748
+ try {
1749
+ await this.deps.api.post(`/host/artifacts/${encodeURIComponent(id)}/stream`, {
1750
+ agent_id: this.agentId,
1751
+ frame
1752
+ });
1753
+ } catch (err) {
1754
+ this.deps.log?.(`[artifact-stream] frame ${frame.seq} failed (${id}): ${errMessage(err)}`);
1755
+ }
1756
+ }
1757
+ }
1758
+ };
1759
+ var ArtifactStreamScanner = class {
1760
+ constructor(agentId, artifactsDir, fsDeps, deps) {
1761
+ this.agentId = agentId;
1762
+ this.artifactsDir = artifactsDir;
1763
+ this.fsDeps = fsDeps;
1764
+ this.deps = deps;
1765
+ }
1766
+ sessions = /* @__PURE__ */ new Map();
1767
+ seenMtime = /* @__PURE__ */ new Map();
1768
+ scanning = false;
1769
+ /** One scan pass. Reads + streams every changed artefact. Never throws. */
1770
+ async scanOnce() {
1771
+ if (this.scanning) return;
1772
+ this.scanning = true;
1773
+ try {
1774
+ let names;
1775
+ try {
1776
+ names = await this.fsDeps.listArtefactNames(this.artifactsDir);
1777
+ } catch {
1778
+ return;
1779
+ }
1780
+ for (const name of names) {
1781
+ const file = join2(this.artifactsDir, name, ARTEFACT_ENTRY_FILE);
1782
+ const mtime = await this.fsDeps.mtimeMs(file).catch(() => null);
1783
+ if (mtime === null) continue;
1784
+ if (this.seenMtime.get(name) === mtime) continue;
1785
+ let content;
1786
+ try {
1787
+ content = await this.fsDeps.readFile(file);
1788
+ } catch (err) {
1789
+ this.deps.log?.(`[artifact-stream] read failed (${name}): ${errMessage(err)}`);
1790
+ continue;
1791
+ }
1792
+ this.seenMtime.set(name, mtime);
1793
+ await this.sessionFor(name).pushSnapshot(content);
1794
+ }
1795
+ const live = new Set(names);
1796
+ for (const key of this.seenMtime.keys()) if (!live.has(key)) this.seenMtime.delete(key);
1797
+ for (const key of this.sessions.keys()) if (!live.has(key)) this.sessions.delete(key);
1798
+ } finally {
1799
+ this.scanning = false;
1800
+ }
1801
+ }
1802
+ sessionFor(name) {
1803
+ let session = this.sessions.get(name);
1804
+ if (!session) {
1805
+ session = new ArtifactStreamSession(this.agentId, name, this.deps, { title: name });
1806
+ this.sessions.set(name, session);
1807
+ }
1808
+ return session;
1809
+ }
1810
+ };
1811
+ function artifactsDirFor(codeName) {
1812
+ return join2(homedir(), ".augmented", codeName, "artifacts");
1813
+ }
1814
+ var nodeArtifactFs = {
1815
+ async listArtefactNames(artifactsDir) {
1816
+ const entries = await readdir(artifactsDir, { withFileTypes: true });
1817
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
1818
+ },
1819
+ async mtimeMs(filePath) {
1820
+ try {
1821
+ return (await stat(filePath)).mtimeMs;
1822
+ } catch {
1823
+ return null;
1824
+ }
1825
+ },
1826
+ readFile(filePath) {
1827
+ return readFile(filePath, "utf8");
1828
+ }
1829
+ };
1830
+
1685
1831
  // src/lib/usage-banner-monitor.ts
1686
1832
  var MIN_CHECK_INTERVAL_MS = 6e4;
1687
1833
  var PANE_TAIL_LINES_FOR_BANNER = 200;
@@ -1739,7 +1885,7 @@ async function maybeReportUsageBanner(args) {
1739
1885
 
1740
1886
  // src/lib/token-usage-monitor.ts
1741
1887
  import { readdirSync, readFileSync as readFileSync4, statSync } from "fs";
1742
- import { join as join2 } from "path";
1888
+ import { join as join3 } from "path";
1743
1889
  var MIN_CHECK_INTERVAL_MS2 = 6e4;
1744
1890
  var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
1745
1891
  var MAX_ENTRIES_PER_POST = 200;
@@ -1768,7 +1914,7 @@ async function maybeReportTokenUsage(args) {
1768
1914
  if (!name.endsWith(".jsonl")) continue;
1769
1915
  const sessionId = name.slice(0, -".jsonl".length);
1770
1916
  if (!sessionId) continue;
1771
- const path = join2(dir, name);
1917
+ const path = join3(dir, name);
1772
1918
  let st;
1773
1919
  try {
1774
1920
  st = statSync(path);
@@ -1866,7 +2012,7 @@ async function maybeReportTokenUsage(args) {
1866
2012
 
1867
2013
  // src/lib/conversation-evaluator.ts
1868
2014
  import { readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
1869
- import { join as join3 } from "path";
2015
+ import { join as join4 } from "path";
1870
2016
  var MIN_CHECK_INTERVAL_MS3 = 5 * 6e4;
1871
2017
  var TRANSCRIPT_MTIME_WINDOW_MS2 = 3 * 24 * 60 * 60 * 1e3;
1872
2018
  var WINDOW_PAD_MS = 5 * 6e4;
@@ -2103,7 +2249,7 @@ function readRecentTurns(dir, nowMs) {
2103
2249
  const turns = [];
2104
2250
  for (const name of entries) {
2105
2251
  if (!name.endsWith(".jsonl")) continue;
2106
- const full = join3(dir, name);
2252
+ const full = join4(dir, name);
2107
2253
  let mtimeMs;
2108
2254
  try {
2109
2255
  mtimeMs = statSync2(full).mtimeMs;
@@ -2283,10 +2429,10 @@ async function reportSkip2(api2, agentId, conversationId, log2, codeName) {
2283
2429
 
2284
2430
  // src/lib/activity-cache-monitor.ts
2285
2431
  import { existsSync as existsSync2, readFileSync as readFileSync6 } from "fs";
2286
- import { homedir } from "os";
2287
- import { join as join4 } from "path";
2432
+ import { homedir as homedir2 } from "os";
2433
+ import { join as join5 } from "path";
2288
2434
  var MIN_CHECK_INTERVAL_MS5 = 6e4;
2289
- var STATS_CACHE_PATH = join4(homedir(), ".claude", "stats-cache.json");
2435
+ var STATS_CACHE_PATH = join5(homedir2(), ".claude", "stats-cache.json");
2290
2436
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
2291
2437
  var state5 = { lastObservedDate: null, lastCheckedAt: 0 };
2292
2438
  function selectNewDailyRows(raw, lastObservedDate) {
@@ -2782,9 +2928,9 @@ var GatewayClientPool = class extends EventEmitter {
2782
2928
  };
2783
2929
 
2784
2930
  // src/lib/claude-auth-detect.ts
2785
- import { readFile, readdir } from "fs/promises";
2786
- import { homedir as homedir2, platform } from "os";
2787
- import { join as join5 } from "path";
2931
+ import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
2932
+ import { homedir as homedir3, platform } from "os";
2933
+ import { join as join6 } from "path";
2788
2934
  import { execFile as execFile2 } from "child_process";
2789
2935
  import { promisify } from "util";
2790
2936
  var execFileAsync = promisify(execFile2);
@@ -2799,17 +2945,17 @@ async function detectClaudeAuth() {
2799
2945
  }
2800
2946
  async function findClaudeCredentialsPaths() {
2801
2947
  const candidates = [
2802
- join5(homedir2(), ".claude", ".credentials.json"),
2803
- join5(homedir2(), ".claude", "credentials.json")
2948
+ join6(homedir3(), ".claude", ".credentials.json"),
2949
+ join6(homedir3(), ".claude", "credentials.json")
2804
2950
  ];
2805
2951
  const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
2806
2952
  if (isLinuxRoot) {
2807
2953
  try {
2808
- const entries = await readdir("/home", { withFileTypes: true });
2954
+ const entries = await readdir2("/home", { withFileTypes: true });
2809
2955
  for (const entry of entries) {
2810
2956
  if (!entry.isDirectory()) continue;
2811
- candidates.push(join5("/home", entry.name, ".claude", ".credentials.json"));
2812
- candidates.push(join5("/home", entry.name, ".claude", "credentials.json"));
2957
+ candidates.push(join6("/home", entry.name, ".claude", ".credentials.json"));
2958
+ candidates.push(join6("/home", entry.name, ".claude", "credentials.json"));
2813
2959
  }
2814
2960
  } catch {
2815
2961
  }
@@ -2845,7 +2991,7 @@ async function readMacKeychain() {
2845
2991
  }
2846
2992
  async function readJsonSilently(path) {
2847
2993
  try {
2848
- const raw = await readFile(path, "utf-8");
2994
+ const raw = await readFile2(path, "utf-8");
2849
2995
  return JSON.parse(raw);
2850
2996
  } catch {
2851
2997
  return null;
@@ -2902,10 +3048,10 @@ function normalize(value) {
2902
3048
 
2903
3049
  // src/lib/channel-hash-cache.ts
2904
3050
  import { existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
2905
- import { join as join6 } from "path";
3051
+ import { join as join7 } from "path";
2906
3052
  var CACHE_FILENAME = "channel-hash-cache.json";
2907
3053
  function getChannelHashCacheFile(configDir) {
2908
- return join6(configDir, CACHE_FILENAME);
3054
+ return join7(configDir, CACHE_FILENAME);
2909
3055
  }
2910
3056
  function loadChannelHashCache(target, configDir) {
2911
3057
  const path = getChannelHashCacheFile(configDir);
@@ -3377,13 +3523,18 @@ function clearAgentState(agentId, codeName) {
3377
3523
  // src/lib/wedge-detection.ts
3378
3524
  var DEFAULTS = {
3379
3525
  inboundWaitSeconds: 120,
3380
- // ENG-6238: the hard cap is now an ABSOLUTE BACKSTOP, not the primary
3381
- // discriminator — a still-producing session (e.g. a runaway loop) is only
3382
- // reaped here. Raised 300→1200 because the soft path now reliably catches
3383
- // the frozen-turn wedge via the transcript signal, so the hard cap no longer
3384
- // needs to fire early (which is exactly what false-killed kylie's long
3385
- // legitimate turns, ENG-6238).
3386
- inboundHardWaitSeconds: 1200,
3526
+ // ENG-6264: DISABLED by default (0). A session that's actively producing
3527
+ // tokens is never force-respawned — a working agent must not be killed just
3528
+ // because a message has been queued behind its turn, no matter how long.
3529
+ // ENG-6238 made this an absolute backstop (1200s) to still catch a
3530
+ // producing-but-never-draining runaway loop, but that re-introduced the exact
3531
+ // failure we set out to kill: cutting off real work on a long turn. Runaway
3532
+ // token burn is owned by the cost guardrail (ENG-5556); a producing-but-silent
3533
+ // loop still trips the synthetic-probe alarm. So the backstop is now opt-in:
3534
+ // set AGT_WEDGE_INBOUND_HARD_WAIT_SECONDS to a positive value to re-enable it
3535
+ // (floored at inboundWaitSeconds). 0 = the frozen/hung wedge (transcript
3536
+ // static) is still caught by the soft path; only the *producing* path is spared.
3537
+ inboundHardWaitSeconds: 0,
3387
3538
  paneStaleSeconds: 120,
3388
3539
  transcriptStaleSeconds: 60,
3389
3540
  minCycles: 3
@@ -3402,10 +3553,12 @@ function resolveWedgeConfig(env = process.env) {
3402
3553
  DEFAULTS.inboundWaitSeconds,
3403
3554
  30
3404
3555
  );
3405
- const inboundHardWaitSeconds = Math.max(
3406
- inboundWaitSeconds,
3407
- parsePositiveInt(env.AGT_WEDGE_INBOUND_HARD_WAIT_SECONDS, DEFAULTS.inboundHardWaitSeconds, 30)
3556
+ const inboundHardWaitRaw = parsePositiveInt(
3557
+ env.AGT_WEDGE_INBOUND_HARD_WAIT_SECONDS,
3558
+ DEFAULTS.inboundHardWaitSeconds,
3559
+ 0
3408
3560
  );
3561
+ const inboundHardWaitSeconds = inboundHardWaitRaw <= 0 ? 0 : Math.max(inboundWaitSeconds, inboundHardWaitRaw);
3409
3562
  return {
3410
3563
  mode: parseMode(env.AGT_WEDGE_RESTART_MODE),
3411
3564
  inboundWaitSeconds,
@@ -3430,6 +3583,7 @@ function isWedgeCandidateCycle(signals, config2) {
3430
3583
  if (inboundAge === null) return false;
3431
3584
  if (inboundAge < config2.inboundWaitSeconds) return false;
3432
3585
  if (isSessionProducing(signals, config2)) {
3586
+ if (config2.inboundHardWaitSeconds <= 0) return false;
3433
3587
  return inboundAge >= config2.inboundHardWaitSeconds;
3434
3588
  }
3435
3589
  return true;
@@ -3498,14 +3652,14 @@ function partitionActionableByPoison(actionable, states, config2) {
3498
3652
 
3499
3653
  // src/lib/restart-flags.ts
3500
3654
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync8, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
3501
- import { homedir as homedir3 } from "os";
3502
- import { join as join7 } from "path";
3655
+ import { homedir as homedir4 } from "os";
3656
+ import { join as join8 } from "path";
3503
3657
  import { randomUUID } from "crypto";
3504
3658
  function restartFlagsDir() {
3505
- return join7(homedir3(), ".augmented", "restart-flags");
3659
+ return join8(homedir4(), ".augmented", "restart-flags");
3506
3660
  }
3507
3661
  function flagPath(codeName) {
3508
- return join7(restartFlagsDir(), `${codeName}.flag`);
3662
+ return join8(restartFlagsDir(), `${codeName}.flag`);
3509
3663
  }
3510
3664
  function readRestartFlags() {
3511
3665
  const dir = restartFlagsDir();
@@ -3514,7 +3668,7 @@ function readRestartFlags() {
3514
3668
  for (const entry of readdirSync3(dir)) {
3515
3669
  if (!entry.endsWith(".flag")) continue;
3516
3670
  try {
3517
- const raw = readFileSync8(join7(dir, entry), "utf8");
3671
+ const raw = readFileSync8(join8(dir, entry), "utf8");
3518
3672
  const parsed = JSON.parse(raw);
3519
3673
  if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
3520
3674
  parsed.codeName = entry.replace(/\.flag$/, "");
@@ -4062,8 +4216,8 @@ function applyRestartAcks(args) {
4062
4216
  var GATEWAY_PORT_BASE = 18800;
4063
4217
  var GATEWAY_PORT_STEP = 10;
4064
4218
  var GATEWAY_PORT_MAX = 18899;
4065
- var AUGMENTED_DIR = join8(process.env["HOME"] ?? "/tmp", ".augmented");
4066
- var GATEWAY_PORTS_FILE = join8(AUGMENTED_DIR, "gateway-ports.json");
4219
+ var AUGMENTED_DIR = join9(process.env["HOME"] ?? "/tmp", ".augmented");
4220
+ var GATEWAY_PORTS_FILE = join9(AUGMENTED_DIR, "gateway-ports.json");
4067
4221
  var CHANNEL_SWEEP_INTERVAL_MS = (() => {
4068
4222
  const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
4069
4223
  if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
@@ -4417,11 +4571,16 @@ function paneLogAgeSecondsFor(codeName) {
4417
4571
  return 0;
4418
4572
  }
4419
4573
  }
4420
- function restartGateFor(codeName, breakerReason) {
4421
- if (!isGateableRestartReason(breakerReason)) return "bypass";
4574
+ function transcriptAgeSecondsFor(codeName) {
4575
+ const sessionId = getSessionState(codeName)?.currentSessionId ?? null;
4576
+ return transcriptActivityAgeSeconds(getProjectDir2(codeName), sessionId, /* @__PURE__ */ new Date());
4577
+ }
4578
+ function restartGateFor(codeName, reason) {
4579
+ if (!isGateableRestartReason(reason)) return "bypass";
4422
4580
  return decideRestartGate({
4423
4581
  window: cachedMaintenanceWindow,
4424
4582
  paneLogAgeSeconds: paneLogAgeSecondsFor(codeName),
4583
+ transcriptAgeSeconds: transcriptAgeSecondsFor(codeName),
4425
4584
  inboundAgeSeconds: inboundAgeSecondsFor(codeName),
4426
4585
  now: /* @__PURE__ */ new Date()
4427
4586
  });
@@ -4431,7 +4590,7 @@ var runningMcpServerKeys = /* @__PURE__ */ new Map();
4431
4590
  var runningChannelSecretHashes = /* @__PURE__ */ new Map();
4432
4591
  function projectMcpHash(_codeName, projectDir) {
4433
4592
  try {
4434
- const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
4593
+ const raw = readFileSync9(join9(projectDir, ".mcp.json"), "utf-8");
4435
4594
  return createHash3("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
4436
4595
  } catch {
4437
4596
  return null;
@@ -4439,7 +4598,7 @@ function projectMcpHash(_codeName, projectDir) {
4439
4598
  }
4440
4599
  function projectMcpKeys(_codeName, projectDir) {
4441
4600
  try {
4442
- const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
4601
+ const raw = readFileSync9(join9(projectDir, ".mcp.json"), "utf-8");
4443
4602
  const parsed = JSON.parse(raw);
4444
4603
  const servers = parsed.mcpServers;
4445
4604
  if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
@@ -4450,7 +4609,7 @@ function projectMcpKeys(_codeName, projectDir) {
4450
4609
  }
4451
4610
  function readMcpHttpServerConfig(projectDir, serverKey, env) {
4452
4611
  try {
4453
- const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
4612
+ const raw = readFileSync9(join9(projectDir, ".mcp.json"), "utf-8");
4454
4613
  const servers = JSON.parse(raw).mcpServers ?? {};
4455
4614
  const entry = servers[serverKey];
4456
4615
  if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
@@ -4478,7 +4637,7 @@ async function runAgentConnectivityProbes(agent, integrations, projectDir) {
4478
4637
  if (integrations.length === 0) return;
4479
4638
  const probeEnv = { ...process.env };
4480
4639
  try {
4481
- const envIntPath = join8(projectDir, ".env.integrations");
4640
+ const envIntPath = join9(projectDir, ".env.integrations");
4482
4641
  if (existsSync5(envIntPath)) {
4483
4642
  Object.assign(probeEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
4484
4643
  }
@@ -4580,10 +4739,10 @@ async function runAgentConnectivityProbes(agent, integrations, projectDir) {
4580
4739
  );
4581
4740
  }
4582
4741
  }
4583
- function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason) {
4584
- const gate = restartGateFor(codeName, breakerReason);
4742
+ function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason, gateReason = breakerReason) {
4743
+ const gate = restartGateFor(codeName, gateReason);
4585
4744
  if (gate !== "bypass" && gate !== "proceed") {
4586
- log(`[maintenance-window] Deferring '${breakerReason}' restart for '${codeName}' (${gate})`);
4745
+ log(`[maintenance-window] Deferring '${gateReason}' restart for '${codeName}' (${gate})`);
4587
4746
  return;
4588
4747
  }
4589
4748
  cancelPendingSessionRestart(codeName);
@@ -4668,7 +4827,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
4668
4827
  function projectChannelSecretHash(projectDir) {
4669
4828
  try {
4670
4829
  const entries = parseEnvIntegrations(
4671
- readFileSync9(join8(projectDir, ".env.integrations"), "utf-8")
4830
+ readFileSync9(join9(projectDir, ".env.integrations"), "utf-8")
4672
4831
  );
4673
4832
  return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
4674
4833
  } catch {
@@ -4760,7 +4919,7 @@ var cachedMaintenanceWindow = null;
4760
4919
  var lastVersionCheckAt = 0;
4761
4920
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
4762
4921
  var lastResponsivenessProbeAt = 0;
4763
- var agtCliVersion = true ? "0.27.150-test.15" : "dev";
4922
+ var agtCliVersion = true ? "0.27.151" : "dev";
4764
4923
  function resolveBrewPath(execFileSync4) {
4765
4924
  try {
4766
4925
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -5011,7 +5170,7 @@ async function ensureFrameworkBinary(frameworkId) {
5011
5170
  var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
5012
5171
  var claudeCodeUpgradeInFlight = false;
5013
5172
  function claudeCodeUpgradeMarkerPath() {
5014
- return join8(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
5173
+ return join9(homedir5(), ".augmented", ".last-claude-code-upgrade-check");
5015
5174
  }
5016
5175
  function stampClaudeCodeUpgradeMarker() {
5017
5176
  try {
@@ -5074,7 +5233,7 @@ ${r.stderr}`;
5074
5233
  }
5075
5234
  var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
5076
5235
  function selfUpdateAppliedMarkerPath() {
5077
- return join8(homedir4(), ".augmented", ".last-self-update-applied");
5236
+ return join9(homedir5(), ".augmented", ".last-self-update-applied");
5078
5237
  }
5079
5238
  var selfUpdateUpToDateLogged = false;
5080
5239
  var restartAfterUpgrade = false;
@@ -5097,7 +5256,7 @@ async function checkAndUpdateCli() {
5097
5256
  const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
5098
5257
  if (!isBrewFormula && !isNpmGlobal) return;
5099
5258
  const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
5100
- const markerPath = join8(homedir4(), ".augmented", ".last-update-check");
5259
+ const markerPath = join9(homedir5(), ".augmented", ".last-update-check");
5101
5260
  try {
5102
5261
  const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
5103
5262
  if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
@@ -5354,9 +5513,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
5354
5513
  throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
5355
5514
  }
5356
5515
  childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
5357
- const claudeDir = join8(homedir4(), ".claude");
5516
+ const claudeDir = join9(homedir5(), ".claude");
5358
5517
  for (const filename of [".credentials.json", "credentials.json"]) {
5359
- const p = join8(claudeDir, filename);
5518
+ const p = join9(claudeDir, filename);
5360
5519
  if (existsSync5(p)) {
5361
5520
  try {
5362
5521
  rmSync2(p, { force: true });
@@ -5372,12 +5531,12 @@ async function applyClaudeAuthToEnv(childEnv, label) {
5372
5531
  var evalEmptyMcpConfigPath = null;
5373
5532
  function ensureEvalEmptyMcpConfig() {
5374
5533
  if (evalEmptyMcpConfigPath && existsSync5(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
5375
- const dir = join8(homedir4(), ".augmented");
5534
+ const dir = join9(homedir5(), ".augmented");
5376
5535
  try {
5377
5536
  mkdirSync4(dir, { recursive: true });
5378
5537
  } catch {
5379
5538
  }
5380
- const p = join8(dir, ".eval-empty-mcp.json");
5539
+ const p = join9(dir, ".eval-empty-mcp.json");
5381
5540
  writeFileSync4(p, JSON.stringify({ mcpServers: {} }));
5382
5541
  evalEmptyMcpConfigPath = p;
5383
5542
  return p;
@@ -5403,7 +5562,7 @@ async function runEvalClaude(prompt, model) {
5403
5562
  ""
5404
5563
  ];
5405
5564
  const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
5406
- cwd: homedir4(),
5565
+ cwd: homedir5(),
5407
5566
  timeout: 12e4,
5408
5567
  stdin: "ignore",
5409
5568
  env: childEnv,
@@ -5482,10 +5641,10 @@ function freePort(codeName) {
5482
5641
  }
5483
5642
  }
5484
5643
  function getStateFile() {
5485
- return join8(config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
5644
+ return join9(config?.configDir ?? join9(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
5486
5645
  }
5487
5646
  function channelHashCacheDir() {
5488
- return config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
5647
+ return config?.configDir ?? join9(process.env["HOME"] ?? "/tmp", ".augmented");
5489
5648
  }
5490
5649
  function loadChannelHashCache2() {
5491
5650
  loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
@@ -5496,7 +5655,7 @@ function saveChannelHashCache2() {
5496
5655
  var _channelQuarantineStore = null;
5497
5656
  function channelQuarantineStore() {
5498
5657
  if (!_channelQuarantineStore) {
5499
- const dir = config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
5658
+ const dir = config?.configDir ?? join9(process.env["HOME"] ?? "/tmp", ".augmented");
5500
5659
  _channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
5501
5660
  }
5502
5661
  return _channelQuarantineStore;
@@ -5551,7 +5710,7 @@ function log(msg) {
5551
5710
  `;
5552
5711
  if (!managerLogPath) {
5553
5712
  try {
5554
- managerLogPath = join8(homedir4(), ".augmented", "manager.log");
5713
+ managerLogPath = join9(homedir5(), ".augmented", "manager.log");
5555
5714
  mkdirSync4(dirname3(managerLogPath), { recursive: true });
5556
5715
  if (existsSync5(managerLogPath)) {
5557
5716
  chmodSync(managerLogPath, 384);
@@ -5606,12 +5765,12 @@ function parseSkillFrontmatter(content) {
5606
5765
  }
5607
5766
  async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
5608
5767
  const { readdirSync: readdirSync5, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync5 } = await import("fs");
5609
- const skillsDir = join8(configDir, codeName, "project", ".claude", "skills");
5610
- const claudeMdPath = join8(configDir, codeName, "project", "CLAUDE.md");
5768
+ const skillsDir = join9(configDir, codeName, "project", ".claude", "skills");
5769
+ const claudeMdPath = join9(configDir, codeName, "project", "CLAUDE.md");
5611
5770
  if (!ex(skillsDir) || !ex(claudeMdPath)) return;
5612
5771
  const entries = [];
5613
5772
  for (const dir of readdirSync5(skillsDir).sort()) {
5614
- const skillFile = join8(skillsDir, dir, "SKILL.md");
5773
+ const skillFile = join9(skillsDir, dir, "SKILL.md");
5615
5774
  if (!ex(skillFile)) continue;
5616
5775
  try {
5617
5776
  const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
@@ -5659,7 +5818,7 @@ ${SKILLS_INDEX_END}`;
5659
5818
  }
5660
5819
  async function migrateToProfiles() {
5661
5820
  const homeDir = process.env["HOME"] ?? "/tmp";
5662
- const sharedConfigPath = join8(homeDir, ".openclaw", "openclaw.json");
5821
+ const sharedConfigPath = join9(homeDir, ".openclaw", "openclaw.json");
5663
5822
  let sharedConfig;
5664
5823
  try {
5665
5824
  sharedConfig = JSON.parse(readFileSync9(sharedConfigPath, "utf-8"));
@@ -5675,19 +5834,19 @@ async function migrateToProfiles() {
5675
5834
  const codeName = agentEntry["id"];
5676
5835
  if (!codeName) continue;
5677
5836
  if (codeName === "main") continue;
5678
- const profileDir = join8(homeDir, `.openclaw-${codeName}`);
5679
- if (existsSync5(join8(profileDir, "openclaw.json"))) continue;
5837
+ const profileDir = join9(homeDir, `.openclaw-${codeName}`);
5838
+ if (existsSync5(join9(profileDir, "openclaw.json"))) continue;
5680
5839
  log(`Migrating agent '${codeName}' to per-agent profile`);
5681
5840
  if (adapter.seedProfileConfig) {
5682
5841
  adapter.seedProfileConfig(codeName);
5683
5842
  }
5684
- const sharedAuthDir = join8(homeDir, ".openclaw", "agents", codeName, "agent");
5685
- const profileAuthDir = join8(profileDir, "agents", codeName, "agent");
5686
- const authFile = join8(sharedAuthDir, "auth-profiles.json");
5843
+ const sharedAuthDir = join9(homeDir, ".openclaw", "agents", codeName, "agent");
5844
+ const profileAuthDir = join9(profileDir, "agents", codeName, "agent");
5845
+ const authFile = join9(sharedAuthDir, "auth-profiles.json");
5687
5846
  if (existsSync5(authFile)) {
5688
5847
  mkdirSync4(profileAuthDir, { recursive: true });
5689
5848
  const authContent = readFileSync9(authFile, "utf-8");
5690
- writeFileSync4(join8(profileAuthDir, "auth-profiles.json"), authContent);
5849
+ writeFileSync4(join9(profileAuthDir, "auth-profiles.json"), authContent);
5691
5850
  }
5692
5851
  allocatePort(codeName);
5693
5852
  migrated++;
@@ -5725,7 +5884,7 @@ function readGatewayToken(codeName) {
5725
5884
  }
5726
5885
  const homeDir = process.env["HOME"] ?? "/tmp";
5727
5886
  try {
5728
- const cfg = JSON.parse(readFileSync9(join8(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
5887
+ const cfg = JSON.parse(readFileSync9(join9(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
5729
5888
  return cfg?.gateway?.auth?.token;
5730
5889
  } catch {
5731
5890
  return void 0;
@@ -5734,7 +5893,7 @@ function readGatewayToken(codeName) {
5734
5893
  var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
5735
5894
  function isGatewayHung(codeName) {
5736
5895
  const homeDir = process.env["HOME"] ?? "/tmp";
5737
- const jobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5896
+ const jobsPath = join9(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5738
5897
  if (!existsSync5(jobsPath)) return false;
5739
5898
  try {
5740
5899
  const data = JSON.parse(readFileSync9(jobsPath, "utf-8"));
@@ -5770,13 +5929,13 @@ async function ensureGatewayRunning(codeName, adapter) {
5770
5929
  }
5771
5930
  await new Promise((r) => setTimeout(r, 2e3));
5772
5931
  const homeDir = process.env["HOME"] ?? "/tmp";
5773
- const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5932
+ const cronJobsPath = join9(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5774
5933
  clearStaleCronRunState(cronJobsPath);
5775
5934
  } else {
5776
5935
  if (status.port) {
5777
5936
  try {
5778
5937
  const homeDir = process.env["HOME"] ?? "/tmp";
5779
- const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5938
+ const configPath = join9(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5780
5939
  if (existsSync5(configPath)) {
5781
5940
  const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
5782
5941
  if (cfg.gateway?.port !== status.port) {
@@ -5802,7 +5961,7 @@ async function ensureGatewayRunning(codeName, adapter) {
5802
5961
  gatewaysStartedThisCycle.add(codeName);
5803
5962
  try {
5804
5963
  const homeDir = process.env["HOME"] ?? "/tmp";
5805
- const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5964
+ const configPath = join9(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5806
5965
  if (existsSync5(configPath)) {
5807
5966
  const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
5808
5967
  if (!cfg.gateway) cfg.gateway = {};
@@ -5958,7 +6117,7 @@ async function pollCycle() {
5958
6117
  }
5959
6118
  try {
5960
6119
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
5961
- const { collectDiagnostics } = await import("../persistent-session-35PWSTLO.js");
6120
+ const { collectDiagnostics } = await import("../persistent-session-CZQPV267.js");
5962
6121
  const diagCodeNames = [...agentState.persistentSessionAgents];
5963
6122
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
5964
6123
  let tailscaleHostname;
@@ -6045,12 +6204,12 @@ async function pollCycle() {
6045
6204
  const {
6046
6205
  collectResponsivenessProbes,
6047
6206
  getResponsivenessIntervalMs
6048
- } = await import("../responsiveness-probe-MA4M2QM4.js");
6207
+ } = await import("../responsiveness-probe-EW2ZLSZL.js");
6049
6208
  const probeIntervalMs = getResponsivenessIntervalMs();
6050
6209
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
6051
6210
  const probeCodeNames = [...agentState.persistentSessionAgents];
6052
6211
  if (probeCodeNames.length > 0) {
6053
- const { takeAcpxExecFailureCount, creditAcpxExecFailureCount } = await import("../persistent-session-35PWSTLO.js");
6212
+ const { takeAcpxExecFailureCount, creditAcpxExecFailureCount } = await import("../persistent-session-CZQPV267.js");
6054
6213
  const drainedGiveUps = /* @__PURE__ */ new Map();
6055
6214
  const drainedAcpxFailures = /* @__PURE__ */ new Map();
6056
6215
  const probes = collectResponsivenessProbes(probeCodeNames).map((p) => {
@@ -6084,8 +6243,7 @@ async function pollCycle() {
6084
6243
  collectResponsivenessProbes,
6085
6244
  livePendingInboundOldestAgeSeconds,
6086
6245
  deadLetterPendingInbound
6087
- } = await import("../responsiveness-probe-MA4M2QM4.js");
6088
- const { transcriptActivityAgeSeconds } = await import("../daily-session-PNQX5URX.js");
6246
+ } = await import("../responsiveness-probe-EW2ZLSZL.js");
6089
6247
  const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
6090
6248
  const wedgeNow = /* @__PURE__ */ new Date();
6091
6249
  const liveAgents = agentState.persistentSessionAgents;
@@ -6149,6 +6307,21 @@ async function pollCycle() {
6149
6307
  log(
6150
6308
  `[wedge] forced fresh respawn ${detail} \u2192 new session ${newId} (transcript preserved${deadNote})`
6151
6309
  );
6310
+ const wedgeAgentId = agentState.codeNameToAgentId.get(codeName);
6311
+ if (wedgeAgentId) {
6312
+ api.post("/host/wedge-respawn", {
6313
+ agent_id: wedgeAgentId,
6314
+ code_name: codeName,
6315
+ dead_lettered_count: deadLettered,
6316
+ pane_age_seconds: signals.paneActivityAgeSeconds,
6317
+ inbound_age_seconds: signals.pendingInboundOldestAgeSeconds,
6318
+ transcript_age_seconds: transcriptAge
6319
+ }).catch((err) => {
6320
+ log(
6321
+ `[wedge] failed to record respawn event for '${codeName}' (ENG-6265): ${err.message} \u2014 respawn proceeded; CloudWatch metric will under-count this event`
6322
+ );
6323
+ });
6324
+ }
6152
6325
  const inProgressCardIds = (kanbanBoardCache.get(codeName) ?? []).filter((item) => item.status === "in_progress" && isHybridActionable(item)).map((item) => item.id);
6153
6326
  const cardStates = wedgeRestartsByCard.get(codeName) ?? /* @__PURE__ */ new Map();
6154
6327
  const { next, newlyPoisoned } = recordWedgeForCards(
@@ -6299,7 +6472,7 @@ async function pollCycle() {
6299
6472
  }
6300
6473
  killAgentChannelProcesses(prev.codeName, { log });
6301
6474
  freePort(prev.codeName);
6302
- const agentDir = join8(adapter.getAgentDir(prev.codeName), "provision");
6475
+ const agentDir = join9(adapter.getAgentDir(prev.codeName), "provision");
6303
6476
  await cleanupAgentFiles(prev.codeName, agentDir);
6304
6477
  clearAgentCaches(prev.agentId, prev.codeName);
6305
6478
  }
@@ -6385,10 +6558,10 @@ async function pollCycle() {
6385
6558
  // pending-inbound marker. Best-effort: a write failure is logged by
6386
6559
  // the watchdog, never fails the poll cycle.
6387
6560
  signalGiveUp: (codeName) => {
6388
- const dir = join8(homedir4(), ".augmented", codeName);
6561
+ const dir = join9(homedir5(), ".augmented", codeName);
6389
6562
  if (!existsSync5(dir)) return;
6390
6563
  atomicWriteFileSync(
6391
- join8(dir, "watchdog-give-up.json"),
6564
+ join9(dir, "watchdog-give-up.json"),
6392
6565
  JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
6393
6566
  );
6394
6567
  }
@@ -6509,7 +6682,7 @@ async function processAgent(agent, agentStates) {
6509
6682
  }
6510
6683
  const now = (/* @__PURE__ */ new Date()).toISOString();
6511
6684
  const adapter = resolveAgentFramework(agent.code_name);
6512
- let agentDir = join8(adapter.getAgentDir(agent.code_name), "provision");
6685
+ let agentDir = join9(adapter.getAgentDir(agent.code_name), "provision");
6513
6686
  if (agent.status === "draft" || agent.status === "paused") {
6514
6687
  if (previousKnownStatus !== agent.status) {
6515
6688
  log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
@@ -6713,7 +6886,7 @@ async function processAgent(agent, agentStates) {
6713
6886
  const frameworkId = refreshData.agent.framework ?? "openclaw";
6714
6887
  agentFrameworkCache.set(agent.code_name, frameworkId);
6715
6888
  const frameworkAdapter = getFramework(frameworkId);
6716
- agentDir = join8(frameworkAdapter.getAgentDir(agent.code_name), "provision");
6889
+ agentDir = join9(frameworkAdapter.getAgentDir(agent.code_name), "provision");
6717
6890
  cacheAgentDeliveryMetadata(agent.code_name, refreshData);
6718
6891
  if (frameworkAdapter.migrateSecretStorage && !migratedSecretStorage.has(agent.code_name)) {
6719
6892
  try {
@@ -6756,7 +6929,7 @@ async function processAgent(agent, agentStates) {
6756
6929
  const changedFiles = [];
6757
6930
  mkdirSync4(agentDir, { recursive: true });
6758
6931
  for (const artifact of artifacts) {
6759
- const filePath = join8(agentDir, artifact.relativePath);
6932
+ const filePath = join9(agentDir, artifact.relativePath);
6760
6933
  let existingHash;
6761
6934
  let newHash;
6762
6935
  let writeContent = artifact.content;
@@ -6775,7 +6948,7 @@ async function processAgent(agent, agentStates) {
6775
6948
  };
6776
6949
  newHash = sha256(stripDynamicSections(artifact.content));
6777
6950
  try {
6778
- const projectClaudeMd = join8(config.configDir, agent.code_name, "project", "CLAUDE.md");
6951
+ const projectClaudeMd = join9(config.configDir, agent.code_name, "project", "CLAUDE.md");
6779
6952
  const existing = readFileSync9(projectClaudeMd, "utf-8");
6780
6953
  existingHash = sha256(stripDynamicSections(existing));
6781
6954
  } catch {
@@ -6816,12 +6989,12 @@ async function processAgent(agent, agentStates) {
6816
6989
  }
6817
6990
  }
6818
6991
  if (changedFiles.length > 0) {
6819
- const isFirst = !existsSync5(join8(agentDir, "CHARTER.md"));
6992
+ const isFirst = !existsSync5(join9(agentDir, "CHARTER.md"));
6820
6993
  const verb = isFirst ? "Provisioning" : "Updating";
6821
6994
  const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
6822
6995
  log(`${verb} '${agent.code_name}': ${fileNames}`);
6823
6996
  for (const file of changedFiles) {
6824
- const filePath = join8(agentDir, file.relativePath);
6997
+ const filePath = join9(agentDir, file.relativePath);
6825
6998
  mkdirSync4(dirname3(filePath), { recursive: true });
6826
6999
  if (file.relativePath === ".mcp.json") {
6827
7000
  safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
@@ -6830,12 +7003,12 @@ async function processAgent(agent, agentStates) {
6830
7003
  }
6831
7004
  }
6832
7005
  try {
6833
- const provSkillsDir = join8(agentDir, ".claude", "skills");
7006
+ const provSkillsDir = join9(agentDir, ".claude", "skills");
6834
7007
  if (existsSync5(provSkillsDir)) {
6835
7008
  for (const folder of readdirSync4(provSkillsDir)) {
6836
7009
  if (folder.startsWith("knowledge-")) {
6837
7010
  try {
6838
- rmSync2(join8(provSkillsDir, folder), { recursive: true });
7011
+ rmSync2(join9(provSkillsDir, folder), { recursive: true });
6839
7012
  } catch {
6840
7013
  }
6841
7014
  }
@@ -6848,7 +7021,7 @@ async function processAgent(agent, agentStates) {
6848
7021
  const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
6849
7022
  const hashes = /* @__PURE__ */ new Map();
6850
7023
  for (const file of trackedFiles2) {
6851
- const h = hashFile(join8(agentDir, file));
7024
+ const h = hashFile(join9(agentDir, file));
6852
7025
  if (h) hashes.set(file, h);
6853
7026
  }
6854
7027
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -6916,7 +7089,7 @@ async function processAgent(agent, agentStates) {
6916
7089
  if (written && existsSync5(agentDir)) {
6917
7090
  const driftedFiles = [];
6918
7091
  for (const [file, expectedHash] of written) {
6919
- const localHash = hashFile(join8(agentDir, file));
7092
+ const localHash = hashFile(join9(agentDir, file));
6920
7093
  if (localHash && localHash !== expectedHash) {
6921
7094
  driftedFiles.push(file);
6922
7095
  }
@@ -6927,7 +7100,7 @@ async function processAgent(agent, agentStates) {
6927
7100
  try {
6928
7101
  const localHashes = {};
6929
7102
  for (const file of driftedFiles) {
6930
- localHashes[file] = hashFile(join8(agentDir, file));
7103
+ localHashes[file] = hashFile(join9(agentDir, file));
6931
7104
  }
6932
7105
  await api.post("/host/drift", {
6933
7106
  agent_id: agent.agent_id,
@@ -7251,18 +7424,18 @@ async function processAgent(agent, agentStates) {
7251
7424
  if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
7252
7425
  try {
7253
7426
  const agentProvisionDir = agentDir;
7254
- const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
7427
+ const projectDir = join9(homedir5(), ".augmented", agent.code_name, "project");
7255
7428
  mkdirSync4(agentProvisionDir, { recursive: true });
7256
7429
  mkdirSync4(projectDir, { recursive: true });
7257
- const provisionMcpPath = join8(agentProvisionDir, ".mcp.json");
7258
- const projectMcpPath = join8(projectDir, ".mcp.json");
7430
+ const provisionMcpPath = join9(agentProvisionDir, ".mcp.json");
7431
+ const projectMcpPath = join9(projectDir, ".mcp.json");
7259
7432
  let mcpConfig = { mcpServers: {} };
7260
7433
  try {
7261
7434
  mcpConfig = JSON.parse(readFileSync9(provisionMcpPath, "utf-8"));
7262
7435
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
7263
7436
  } catch {
7264
7437
  }
7265
- const localDirectChatChannel = join8(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
7438
+ const localDirectChatChannel = join9(homedir5(), ".augmented", "_mcp", "direct-chat-channel.js");
7266
7439
  const directChatTeamSettings = refreshData.team?.settings;
7267
7440
  const directChatTz = (() => {
7268
7441
  const tz = directChatTeamSettings?.["timezone"];
@@ -7292,7 +7465,7 @@ async function processAgent(agent, agentStates) {
7292
7465
  log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
7293
7466
  }
7294
7467
  }
7295
- const staleChannelsPath = join8(projectDir, ".mcp-channels.json");
7468
+ const staleChannelsPath = join9(projectDir, ".mcp-channels.json");
7296
7469
  if (existsSync5(staleChannelsPath)) {
7297
7470
  try {
7298
7471
  rmSync2(staleChannelsPath, { force: true });
@@ -7357,7 +7530,7 @@ async function processAgent(agent, agentStates) {
7357
7530
  }
7358
7531
  if (process.env.AGT_CONNECTIVITY_PROBE_ENABLED === "true") {
7359
7532
  try {
7360
- const probeProjectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
7533
+ const probeProjectDir = join9(homedir5(), ".augmented", agent.code_name, "project");
7361
7534
  await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
7362
7535
  } catch (err) {
7363
7536
  log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
@@ -7375,8 +7548,8 @@ async function processAgent(agent, agentStates) {
7375
7548
  recordConfigChurnEvent(agent.agent_id, agent.code_name, FLAP_CHANNEL_INTEGRATIONS, intMembership);
7376
7549
  }
7377
7550
  if (intHash !== prevIntHash) {
7378
- const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
7379
- const envIntPath = join8(projectDir, ".env.integrations");
7551
+ const projectDir = join9(homedir5(), ".augmented", agent.code_name, "project");
7552
+ const envIntPath = join9(projectDir, ".env.integrations");
7380
7553
  let preWriteEnv;
7381
7554
  try {
7382
7555
  preWriteEnv = readFileSync9(envIntPath, "utf-8");
@@ -7391,7 +7564,7 @@ async function processAgent(agent, agentStates) {
7391
7564
  let rotationHandled = true;
7392
7565
  if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
7393
7566
  try {
7394
- const projectMcpPath = join8(projectDir, ".mcp.json");
7567
+ const projectMcpPath = join9(projectDir, ".mcp.json");
7395
7568
  const postWriteEnv = readFileSync9(envIntPath, "utf-8");
7396
7569
  const mcpContent = readFileSync9(projectMcpPath, "utf-8");
7397
7570
  const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
@@ -7575,7 +7748,7 @@ async function processAgent(agent, agentStates) {
7575
7748
  if (agent.status === "active") {
7576
7749
  if (frameworkAdapter.installPlugin) {
7577
7750
  try {
7578
- const pluginPath = join8(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
7751
+ const pluginPath = join9(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
7579
7752
  if (existsSync5(pluginPath)) {
7580
7753
  frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
7581
7754
  agtHost: requireHost(),
@@ -7642,18 +7815,18 @@ async function processAgent(agent, agentStates) {
7642
7815
  }
7643
7816
  try {
7644
7817
  const { readdirSync: readdirSync5, rmSync: rmSync3 } = await import("fs");
7645
- const { homedir: homedir5 } = await import("os");
7818
+ const { homedir: homedir6 } = await import("os");
7646
7819
  const frameworkId2 = frameworkAdapter.id;
7647
7820
  const candidateSkillDirs = [
7648
7821
  // Claude Code — framework runtime tree
7649
- join8(homedir5(), ".augmented", agent.code_name, "skills"),
7822
+ join9(homedir6(), ".augmented", agent.code_name, "skills"),
7650
7823
  // Claude Code — project tree
7651
- join8(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
7824
+ join9(homedir6(), ".augmented", agent.code_name, "project", ".claude", "skills"),
7652
7825
  // OpenClaw — framework runtime tree
7653
- join8(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
7826
+ join9(homedir6(), `.openclaw-${agent.code_name}`, "skills"),
7654
7827
  // Defensive: legacy provision-side path, not currently an
7655
7828
  // install target but cheap to sweep.
7656
- join8(agentDir, ".claude", "skills")
7829
+ join9(agentDir, ".claude", "skills")
7657
7830
  ];
7658
7831
  const existingDirs = candidateSkillDirs.filter((d) => existsSync5(d));
7659
7832
  const discoveredEntries = /* @__PURE__ */ new Set();
@@ -7669,7 +7842,7 @@ async function processAgent(agent, agentStates) {
7669
7842
  }
7670
7843
  const removeSkillFolder = (entry, reason) => {
7671
7844
  for (const dir of existingDirs) {
7672
- const p = join8(dir, entry);
7845
+ const p = join9(dir, entry);
7673
7846
  if (existsSync5(p)) {
7674
7847
  rmSync3(p, { recursive: true, force: true });
7675
7848
  }
@@ -7849,7 +8022,7 @@ async function processAgent(agent, agentStates) {
7849
8022
  const sess = getSessionState(agent.code_name);
7850
8023
  let mcpJsonParsed = null;
7851
8024
  try {
7852
- const mcpPath = join8(getProjectDir(agent.code_name), ".mcp.json");
8025
+ const mcpPath = join9(getProjectDir(agent.code_name), ".mcp.json");
7853
8026
  mcpJsonParsed = JSON.parse(readFileSync9(mcpPath, "utf-8"));
7854
8027
  } catch {
7855
8028
  }
@@ -7868,7 +8041,19 @@ async function processAgent(agent, agentStates) {
7868
8041
  // isolated and the agent keeps running degraded instead of being
7869
8042
  // paused wholesale. reaperRestartBreakerReason() encodes that
7870
8043
  // single-vs-multi decision; undefined means "restart, don't count".
7871
- stopSession: (codeName, ctx) => stopPersistentSessionAndForgetMcpBaseline(codeName, reaperRestartBreakerReason(ctx.activeKeys)),
8044
+ //
8045
+ // ENG-6264: the breaker-count reason (above, undefined for a single dead
8046
+ // MCP) is decoupled from the GATE reason. Pre-6264 an undefined breaker
8047
+ // reason also made the restart non-gateable → 'bypass' → the session was
8048
+ // torn down mid-turn (the common single-MCP case interrupted busy
8049
+ // agents). Always pass 'mcp-presence-reaper' as the gate reason so the
8050
+ // restart defers-until-idle, while breakerReason still governs whether it
8051
+ // counts against the breaker.
8052
+ stopSession: (codeName, ctx) => stopPersistentSessionAndForgetMcpBaseline(
8053
+ codeName,
8054
+ reaperRestartBreakerReason(ctx.activeKeys),
8055
+ "mcp-presence-reaper"
8056
+ ),
7872
8057
  // ENG-5292: when the reaper gives up on a managed MCP (cap from
7873
8058
  // ENG-5279 + state-preservation from ENG-5285 both said "this
7874
8059
  // MCP keeps failing after 3 restart cycles"), mark the matching
@@ -8047,7 +8232,7 @@ async function processAgent(agent, agentStates) {
8047
8232
  lastWorkTriggerAt.set(agent.code_name, triggerTs);
8048
8233
  if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
8049
8234
  const homeDir = process.env["HOME"] ?? "/tmp";
8050
- const jobsPath = join8(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
8235
+ const jobsPath = join9(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
8051
8236
  if (existsSync5(jobsPath)) {
8052
8237
  try {
8053
8238
  const jobsData = JSON.parse(readFileSync9(jobsPath, "utf-8"));
@@ -8187,7 +8372,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
8187
8372
  if (trackedFiles.length > 0 && existsSync5(agentDir)) {
8188
8373
  const hashes = /* @__PURE__ */ new Map();
8189
8374
  for (const file of trackedFiles) {
8190
- const h = hashFile(join8(agentDir, file));
8375
+ const h = hashFile(join9(agentDir, file));
8191
8376
  if (h) hashes.set(file, h);
8192
8377
  }
8193
8378
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -8221,16 +8406,16 @@ function cleanupStaleSessions(codeName) {
8221
8406
  lastCleanupAt.set(codeName, Date.now());
8222
8407
  const homeDir = process.env["HOME"] ?? "/tmp";
8223
8408
  for (const agentDir of ["main", codeName]) {
8224
- const sessionsDir = join8(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
8409
+ const sessionsDir = join9(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
8225
8410
  cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
8226
8411
  }
8227
- const cronRunsDir = join8(homeDir, `.openclaw-${codeName}`, "cron", "runs");
8412
+ const cronRunsDir = join9(homeDir, `.openclaw-${codeName}`, "cron", "runs");
8228
8413
  cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
8229
- const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
8414
+ const cronJobsPath = join9(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
8230
8415
  clearStaleCronRunState(cronJobsPath);
8231
8416
  }
8232
8417
  function cleanupCronSessions(sessionsDir, keepCount) {
8233
- const indexPath = join8(sessionsDir, "sessions.json");
8418
+ const indexPath = join9(sessionsDir, "sessions.json");
8234
8419
  if (!existsSync5(indexPath)) return;
8235
8420
  try {
8236
8421
  const raw = readFileSync9(indexPath, "utf-8");
@@ -8246,7 +8431,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
8246
8431
  for (const entry of toDelete) {
8247
8432
  delete index[entry.key];
8248
8433
  if (entry.sessionId) {
8249
- const sessionFile = join8(sessionsDir, `${entry.sessionId}.jsonl`);
8434
+ const sessionFile = join9(sessionsDir, `${entry.sessionId}.jsonl`);
8250
8435
  try {
8251
8436
  if (existsSync5(sessionFile)) {
8252
8437
  unlinkSync(sessionFile);
@@ -8268,7 +8453,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
8268
8453
  delete index[parentKey];
8269
8454
  if (parentSessionId) {
8270
8455
  try {
8271
- const f = join8(sessionsDir, `${parentSessionId}.jsonl`);
8456
+ const f = join9(sessionsDir, `${parentSessionId}.jsonl`);
8272
8457
  if (existsSync5(f)) {
8273
8458
  unlinkSync(f);
8274
8459
  deletedFiles++;
@@ -8326,7 +8511,7 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
8326
8511
  try {
8327
8512
  for (const f of readdirSync4(dir)) {
8328
8513
  if (!f.endsWith(ext)) continue;
8329
- const fullPath = join8(dir, f);
8514
+ const fullPath = join9(dir, f);
8330
8515
  try {
8331
8516
  const st = statSync3(fullPath);
8332
8517
  if (st.mtimeMs < cutoff) {
@@ -8366,7 +8551,7 @@ var inFlightClaudeTasks = /* @__PURE__ */ new Set();
8366
8551
  var claudeTaskConcurrency = /* @__PURE__ */ new Map();
8367
8552
  var MAX_CLAUDE_CONCURRENCY = 2;
8368
8553
  function claudePidFilePath() {
8369
- return join8(homedir4(), ".augmented", "manager-claude-pids.json");
8554
+ return join9(homedir5(), ".augmented", "manager-claude-pids.json");
8370
8555
  }
8371
8556
  var inFlightClaudePids = /* @__PURE__ */ new Map();
8372
8557
  function registerClaudeSpawn(record) {
@@ -8706,7 +8891,7 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
8706
8891
  }
8707
8892
  async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
8708
8893
  const projectDir = getProjectDir2(codeName);
8709
- const mcpConfigPath = join8(projectDir, ".mcp.json");
8894
+ const mcpConfigPath = join9(projectDir, ".mcp.json");
8710
8895
  let runId = null;
8711
8896
  let kanbanItemId = null;
8712
8897
  let taskResult;
@@ -8714,7 +8899,7 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
8714
8899
  const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
8715
8900
  prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
8716
8901
  try {
8717
- const claudeMdPath = join8(projectDir, "CLAUDE.md");
8902
+ const claudeMdPath = join9(projectDir, "CLAUDE.md");
8718
8903
  const serverNames = [];
8719
8904
  if (existsSync5(mcpConfigPath)) {
8720
8905
  try {
@@ -8741,7 +8926,7 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
8741
8926
  claudeArgs.push("--system-prompt-file", claudeMdPath);
8742
8927
  }
8743
8928
  const childEnv = { ...process.env };
8744
- const envIntPath = join8(projectDir, ".env.integrations");
8929
+ const envIntPath = join9(projectDir, ".env.integrations");
8745
8930
  if (existsSync5(envIntPath)) {
8746
8931
  try {
8747
8932
  Object.assign(childEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
@@ -8916,8 +9101,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
8916
9101
  async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
8917
9102
  const codeName = agent.code_name;
8918
9103
  const projectDir = getProjectDir(codeName);
8919
- const mcpConfigPath = join8(projectDir, ".mcp.json");
8920
- const claudeMdPath = join8(projectDir, "CLAUDE.md");
9104
+ const mcpConfigPath = join9(projectDir, ".mcp.json");
9105
+ const claudeMdPath = join9(projectDir, "CLAUDE.md");
8921
9106
  if (restartBreaker.isTripped(codeName)) {
8922
9107
  const trip = restartBreaker.getTrip(codeName);
8923
9108
  return {
@@ -9087,6 +9272,11 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash3("sha256").
9087
9272
  } catch (err) {
9088
9273
  log(`[persistent-session] Failed to provision auto-progress hook for '${codeName}': ${err.message}`);
9089
9274
  }
9275
+ try {
9276
+ provisionSessionStateHook(codeName);
9277
+ } catch (err) {
9278
+ log(`[persistent-session] Failed to provision session-state hook for '${codeName}': ${err.message}`);
9279
+ }
9090
9280
  const sessionRunResult = await startRun({
9091
9281
  agent_id: agent.agent_id,
9092
9282
  source_type: "system",
@@ -9546,7 +9736,7 @@ ${escapeXml(msg.content)}
9546
9736
  log(`[direct-chat] One-shot spawn for '${agent.codeName}' (msg=${msg.id}; host in-flight=${directChatSpawnGate.hostInFlight}, queued=${directChatSpawnGate.queuedCount})`);
9547
9737
  const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
9548
9738
  const projDir = ccProjectDir(agent.codeName);
9549
- const mcpConfigPath = join8(projDir, ".mcp.json");
9739
+ const mcpConfigPath = join9(projDir, ".mcp.json");
9550
9740
  const serverNames = [];
9551
9741
  if (existsSync5(mcpConfigPath)) {
9552
9742
  try {
@@ -9569,11 +9759,11 @@ ${escapeXml(msg.content)}
9569
9759
  "--allowedTools",
9570
9760
  allowedTools
9571
9761
  ];
9572
- const chatClaudeMd = join8(projDir, "CLAUDE.md");
9762
+ const chatClaudeMd = join9(projDir, "CLAUDE.md");
9573
9763
  if (existsSync5(chatClaudeMd)) {
9574
9764
  chatArgs.push("--system-prompt-file", chatClaudeMd);
9575
9765
  }
9576
- const envIntPath = join8(projDir, ".env.integrations");
9766
+ const envIntPath = join9(projDir, ".env.integrations");
9577
9767
  const childEnv = { ...process.env };
9578
9768
  if (existsSync5(envIntPath)) {
9579
9769
  try {
@@ -9955,8 +10145,8 @@ function getBuiltInSkillContent(skillId) {
9955
10145
  if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
9956
10146
  try {
9957
10147
  const candidates = [
9958
- join8(process.cwd(), "skills", skillId, "SKILL.md"),
9959
- join8(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
10148
+ join9(process.cwd(), "skills", skillId, "SKILL.md"),
10149
+ join9(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
9960
10150
  ];
9961
10151
  for (const candidate of candidates) {
9962
10152
  if (existsSync5(candidate)) {
@@ -10615,7 +10805,7 @@ async function processClaudePairSessions(agents) {
10615
10805
  killPairSession,
10616
10806
  pairTmuxSession,
10617
10807
  finalizeClaudePairOnboarding
10618
- } = await import("../claude-pair-runtime-GIUCD7IG.js");
10808
+ } = await import("../claude-pair-runtime-AYMNAIYY.js");
10619
10809
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
10620
10810
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
10621
10811
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -10676,11 +10866,11 @@ async function processClaudePairSessions(agents) {
10676
10866
  });
10677
10867
  } else {
10678
10868
  const errKind = result.error.kind;
10679
- const errMessage = "message" in result.error ? result.error.message : void 0;
10869
+ const errMessage2 = "message" in result.error ? result.error.message : void 0;
10680
10870
  await reportAndCleanup(session.pair_id, {
10681
10871
  status: errKind === "no-session" ? "session_missing" : "failure",
10682
10872
  error_code: errKind,
10683
- error_message: errMessage
10873
+ error_message: errMessage2
10684
10874
  });
10685
10875
  }
10686
10876
  } else if (session.status === "code_submitted" && session.code) {
@@ -10709,11 +10899,11 @@ async function processClaudePairSessions(agents) {
10709
10899
  });
10710
10900
  } else {
10711
10901
  const errKind = result.error.kind;
10712
- const errMessage = "message" in result.error ? result.error.message : void 0;
10902
+ const errMessage2 = "message" in result.error ? result.error.message : void 0;
10713
10903
  await reportAndCleanup(session.pair_id, {
10714
10904
  status: errKind === "no-session" ? "session_missing" : "failure",
10715
10905
  error_code: errKind,
10716
- error_message: errMessage
10906
+ error_message: errMessage2
10717
10907
  });
10718
10908
  }
10719
10909
  }
@@ -10925,8 +11115,8 @@ function parseMemoryFile(raw, fallbackName) {
10925
11115
  };
10926
11116
  }
10927
11117
  async function syncMemories(agent, configDir, log2) {
10928
- const projectDir = join8(configDir, agent.code_name, "project");
10929
- const memoryDir = join8(projectDir, "memory");
11118
+ const projectDir = join9(configDir, agent.code_name, "project");
11119
+ const memoryDir = join9(projectDir, "memory");
10930
11120
  const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
10931
11121
  if (isFreshSync) {
10932
11122
  log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
@@ -10944,7 +11134,7 @@ async function syncMemories(agent, configDir, log2) {
10944
11134
  for (const file of readdirSync4(memoryDir)) {
10945
11135
  if (!file.endsWith(".md")) continue;
10946
11136
  try {
10947
- const raw = readFileSync9(join8(memoryDir, file), "utf-8");
11137
+ const raw = readFileSync9(join9(memoryDir, file), "utf-8");
10948
11138
  const fileHash = createHash3("sha256").update(raw).digest("hex").slice(0, 16);
10949
11139
  currentHashes.set(file, fileHash);
10950
11140
  if (prevHashes.get(file) === fileHash) continue;
@@ -10969,7 +11159,7 @@ async function syncMemories(agent, configDir, log2) {
10969
11159
  } catch (err) {
10970
11160
  for (const mem of changedMemories) {
10971
11161
  for (const [file] of currentHashes) {
10972
- const parsed = parseMemoryFile(readFileSync9(join8(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
11162
+ const parsed = parseMemoryFile(readFileSync9(join9(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
10973
11163
  if (parsed?.name === mem.name) currentHashes.delete(file);
10974
11164
  }
10975
11165
  }
@@ -11004,7 +11194,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
11004
11194
  const mem = dbMemories.memories[i];
11005
11195
  const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
11006
11196
  const slug = rawSlug || `memory-${i}`;
11007
- const filePath = join8(memoryDir, `${slug}.md`);
11197
+ const filePath = join9(memoryDir, `${slug}.md`);
11008
11198
  const desired = `---
11009
11199
  name: ${JSON.stringify(mem.name)}
11010
11200
  type: ${mem.type}
@@ -11125,6 +11315,43 @@ function stopCaffeinate() {
11125
11315
  log("caffeinate stopped");
11126
11316
  }
11127
11317
  }
11318
+ var artifactScanners = /* @__PURE__ */ new Map();
11319
+ var artifactStreamingInFlight = false;
11320
+ async function driveArtifactStreaming() {
11321
+ if (!isArtifactStreamingEnabled()) return;
11322
+ if (artifactStreamingInFlight) return;
11323
+ artifactStreamingInFlight = true;
11324
+ try {
11325
+ await driveArtifactStreamingInner();
11326
+ } finally {
11327
+ artifactStreamingInFlight = false;
11328
+ }
11329
+ }
11330
+ async function driveArtifactStreamingInner() {
11331
+ const liveIds = /* @__PURE__ */ new Set();
11332
+ for (const agent of state6.agents) {
11333
+ if (!agent.agentId || !agent.codeName || agent.status !== "active") continue;
11334
+ liveIds.add(agent.agentId);
11335
+ let scanner = artifactScanners.get(agent.agentId);
11336
+ if (!scanner) {
11337
+ scanner = new ArtifactStreamScanner(
11338
+ agent.agentId,
11339
+ artifactsDirFor(agent.codeName),
11340
+ nodeArtifactFs,
11341
+ { api, log }
11342
+ );
11343
+ artifactScanners.set(agent.agentId, scanner);
11344
+ }
11345
+ try {
11346
+ await scanner.scanOnce();
11347
+ } catch (err) {
11348
+ log(`[artifact-stream] scan failed for ${agent.codeName}: ${err.message}`);
11349
+ }
11350
+ }
11351
+ for (const id of artifactScanners.keys()) {
11352
+ if (!liveIds.has(id)) artifactScanners.delete(id);
11353
+ }
11354
+ }
11128
11355
  function startPolling() {
11129
11356
  if (!config || running) return;
11130
11357
  running = true;
@@ -11136,6 +11363,7 @@ function startPolling() {
11136
11363
  startGatewayPool();
11137
11364
  return pollCycle();
11138
11365
  }).then(() => {
11366
+ void driveArtifactStreaming();
11139
11367
  scheduleNext();
11140
11368
  });
11141
11369
  }
@@ -11158,7 +11386,10 @@ function scheduleNext() {
11158
11386
  restartNow();
11159
11387
  return;
11160
11388
  }
11161
- void pollCycle().then(scheduleNext);
11389
+ void pollCycle().then(() => {
11390
+ void driveArtifactStreaming();
11391
+ scheduleNext();
11392
+ });
11162
11393
  }, delayMs);
11163
11394
  }
11164
11395
  async function killAllAgtTmuxSessions() {
@@ -11251,7 +11482,7 @@ function startManager(opts) {
11251
11482
  log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
11252
11483
  }
11253
11484
  log(
11254
- `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join8(homedir4(), ".augmented", "manager.log")}`
11485
+ `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join9(homedir5(), ".augmented", "manager.log")}`
11255
11486
  );
11256
11487
  deployMcpAssets();
11257
11488
  reapOrphanChannelMcps({ log });
@@ -11382,14 +11613,14 @@ function restartRunningChannelMcps(basenames) {
11382
11613
  }
11383
11614
  }
11384
11615
  function deployMcpAssets() {
11385
- const targetDir = join8(homedir4(), ".augmented", "_mcp");
11616
+ const targetDir = join9(homedir5(), ".augmented", "_mcp");
11386
11617
  mkdirSync4(targetDir, { recursive: true });
11387
11618
  const moduleDir = dirname3(fileURLToPath(import.meta.url));
11388
11619
  let mcpSourceDir = "";
11389
11620
  let dir = moduleDir;
11390
11621
  for (let i = 0; i < 6; i++) {
11391
- const candidate = join8(dir, "dist", "mcp");
11392
- if (existsSync5(join8(candidate, "index.js"))) {
11622
+ const candidate = join9(dir, "dist", "mcp");
11623
+ if (existsSync5(join9(candidate, "index.js"))) {
11393
11624
  mcpSourceDir = candidate;
11394
11625
  break;
11395
11626
  }
@@ -11432,8 +11663,8 @@ function deployMcpAssets() {
11432
11663
  // natural session restart.
11433
11664
  "augmented-admin.js"
11434
11665
  ]) {
11435
- const src = join8(mcpSourceDir, file);
11436
- const dst = join8(targetDir, file);
11666
+ const src = join9(mcpSourceDir, file);
11667
+ const dst = join9(targetDir, file);
11437
11668
  if (!existsSync5(src)) continue;
11438
11669
  const before = fileHash(dst);
11439
11670
  try {
@@ -11451,14 +11682,14 @@ function deployMcpAssets() {
11451
11682
  log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
11452
11683
  restartRunningChannelMcps(changedBasenames);
11453
11684
  }
11454
- const localMcpPath = join8(targetDir, "index.js");
11685
+ const localMcpPath = join9(targetDir, "index.js");
11455
11686
  try {
11456
- const agentsDir = join8(homedir4(), ".augmented", "agents");
11687
+ const agentsDir = join9(homedir5(), ".augmented", "agents");
11457
11688
  if (existsSync5(agentsDir)) {
11458
11689
  for (const entry of readdirSync4(agentsDir, { withFileTypes: true })) {
11459
11690
  if (!entry.isDirectory()) continue;
11460
11691
  for (const subdir of ["provision", "project"]) {
11461
- const mcpJsonPath = join8(agentsDir, entry.name, subdir, ".mcp.json");
11692
+ const mcpJsonPath = join9(agentsDir, entry.name, subdir, ".mcp.json");
11462
11693
  try {
11463
11694
  const raw = readFileSync9(mcpJsonPath, "utf-8");
11464
11695
  if (!raw.includes("@integrity-labs/augmented-mcp")) continue;