@integrity-labs/agt-cli 0.27.150-test.16 → 0.27.150-test.17

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-QHEAAPEG.js";
22
+ } from "../chunk-L2JA4OHU.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,20 @@ import {
49
53
  rotateSessionForWedge,
50
54
  sanitizeMcpJson,
51
55
  sendToAgent,
56
+ sessionTranscriptDir,
52
57
  startPersistentSession,
53
58
  stopAllSessionsAndWait,
54
59
  stopPersistentSession,
60
+ subagentActivityAgeSeconds,
55
61
  takeWatchdogGiveUpCount,
56
- takeZombieDetection
57
- } from "../chunk-7GKJZBTB.js";
62
+ takeZombieDetection,
63
+ transcriptActivityAgeSeconds
64
+ } from "../chunk-HXMLMIR4.js";
58
65
  import {
59
66
  KANBAN_CHECK_COMMAND,
60
67
  MAX_AVATAR_ENV_URL_BYTES,
61
68
  SUPPRESS_SENTINEL,
69
+ StreamEncoder,
62
70
  appendDmFooter,
63
71
  attributeTranscriptUsageByRun,
64
72
  classifyActor,
@@ -83,13 +91,7 @@ import {
83
91
  resolveDmTarget,
84
92
  worseConnectivityOutcome,
85
93
  wrapScheduledTaskPrompt
86
- } from "../chunk-WOOYOAPG.js";
87
- import {
88
- isAgentIdle,
89
- isStaleForToday,
90
- peekCurrentSession,
91
- sessionTranscriptDir
92
- } from "../chunk-354FAVQR.js";
94
+ } from "../chunk-FZTGR2AQ.js";
93
95
  import {
94
96
  parsePsRows,
95
97
  reapOrphanChannelMcps
@@ -100,8 +102,8 @@ import { createHash as createHash3 } from "crypto";
100
102
  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
103
  import https from "https";
102
104
  import { execFileSync as syncExecFile } from "child_process";
103
- import { join as join8, dirname as dirname3 } from "path";
104
- import { homedir as homedir4 } from "os";
105
+ import { join as join9, dirname as dirname3 } from "path";
106
+ import { homedir as homedir5 } from "os";
105
107
  import { fileURLToPath } from "url";
106
108
 
107
109
  // src/lib/mcp-config-drift.ts
@@ -1515,6 +1517,7 @@ function isUrgentUpgrade(opts) {
1515
1517
  }
1516
1518
  var RESTART_IDLE_THRESHOLD_SECONDS = 120;
1517
1519
  var RESTART_INBOUND_QUIET_SECONDS = 300;
1520
+ var RESTART_TRANSCRIPT_STALE_SECONDS = 60;
1518
1521
  var GATEABLE_RESTART_REASONS = /* @__PURE__ */ new Set([
1519
1522
  "model-change",
1520
1523
  "channel-set-change",
@@ -1533,9 +1536,11 @@ function decideRestartGate(opts) {
1533
1536
  }
1534
1537
  const paneThreshold = opts.idleThresholdSeconds ?? RESTART_IDLE_THRESHOLD_SECONDS;
1535
1538
  const inboundThreshold = opts.inboundQuietSeconds ?? RESTART_INBOUND_QUIET_SECONDS;
1536
- const paneBusy = opts.paneLogAgeSeconds !== null && opts.paneLogAgeSeconds < paneThreshold;
1539
+ const transcriptThreshold = opts.transcriptStaleSeconds ?? RESTART_TRANSCRIPT_STALE_SECONDS;
1540
+ const transcriptAge = opts.transcriptAgeSeconds ?? null;
1541
+ const progressBusy = transcriptAge !== null ? transcriptAge < transcriptThreshold : opts.paneLogAgeSeconds !== null && opts.paneLogAgeSeconds < paneThreshold;
1537
1542
  const inboundBusy = opts.inboundAgeSeconds !== null && opts.inboundAgeSeconds < inboundThreshold;
1538
- if (paneBusy || inboundBusy) return "defer-idle";
1543
+ if (progressBusy || inboundBusy) return "defer-idle";
1539
1544
  return "proceed";
1540
1545
  }
1541
1546
 
@@ -1682,9 +1687,146 @@ var DirectChatSpawnGate = class {
1682
1687
  }
1683
1688
  };
1684
1689
 
1690
+ // src/lib/artifact-stream.ts
1691
+ import { join as join2 } from "path";
1692
+ import { homedir } from "os";
1693
+ import { readdir, stat, readFile } from "fs/promises";
1694
+ var ARTEFACT_ENTRY_FILE = "index.html";
1695
+ function errMessage(err) {
1696
+ return err instanceof Error ? err.message : String(err);
1697
+ }
1698
+ var ArtifactStreamSession = class {
1699
+ constructor(agentId, clientToken, deps, meta = {}) {
1700
+ this.agentId = agentId;
1701
+ this.clientToken = clientToken;
1702
+ this.deps = deps;
1703
+ this.meta = meta;
1704
+ this.encoder = new StreamEncoder(deps.now ? { now: deps.now } : {});
1705
+ }
1706
+ encoder;
1707
+ draftId = null;
1708
+ creating = null;
1709
+ /** The draft id once created, else null. */
1710
+ get id() {
1711
+ return this.draftId;
1712
+ }
1713
+ /**
1714
+ * Create the draft on first use (idempotent via client_token — a manager
1715
+ * restart returns the same draft). Concurrent callers share one in-flight
1716
+ * request. Returns null on failure (best-effort; the next snapshot retries).
1717
+ */
1718
+ async ensureDraft() {
1719
+ if (this.draftId) return this.draftId;
1720
+ if (!this.creating) {
1721
+ this.creating = this.deps.api.post("/host/artifacts/draft", {
1722
+ agent_id: this.agentId,
1723
+ client_token: this.clientToken,
1724
+ kind: this.meta.kind ?? "other",
1725
+ ...this.meta.title ? { title: this.meta.title } : {}
1726
+ }).then((res) => {
1727
+ this.draftId = res?.id ?? null;
1728
+ return this.draftId;
1729
+ }).catch((err) => {
1730
+ this.deps.log?.(`[artifact-stream] draft create failed (${this.clientToken}): ${errMessage(err)}`);
1731
+ return null;
1732
+ }).finally(() => {
1733
+ this.creating = null;
1734
+ });
1735
+ }
1736
+ return this.creating;
1737
+ }
1738
+ /** Encode one snapshot and relay its frame(s). Best-effort, never throws. */
1739
+ async pushSnapshot(content) {
1740
+ const id = await this.ensureDraft();
1741
+ if (!id) return;
1742
+ const frames = this.encoder.encode(content);
1743
+ for (const frame of frames) {
1744
+ try {
1745
+ await this.deps.api.post(`/host/artifacts/${encodeURIComponent(id)}/stream`, {
1746
+ agent_id: this.agentId,
1747
+ frame
1748
+ });
1749
+ } catch (err) {
1750
+ this.deps.log?.(`[artifact-stream] frame ${frame.seq} failed (${id}): ${errMessage(err)}`);
1751
+ }
1752
+ }
1753
+ }
1754
+ };
1755
+ var ArtifactStreamScanner = class {
1756
+ constructor(agentId, artifactsDir, fsDeps, deps) {
1757
+ this.agentId = agentId;
1758
+ this.artifactsDir = artifactsDir;
1759
+ this.fsDeps = fsDeps;
1760
+ this.deps = deps;
1761
+ }
1762
+ sessions = /* @__PURE__ */ new Map();
1763
+ seenMtime = /* @__PURE__ */ new Map();
1764
+ scanning = false;
1765
+ /** One scan pass. Reads + streams every changed artefact. Never throws. */
1766
+ async scanOnce() {
1767
+ if (this.scanning) return;
1768
+ this.scanning = true;
1769
+ try {
1770
+ let names;
1771
+ try {
1772
+ names = await this.fsDeps.listArtefactNames(this.artifactsDir);
1773
+ } catch {
1774
+ return;
1775
+ }
1776
+ for (const name of names) {
1777
+ const file = join2(this.artifactsDir, name, ARTEFACT_ENTRY_FILE);
1778
+ const mtime = await this.fsDeps.mtimeMs(file).catch(() => null);
1779
+ if (mtime === null) continue;
1780
+ if (this.seenMtime.get(name) === mtime) continue;
1781
+ let content;
1782
+ try {
1783
+ content = await this.fsDeps.readFile(file);
1784
+ } catch (err) {
1785
+ this.deps.log?.(`[artifact-stream] read failed (${name}): ${errMessage(err)}`);
1786
+ continue;
1787
+ }
1788
+ this.seenMtime.set(name, mtime);
1789
+ await this.sessionFor(name).pushSnapshot(content);
1790
+ }
1791
+ const live = new Set(names);
1792
+ for (const key of this.seenMtime.keys()) if (!live.has(key)) this.seenMtime.delete(key);
1793
+ for (const key of this.sessions.keys()) if (!live.has(key)) this.sessions.delete(key);
1794
+ } finally {
1795
+ this.scanning = false;
1796
+ }
1797
+ }
1798
+ sessionFor(name) {
1799
+ let session = this.sessions.get(name);
1800
+ if (!session) {
1801
+ session = new ArtifactStreamSession(this.agentId, name, this.deps, { title: name });
1802
+ this.sessions.set(name, session);
1803
+ }
1804
+ return session;
1805
+ }
1806
+ };
1807
+ function artifactsDirFor(codeName) {
1808
+ return join2(homedir(), ".augmented", codeName, "artifacts");
1809
+ }
1810
+ var nodeArtifactFs = {
1811
+ async listArtefactNames(artifactsDir) {
1812
+ const entries = await readdir(artifactsDir, { withFileTypes: true });
1813
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
1814
+ },
1815
+ async mtimeMs(filePath) {
1816
+ try {
1817
+ return (await stat(filePath)).mtimeMs;
1818
+ } catch {
1819
+ return null;
1820
+ }
1821
+ },
1822
+ readFile(filePath) {
1823
+ return readFile(filePath, "utf8");
1824
+ }
1825
+ };
1826
+
1685
1827
  // src/lib/usage-banner-monitor.ts
1686
1828
  var MIN_CHECK_INTERVAL_MS = 6e4;
1687
- var PANE_TAIL_LINES_FOR_BANNER = 200;
1829
+ var PANE_TAIL_LINES_FOR_BANNER = 5e3;
1688
1830
  var state = /* @__PURE__ */ new Map();
1689
1831
  async function maybeReportUsageBanner(args) {
1690
1832
  const { api: api2, codeName, agentId, log: log2 } = args;
@@ -1739,7 +1881,7 @@ async function maybeReportUsageBanner(args) {
1739
1881
 
1740
1882
  // src/lib/token-usage-monitor.ts
1741
1883
  import { readdirSync, readFileSync as readFileSync4, statSync } from "fs";
1742
- import { join as join2 } from "path";
1884
+ import { join as join3 } from "path";
1743
1885
  var MIN_CHECK_INTERVAL_MS2 = 6e4;
1744
1886
  var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
1745
1887
  var MAX_ENTRIES_PER_POST = 200;
@@ -1768,7 +1910,7 @@ async function maybeReportTokenUsage(args) {
1768
1910
  if (!name.endsWith(".jsonl")) continue;
1769
1911
  const sessionId = name.slice(0, -".jsonl".length);
1770
1912
  if (!sessionId) continue;
1771
- const path = join2(dir, name);
1913
+ const path = join3(dir, name);
1772
1914
  let st;
1773
1915
  try {
1774
1916
  st = statSync(path);
@@ -1866,7 +2008,7 @@ async function maybeReportTokenUsage(args) {
1866
2008
 
1867
2009
  // src/lib/conversation-evaluator.ts
1868
2010
  import { readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
1869
- import { join as join3 } from "path";
2011
+ import { join as join4 } from "path";
1870
2012
  var MIN_CHECK_INTERVAL_MS3 = 5 * 6e4;
1871
2013
  var TRANSCRIPT_MTIME_WINDOW_MS2 = 3 * 24 * 60 * 60 * 1e3;
1872
2014
  var WINDOW_PAD_MS = 5 * 6e4;
@@ -2103,7 +2245,7 @@ function readRecentTurns(dir, nowMs) {
2103
2245
  const turns = [];
2104
2246
  for (const name of entries) {
2105
2247
  if (!name.endsWith(".jsonl")) continue;
2106
- const full = join3(dir, name);
2248
+ const full = join4(dir, name);
2107
2249
  let mtimeMs;
2108
2250
  try {
2109
2251
  mtimeMs = statSync2(full).mtimeMs;
@@ -2283,10 +2425,10 @@ async function reportSkip2(api2, agentId, conversationId, log2, codeName) {
2283
2425
 
2284
2426
  // src/lib/activity-cache-monitor.ts
2285
2427
  import { existsSync as existsSync2, readFileSync as readFileSync6 } from "fs";
2286
- import { homedir } from "os";
2287
- import { join as join4 } from "path";
2428
+ import { homedir as homedir2 } from "os";
2429
+ import { join as join5 } from "path";
2288
2430
  var MIN_CHECK_INTERVAL_MS5 = 6e4;
2289
- var STATS_CACHE_PATH = join4(homedir(), ".claude", "stats-cache.json");
2431
+ var STATS_CACHE_PATH = join5(homedir2(), ".claude", "stats-cache.json");
2290
2432
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
2291
2433
  var state5 = { lastObservedDate: null, lastCheckedAt: 0 };
2292
2434
  function selectNewDailyRows(raw, lastObservedDate) {
@@ -2782,9 +2924,9 @@ var GatewayClientPool = class extends EventEmitter {
2782
2924
  };
2783
2925
 
2784
2926
  // 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";
2927
+ import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
2928
+ import { homedir as homedir3, platform } from "os";
2929
+ import { join as join6 } from "path";
2788
2930
  import { execFile as execFile2 } from "child_process";
2789
2931
  import { promisify } from "util";
2790
2932
  var execFileAsync = promisify(execFile2);
@@ -2799,17 +2941,17 @@ async function detectClaudeAuth() {
2799
2941
  }
2800
2942
  async function findClaudeCredentialsPaths() {
2801
2943
  const candidates = [
2802
- join5(homedir2(), ".claude", ".credentials.json"),
2803
- join5(homedir2(), ".claude", "credentials.json")
2944
+ join6(homedir3(), ".claude", ".credentials.json"),
2945
+ join6(homedir3(), ".claude", "credentials.json")
2804
2946
  ];
2805
2947
  const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
2806
2948
  if (isLinuxRoot) {
2807
2949
  try {
2808
- const entries = await readdir("/home", { withFileTypes: true });
2950
+ const entries = await readdir2("/home", { withFileTypes: true });
2809
2951
  for (const entry of entries) {
2810
2952
  if (!entry.isDirectory()) continue;
2811
- candidates.push(join5("/home", entry.name, ".claude", ".credentials.json"));
2812
- candidates.push(join5("/home", entry.name, ".claude", "credentials.json"));
2953
+ candidates.push(join6("/home", entry.name, ".claude", ".credentials.json"));
2954
+ candidates.push(join6("/home", entry.name, ".claude", "credentials.json"));
2813
2955
  }
2814
2956
  } catch {
2815
2957
  }
@@ -2845,7 +2987,7 @@ async function readMacKeychain() {
2845
2987
  }
2846
2988
  async function readJsonSilently(path) {
2847
2989
  try {
2848
- const raw = await readFile(path, "utf-8");
2990
+ const raw = await readFile2(path, "utf-8");
2849
2991
  return JSON.parse(raw);
2850
2992
  } catch {
2851
2993
  return null;
@@ -2902,10 +3044,10 @@ function normalize(value) {
2902
3044
 
2903
3045
  // src/lib/channel-hash-cache.ts
2904
3046
  import { existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
2905
- import { join as join6 } from "path";
3047
+ import { join as join7 } from "path";
2906
3048
  var CACHE_FILENAME = "channel-hash-cache.json";
2907
3049
  function getChannelHashCacheFile(configDir) {
2908
- return join6(configDir, CACHE_FILENAME);
3050
+ return join7(configDir, CACHE_FILENAME);
2909
3051
  }
2910
3052
  function loadChannelHashCache(target, configDir) {
2911
3053
  const path = getChannelHashCacheFile(configDir);
@@ -3377,13 +3519,18 @@ function clearAgentState(agentId, codeName) {
3377
3519
  // src/lib/wedge-detection.ts
3378
3520
  var DEFAULTS = {
3379
3521
  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,
3522
+ // ENG-6264: DISABLED by default (0). A session that's actively producing
3523
+ // tokens is never force-respawned — a working agent must not be killed just
3524
+ // because a message has been queued behind its turn, no matter how long.
3525
+ // ENG-6238 made this an absolute backstop (1200s) to still catch a
3526
+ // producing-but-never-draining runaway loop, but that re-introduced the exact
3527
+ // failure we set out to kill: cutting off real work on a long turn. Runaway
3528
+ // token burn is owned by the cost guardrail (ENG-5556); a producing-but-silent
3529
+ // loop still trips the synthetic-probe alarm. So the backstop is now opt-in:
3530
+ // set AGT_WEDGE_INBOUND_HARD_WAIT_SECONDS to a positive value to re-enable it
3531
+ // (floored at inboundWaitSeconds). 0 = the frozen/hung wedge (transcript
3532
+ // static) is still caught by the soft path; only the *producing* path is spared.
3533
+ inboundHardWaitSeconds: 0,
3387
3534
  paneStaleSeconds: 120,
3388
3535
  transcriptStaleSeconds: 60,
3389
3536
  minCycles: 3
@@ -3402,10 +3549,12 @@ function resolveWedgeConfig(env = process.env) {
3402
3549
  DEFAULTS.inboundWaitSeconds,
3403
3550
  30
3404
3551
  );
3405
- const inboundHardWaitSeconds = Math.max(
3406
- inboundWaitSeconds,
3407
- parsePositiveInt(env.AGT_WEDGE_INBOUND_HARD_WAIT_SECONDS, DEFAULTS.inboundHardWaitSeconds, 30)
3552
+ const inboundHardWaitRaw = parsePositiveInt(
3553
+ env.AGT_WEDGE_INBOUND_HARD_WAIT_SECONDS,
3554
+ DEFAULTS.inboundHardWaitSeconds,
3555
+ 0
3408
3556
  );
3557
+ const inboundHardWaitSeconds = inboundHardWaitRaw <= 0 ? 0 : Math.max(inboundWaitSeconds, inboundHardWaitRaw);
3409
3558
  return {
3410
3559
  mode: parseMode(env.AGT_WEDGE_RESTART_MODE),
3411
3560
  inboundWaitSeconds,
@@ -3420,16 +3569,25 @@ function resolveWedgeConfig(env = process.env) {
3420
3569
  };
3421
3570
  }
3422
3571
  function isSessionProducing(signals, config2) {
3572
+ const subagentAge = signals.subagentActivityAgeSeconds;
3573
+ if (subagentAge !== null && subagentAge < config2.transcriptStaleSeconds) return true;
3423
3574
  const transcriptAge = signals.transcriptActivityAgeSeconds;
3424
3575
  if (transcriptAge !== null) return transcriptAge < config2.transcriptStaleSeconds;
3425
3576
  const paneAge = signals.paneActivityAgeSeconds;
3426
3577
  return paneAge !== null && paneAge < config2.paneStaleSeconds;
3427
3578
  }
3579
+ function wedgeExemptionReason(signals, config2) {
3580
+ if (signals.subagentActivityAgeSeconds === null) return null;
3581
+ if (isWedgeCandidateCycle(signals, config2)) return null;
3582
+ const withoutSubagent = { ...signals, subagentActivityAgeSeconds: null };
3583
+ return isWedgeCandidateCycle(withoutSubagent, config2) ? "background-task-in-flight" : null;
3584
+ }
3428
3585
  function isWedgeCandidateCycle(signals, config2) {
3429
3586
  const inboundAge = signals.pendingInboundOldestAgeSeconds;
3430
3587
  if (inboundAge === null) return false;
3431
3588
  if (inboundAge < config2.inboundWaitSeconds) return false;
3432
3589
  if (isSessionProducing(signals, config2)) {
3590
+ if (config2.inboundHardWaitSeconds <= 0) return false;
3433
3591
  return inboundAge >= config2.inboundHardWaitSeconds;
3434
3592
  }
3435
3593
  return true;
@@ -3498,14 +3656,14 @@ function partitionActionableByPoison(actionable, states, config2) {
3498
3656
 
3499
3657
  // src/lib/restart-flags.ts
3500
3658
  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";
3659
+ import { homedir as homedir4 } from "os";
3660
+ import { join as join8 } from "path";
3503
3661
  import { randomUUID } from "crypto";
3504
3662
  function restartFlagsDir() {
3505
- return join7(homedir3(), ".augmented", "restart-flags");
3663
+ return join8(homedir4(), ".augmented", "restart-flags");
3506
3664
  }
3507
3665
  function flagPath(codeName) {
3508
- return join7(restartFlagsDir(), `${codeName}.flag`);
3666
+ return join8(restartFlagsDir(), `${codeName}.flag`);
3509
3667
  }
3510
3668
  function readRestartFlags() {
3511
3669
  const dir = restartFlagsDir();
@@ -3514,7 +3672,7 @@ function readRestartFlags() {
3514
3672
  for (const entry of readdirSync3(dir)) {
3515
3673
  if (!entry.endsWith(".flag")) continue;
3516
3674
  try {
3517
- const raw = readFileSync8(join7(dir, entry), "utf8");
3675
+ const raw = readFileSync8(join8(dir, entry), "utf8");
3518
3676
  const parsed = JSON.parse(raw);
3519
3677
  if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
3520
3678
  parsed.codeName = entry.replace(/\.flag$/, "");
@@ -4062,8 +4220,8 @@ function applyRestartAcks(args) {
4062
4220
  var GATEWAY_PORT_BASE = 18800;
4063
4221
  var GATEWAY_PORT_STEP = 10;
4064
4222
  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");
4223
+ var AUGMENTED_DIR = join9(process.env["HOME"] ?? "/tmp", ".augmented");
4224
+ var GATEWAY_PORTS_FILE = join9(AUGMENTED_DIR, "gateway-ports.json");
4067
4225
  var CHANNEL_SWEEP_INTERVAL_MS = (() => {
4068
4226
  const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
4069
4227
  if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
@@ -4399,6 +4557,7 @@ var DEFER_LOG_THROTTLE_MS = 6e5;
4399
4557
  var deferLogThrottle = /* @__PURE__ */ new Map();
4400
4558
  var lastInboundMs = /* @__PURE__ */ new Map();
4401
4559
  var consecutiveWedgeCycles = /* @__PURE__ */ new Map();
4560
+ var wedgeExemptionLogged = /* @__PURE__ */ new Set();
4402
4561
  var wedgeRestartsByCard = /* @__PURE__ */ new Map();
4403
4562
  function noteInbound(codeName) {
4404
4563
  lastInboundMs.set(codeName, Date.now());
@@ -4417,11 +4576,16 @@ function paneLogAgeSecondsFor(codeName) {
4417
4576
  return 0;
4418
4577
  }
4419
4578
  }
4420
- function restartGateFor(codeName, breakerReason) {
4421
- if (!isGateableRestartReason(breakerReason)) return "bypass";
4579
+ function transcriptAgeSecondsFor(codeName) {
4580
+ const sessionId = getSessionState(codeName)?.currentSessionId ?? null;
4581
+ return transcriptActivityAgeSeconds(getProjectDir2(codeName), sessionId, /* @__PURE__ */ new Date());
4582
+ }
4583
+ function restartGateFor(codeName, reason) {
4584
+ if (!isGateableRestartReason(reason)) return "bypass";
4422
4585
  return decideRestartGate({
4423
4586
  window: cachedMaintenanceWindow,
4424
4587
  paneLogAgeSeconds: paneLogAgeSecondsFor(codeName),
4588
+ transcriptAgeSeconds: transcriptAgeSecondsFor(codeName),
4425
4589
  inboundAgeSeconds: inboundAgeSecondsFor(codeName),
4426
4590
  now: /* @__PURE__ */ new Date()
4427
4591
  });
@@ -4431,7 +4595,7 @@ var runningMcpServerKeys = /* @__PURE__ */ new Map();
4431
4595
  var runningChannelSecretHashes = /* @__PURE__ */ new Map();
4432
4596
  function projectMcpHash(_codeName, projectDir) {
4433
4597
  try {
4434
- const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
4598
+ const raw = readFileSync9(join9(projectDir, ".mcp.json"), "utf-8");
4435
4599
  return createHash3("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
4436
4600
  } catch {
4437
4601
  return null;
@@ -4439,7 +4603,7 @@ function projectMcpHash(_codeName, projectDir) {
4439
4603
  }
4440
4604
  function projectMcpKeys(_codeName, projectDir) {
4441
4605
  try {
4442
- const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
4606
+ const raw = readFileSync9(join9(projectDir, ".mcp.json"), "utf-8");
4443
4607
  const parsed = JSON.parse(raw);
4444
4608
  const servers = parsed.mcpServers;
4445
4609
  if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
@@ -4450,7 +4614,7 @@ function projectMcpKeys(_codeName, projectDir) {
4450
4614
  }
4451
4615
  function readMcpHttpServerConfig(projectDir, serverKey, env) {
4452
4616
  try {
4453
- const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
4617
+ const raw = readFileSync9(join9(projectDir, ".mcp.json"), "utf-8");
4454
4618
  const servers = JSON.parse(raw).mcpServers ?? {};
4455
4619
  const entry = servers[serverKey];
4456
4620
  if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
@@ -4478,7 +4642,7 @@ async function runAgentConnectivityProbes(agent, integrations, projectDir) {
4478
4642
  if (integrations.length === 0) return;
4479
4643
  const probeEnv = { ...process.env };
4480
4644
  try {
4481
- const envIntPath = join8(projectDir, ".env.integrations");
4645
+ const envIntPath = join9(projectDir, ".env.integrations");
4482
4646
  if (existsSync5(envIntPath)) {
4483
4647
  Object.assign(probeEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
4484
4648
  }
@@ -4580,10 +4744,10 @@ async function runAgentConnectivityProbes(agent, integrations, projectDir) {
4580
4744
  );
4581
4745
  }
4582
4746
  }
4583
- function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason) {
4584
- const gate = restartGateFor(codeName, breakerReason);
4747
+ function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason, gateReason = breakerReason) {
4748
+ const gate = restartGateFor(codeName, gateReason);
4585
4749
  if (gate !== "bypass" && gate !== "proceed") {
4586
- log(`[maintenance-window] Deferring '${breakerReason}' restart for '${codeName}' (${gate})`);
4750
+ log(`[maintenance-window] Deferring '${gateReason}' restart for '${codeName}' (${gate})`);
4587
4751
  return;
4588
4752
  }
4589
4753
  cancelPendingSessionRestart(codeName);
@@ -4668,7 +4832,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
4668
4832
  function projectChannelSecretHash(projectDir) {
4669
4833
  try {
4670
4834
  const entries = parseEnvIntegrations(
4671
- readFileSync9(join8(projectDir, ".env.integrations"), "utf-8")
4835
+ readFileSync9(join9(projectDir, ".env.integrations"), "utf-8")
4672
4836
  );
4673
4837
  return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
4674
4838
  } catch {
@@ -4760,7 +4924,7 @@ var cachedMaintenanceWindow = null;
4760
4924
  var lastVersionCheckAt = 0;
4761
4925
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
4762
4926
  var lastResponsivenessProbeAt = 0;
4763
- var agtCliVersion = true ? "0.27.150-test.16" : "dev";
4927
+ var agtCliVersion = true ? "0.27.150-test.17" : "dev";
4764
4928
  function resolveBrewPath(execFileSync4) {
4765
4929
  try {
4766
4930
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -5011,7 +5175,7 @@ async function ensureFrameworkBinary(frameworkId) {
5011
5175
  var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
5012
5176
  var claudeCodeUpgradeInFlight = false;
5013
5177
  function claudeCodeUpgradeMarkerPath() {
5014
- return join8(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
5178
+ return join9(homedir5(), ".augmented", ".last-claude-code-upgrade-check");
5015
5179
  }
5016
5180
  function stampClaudeCodeUpgradeMarker() {
5017
5181
  try {
@@ -5074,7 +5238,7 @@ ${r.stderr}`;
5074
5238
  }
5075
5239
  var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
5076
5240
  function selfUpdateAppliedMarkerPath() {
5077
- return join8(homedir4(), ".augmented", ".last-self-update-applied");
5241
+ return join9(homedir5(), ".augmented", ".last-self-update-applied");
5078
5242
  }
5079
5243
  var selfUpdateUpToDateLogged = false;
5080
5244
  var restartAfterUpgrade = false;
@@ -5097,7 +5261,7 @@ async function checkAndUpdateCli() {
5097
5261
  const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
5098
5262
  if (!isBrewFormula && !isNpmGlobal) return;
5099
5263
  const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
5100
- const markerPath = join8(homedir4(), ".augmented", ".last-update-check");
5264
+ const markerPath = join9(homedir5(), ".augmented", ".last-update-check");
5101
5265
  try {
5102
5266
  const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
5103
5267
  if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
@@ -5354,9 +5518,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
5354
5518
  throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
5355
5519
  }
5356
5520
  childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
5357
- const claudeDir = join8(homedir4(), ".claude");
5521
+ const claudeDir = join9(homedir5(), ".claude");
5358
5522
  for (const filename of [".credentials.json", "credentials.json"]) {
5359
- const p = join8(claudeDir, filename);
5523
+ const p = join9(claudeDir, filename);
5360
5524
  if (existsSync5(p)) {
5361
5525
  try {
5362
5526
  rmSync2(p, { force: true });
@@ -5372,12 +5536,12 @@ async function applyClaudeAuthToEnv(childEnv, label) {
5372
5536
  var evalEmptyMcpConfigPath = null;
5373
5537
  function ensureEvalEmptyMcpConfig() {
5374
5538
  if (evalEmptyMcpConfigPath && existsSync5(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
5375
- const dir = join8(homedir4(), ".augmented");
5539
+ const dir = join9(homedir5(), ".augmented");
5376
5540
  try {
5377
5541
  mkdirSync4(dir, { recursive: true });
5378
5542
  } catch {
5379
5543
  }
5380
- const p = join8(dir, ".eval-empty-mcp.json");
5544
+ const p = join9(dir, ".eval-empty-mcp.json");
5381
5545
  writeFileSync4(p, JSON.stringify({ mcpServers: {} }));
5382
5546
  evalEmptyMcpConfigPath = p;
5383
5547
  return p;
@@ -5403,7 +5567,7 @@ async function runEvalClaude(prompt, model) {
5403
5567
  ""
5404
5568
  ];
5405
5569
  const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
5406
- cwd: homedir4(),
5570
+ cwd: homedir5(),
5407
5571
  timeout: 12e4,
5408
5572
  stdin: "ignore",
5409
5573
  env: childEnv,
@@ -5482,10 +5646,10 @@ function freePort(codeName) {
5482
5646
  }
5483
5647
  }
5484
5648
  function getStateFile() {
5485
- return join8(config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
5649
+ return join9(config?.configDir ?? join9(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
5486
5650
  }
5487
5651
  function channelHashCacheDir() {
5488
- return config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
5652
+ return config?.configDir ?? join9(process.env["HOME"] ?? "/tmp", ".augmented");
5489
5653
  }
5490
5654
  function loadChannelHashCache2() {
5491
5655
  loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
@@ -5496,7 +5660,7 @@ function saveChannelHashCache2() {
5496
5660
  var _channelQuarantineStore = null;
5497
5661
  function channelQuarantineStore() {
5498
5662
  if (!_channelQuarantineStore) {
5499
- const dir = config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
5663
+ const dir = config?.configDir ?? join9(process.env["HOME"] ?? "/tmp", ".augmented");
5500
5664
  _channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
5501
5665
  }
5502
5666
  return _channelQuarantineStore;
@@ -5551,7 +5715,7 @@ function log(msg) {
5551
5715
  `;
5552
5716
  if (!managerLogPath) {
5553
5717
  try {
5554
- managerLogPath = join8(homedir4(), ".augmented", "manager.log");
5718
+ managerLogPath = join9(homedir5(), ".augmented", "manager.log");
5555
5719
  mkdirSync4(dirname3(managerLogPath), { recursive: true });
5556
5720
  if (existsSync5(managerLogPath)) {
5557
5721
  chmodSync(managerLogPath, 384);
@@ -5606,12 +5770,12 @@ function parseSkillFrontmatter(content) {
5606
5770
  }
5607
5771
  async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
5608
5772
  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");
5773
+ const skillsDir = join9(configDir, codeName, "project", ".claude", "skills");
5774
+ const claudeMdPath = join9(configDir, codeName, "project", "CLAUDE.md");
5611
5775
  if (!ex(skillsDir) || !ex(claudeMdPath)) return;
5612
5776
  const entries = [];
5613
5777
  for (const dir of readdirSync5(skillsDir).sort()) {
5614
- const skillFile = join8(skillsDir, dir, "SKILL.md");
5778
+ const skillFile = join9(skillsDir, dir, "SKILL.md");
5615
5779
  if (!ex(skillFile)) continue;
5616
5780
  try {
5617
5781
  const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
@@ -5659,7 +5823,7 @@ ${SKILLS_INDEX_END}`;
5659
5823
  }
5660
5824
  async function migrateToProfiles() {
5661
5825
  const homeDir = process.env["HOME"] ?? "/tmp";
5662
- const sharedConfigPath = join8(homeDir, ".openclaw", "openclaw.json");
5826
+ const sharedConfigPath = join9(homeDir, ".openclaw", "openclaw.json");
5663
5827
  let sharedConfig;
5664
5828
  try {
5665
5829
  sharedConfig = JSON.parse(readFileSync9(sharedConfigPath, "utf-8"));
@@ -5675,19 +5839,19 @@ async function migrateToProfiles() {
5675
5839
  const codeName = agentEntry["id"];
5676
5840
  if (!codeName) continue;
5677
5841
  if (codeName === "main") continue;
5678
- const profileDir = join8(homeDir, `.openclaw-${codeName}`);
5679
- if (existsSync5(join8(profileDir, "openclaw.json"))) continue;
5842
+ const profileDir = join9(homeDir, `.openclaw-${codeName}`);
5843
+ if (existsSync5(join9(profileDir, "openclaw.json"))) continue;
5680
5844
  log(`Migrating agent '${codeName}' to per-agent profile`);
5681
5845
  if (adapter.seedProfileConfig) {
5682
5846
  adapter.seedProfileConfig(codeName);
5683
5847
  }
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");
5848
+ const sharedAuthDir = join9(homeDir, ".openclaw", "agents", codeName, "agent");
5849
+ const profileAuthDir = join9(profileDir, "agents", codeName, "agent");
5850
+ const authFile = join9(sharedAuthDir, "auth-profiles.json");
5687
5851
  if (existsSync5(authFile)) {
5688
5852
  mkdirSync4(profileAuthDir, { recursive: true });
5689
5853
  const authContent = readFileSync9(authFile, "utf-8");
5690
- writeFileSync4(join8(profileAuthDir, "auth-profiles.json"), authContent);
5854
+ writeFileSync4(join9(profileAuthDir, "auth-profiles.json"), authContent);
5691
5855
  }
5692
5856
  allocatePort(codeName);
5693
5857
  migrated++;
@@ -5725,7 +5889,7 @@ function readGatewayToken(codeName) {
5725
5889
  }
5726
5890
  const homeDir = process.env["HOME"] ?? "/tmp";
5727
5891
  try {
5728
- const cfg = JSON.parse(readFileSync9(join8(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
5892
+ const cfg = JSON.parse(readFileSync9(join9(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
5729
5893
  return cfg?.gateway?.auth?.token;
5730
5894
  } catch {
5731
5895
  return void 0;
@@ -5734,7 +5898,7 @@ function readGatewayToken(codeName) {
5734
5898
  var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
5735
5899
  function isGatewayHung(codeName) {
5736
5900
  const homeDir = process.env["HOME"] ?? "/tmp";
5737
- const jobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5901
+ const jobsPath = join9(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5738
5902
  if (!existsSync5(jobsPath)) return false;
5739
5903
  try {
5740
5904
  const data = JSON.parse(readFileSync9(jobsPath, "utf-8"));
@@ -5770,13 +5934,13 @@ async function ensureGatewayRunning(codeName, adapter) {
5770
5934
  }
5771
5935
  await new Promise((r) => setTimeout(r, 2e3));
5772
5936
  const homeDir = process.env["HOME"] ?? "/tmp";
5773
- const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5937
+ const cronJobsPath = join9(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5774
5938
  clearStaleCronRunState(cronJobsPath);
5775
5939
  } else {
5776
5940
  if (status.port) {
5777
5941
  try {
5778
5942
  const homeDir = process.env["HOME"] ?? "/tmp";
5779
- const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5943
+ const configPath = join9(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5780
5944
  if (existsSync5(configPath)) {
5781
5945
  const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
5782
5946
  if (cfg.gateway?.port !== status.port) {
@@ -5802,7 +5966,7 @@ async function ensureGatewayRunning(codeName, adapter) {
5802
5966
  gatewaysStartedThisCycle.add(codeName);
5803
5967
  try {
5804
5968
  const homeDir = process.env["HOME"] ?? "/tmp";
5805
- const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5969
+ const configPath = join9(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5806
5970
  if (existsSync5(configPath)) {
5807
5971
  const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
5808
5972
  if (!cfg.gateway) cfg.gateway = {};
@@ -5958,7 +6122,7 @@ async function pollCycle() {
5958
6122
  }
5959
6123
  try {
5960
6124
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
5961
- const { collectDiagnostics } = await import("../persistent-session-35PWSTLO.js");
6125
+ const { collectDiagnostics } = await import("../persistent-session-IKQLTZZ6.js");
5962
6126
  const diagCodeNames = [...agentState.persistentSessionAgents];
5963
6127
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
5964
6128
  let tailscaleHostname;
@@ -6045,12 +6209,12 @@ async function pollCycle() {
6045
6209
  const {
6046
6210
  collectResponsivenessProbes,
6047
6211
  getResponsivenessIntervalMs
6048
- } = await import("../responsiveness-probe-MA4M2QM4.js");
6212
+ } = await import("../responsiveness-probe-2K4QHOWW.js");
6049
6213
  const probeIntervalMs = getResponsivenessIntervalMs();
6050
6214
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
6051
6215
  const probeCodeNames = [...agentState.persistentSessionAgents];
6052
6216
  if (probeCodeNames.length > 0) {
6053
- const { takeAcpxExecFailureCount, creditAcpxExecFailureCount } = await import("../persistent-session-35PWSTLO.js");
6217
+ const { takeAcpxExecFailureCount, creditAcpxExecFailureCount } = await import("../persistent-session-IKQLTZZ6.js");
6054
6218
  const drainedGiveUps = /* @__PURE__ */ new Map();
6055
6219
  const drainedAcpxFailures = /* @__PURE__ */ new Map();
6056
6220
  const probes = collectResponsivenessProbes(probeCodeNames).map((p) => {
@@ -6084,14 +6248,16 @@ async function pollCycle() {
6084
6248
  collectResponsivenessProbes,
6085
6249
  livePendingInboundOldestAgeSeconds,
6086
6250
  deadLetterPendingInbound
6087
- } = await import("../responsiveness-probe-MA4M2QM4.js");
6088
- const { transcriptActivityAgeSeconds } = await import("../daily-session-PNQX5URX.js");
6251
+ } = await import("../responsiveness-probe-2K4QHOWW.js");
6089
6252
  const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
6090
6253
  const wedgeNow = /* @__PURE__ */ new Date();
6091
6254
  const liveAgents = agentState.persistentSessionAgents;
6092
6255
  for (const tracked of consecutiveWedgeCycles.keys()) {
6093
6256
  if (!liveAgents.has(tracked)) consecutiveWedgeCycles.delete(tracked);
6094
6257
  }
6258
+ for (const tracked of wedgeExemptionLogged) {
6259
+ if (!liveAgents.has(tracked)) wedgeExemptionLogged.delete(tracked);
6260
+ }
6095
6261
  for (const tracked of wedgeRestartsByCard.keys()) {
6096
6262
  if (!liveAgents.has(tracked)) wedgeRestartsByCard.delete(tracked);
6097
6263
  }
@@ -6100,6 +6266,7 @@ async function pollCycle() {
6100
6266
  const codeName = probe.code_name;
6101
6267
  if (!isSessionHealthy(codeName)) {
6102
6268
  consecutiveWedgeCycles.delete(codeName);
6269
+ wedgeExemptionLogged.delete(codeName);
6103
6270
  continue;
6104
6271
  }
6105
6272
  const wedgeSession = getSessionState(codeName);
@@ -6109,9 +6276,15 @@ async function pollCycle() {
6109
6276
  wedgeSession?.currentSessionId ?? null,
6110
6277
  wedgeNow
6111
6278
  );
6279
+ const subagentAge = subagentActivityAgeSeconds(
6280
+ wedgeProjectDir(codeName),
6281
+ wedgeSession?.currentSessionId ?? null,
6282
+ wedgeNow
6283
+ );
6112
6284
  const signals = {
6113
6285
  paneActivityAgeSeconds: probe.pane_activity_age_seconds,
6114
6286
  transcriptActivityAgeSeconds: transcriptAge,
6287
+ subagentActivityAgeSeconds: subagentAge,
6115
6288
  pendingInboundOldestAgeSeconds: livePendingInboundOldestAgeSeconds(
6116
6289
  codeName,
6117
6290
  sessionStartMs,
@@ -6120,8 +6293,20 @@ async function pollCycle() {
6120
6293
  };
6121
6294
  if (!isWedgeCandidateCycle(signals, wedgeConfig)) {
6122
6295
  consecutiveWedgeCycles.delete(codeName);
6296
+ const exemption = wedgeExemptionReason(signals, wedgeConfig);
6297
+ if (exemption !== null) {
6298
+ if (!wedgeExemptionLogged.has(codeName)) {
6299
+ wedgeExemptionLogged.add(codeName);
6300
+ log(
6301
+ `[wedge] exempt agent=${codeName} reason=${exemption} subagentAge=${subagentAge}s inboundAge=${signals.pendingInboundOldestAgeSeconds}s transcriptAge=${transcriptAge === null ? "n/a" : `${transcriptAge}s`}`
6302
+ );
6303
+ }
6304
+ } else {
6305
+ wedgeExemptionLogged.delete(codeName);
6306
+ }
6123
6307
  continue;
6124
6308
  }
6309
+ wedgeExemptionLogged.delete(codeName);
6125
6310
  const streak = (consecutiveWedgeCycles.get(codeName) ?? 0) + 1;
6126
6311
  consecutiveWedgeCycles.set(codeName, streak);
6127
6312
  if (decideWedgeRestart({ ...signals, consecutiveWedgeCycles: streak }, wedgeConfig) !== "wedged") {
@@ -6149,6 +6334,21 @@ async function pollCycle() {
6149
6334
  log(
6150
6335
  `[wedge] forced fresh respawn ${detail} \u2192 new session ${newId} (transcript preserved${deadNote})`
6151
6336
  );
6337
+ const wedgeAgentId = agentState.codeNameToAgentId.get(codeName);
6338
+ if (wedgeAgentId) {
6339
+ api.post("/host/wedge-respawn", {
6340
+ agent_id: wedgeAgentId,
6341
+ code_name: codeName,
6342
+ dead_lettered_count: deadLettered,
6343
+ pane_age_seconds: signals.paneActivityAgeSeconds,
6344
+ inbound_age_seconds: signals.pendingInboundOldestAgeSeconds,
6345
+ transcript_age_seconds: transcriptAge
6346
+ }).catch((err) => {
6347
+ log(
6348
+ `[wedge] failed to record respawn event for '${codeName}' (ENG-6265): ${err.message} \u2014 respawn proceeded; CloudWatch metric will under-count this event`
6349
+ );
6350
+ });
6351
+ }
6152
6352
  const inProgressCardIds = (kanbanBoardCache.get(codeName) ?? []).filter((item) => item.status === "in_progress" && isHybridActionable(item)).map((item) => item.id);
6153
6353
  const cardStates = wedgeRestartsByCard.get(codeName) ?? /* @__PURE__ */ new Map();
6154
6354
  const { next, newlyPoisoned } = recordWedgeForCards(
@@ -6299,7 +6499,7 @@ async function pollCycle() {
6299
6499
  }
6300
6500
  killAgentChannelProcesses(prev.codeName, { log });
6301
6501
  freePort(prev.codeName);
6302
- const agentDir = join8(adapter.getAgentDir(prev.codeName), "provision");
6502
+ const agentDir = join9(adapter.getAgentDir(prev.codeName), "provision");
6303
6503
  await cleanupAgentFiles(prev.codeName, agentDir);
6304
6504
  clearAgentCaches(prev.agentId, prev.codeName);
6305
6505
  }
@@ -6385,10 +6585,10 @@ async function pollCycle() {
6385
6585
  // pending-inbound marker. Best-effort: a write failure is logged by
6386
6586
  // the watchdog, never fails the poll cycle.
6387
6587
  signalGiveUp: (codeName) => {
6388
- const dir = join8(homedir4(), ".augmented", codeName);
6588
+ const dir = join9(homedir5(), ".augmented", codeName);
6389
6589
  if (!existsSync5(dir)) return;
6390
6590
  atomicWriteFileSync(
6391
- join8(dir, "watchdog-give-up.json"),
6591
+ join9(dir, "watchdog-give-up.json"),
6392
6592
  JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
6393
6593
  );
6394
6594
  }
@@ -6509,7 +6709,7 @@ async function processAgent(agent, agentStates) {
6509
6709
  }
6510
6710
  const now = (/* @__PURE__ */ new Date()).toISOString();
6511
6711
  const adapter = resolveAgentFramework(agent.code_name);
6512
- let agentDir = join8(adapter.getAgentDir(agent.code_name), "provision");
6712
+ let agentDir = join9(adapter.getAgentDir(agent.code_name), "provision");
6513
6713
  if (agent.status === "draft" || agent.status === "paused") {
6514
6714
  if (previousKnownStatus !== agent.status) {
6515
6715
  log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
@@ -6713,7 +6913,7 @@ async function processAgent(agent, agentStates) {
6713
6913
  const frameworkId = refreshData.agent.framework ?? "openclaw";
6714
6914
  agentFrameworkCache.set(agent.code_name, frameworkId);
6715
6915
  const frameworkAdapter = getFramework(frameworkId);
6716
- agentDir = join8(frameworkAdapter.getAgentDir(agent.code_name), "provision");
6916
+ agentDir = join9(frameworkAdapter.getAgentDir(agent.code_name), "provision");
6717
6917
  cacheAgentDeliveryMetadata(agent.code_name, refreshData);
6718
6918
  if (frameworkAdapter.migrateSecretStorage && !migratedSecretStorage.has(agent.code_name)) {
6719
6919
  try {
@@ -6756,7 +6956,7 @@ async function processAgent(agent, agentStates) {
6756
6956
  const changedFiles = [];
6757
6957
  mkdirSync4(agentDir, { recursive: true });
6758
6958
  for (const artifact of artifacts) {
6759
- const filePath = join8(agentDir, artifact.relativePath);
6959
+ const filePath = join9(agentDir, artifact.relativePath);
6760
6960
  let existingHash;
6761
6961
  let newHash;
6762
6962
  let writeContent = artifact.content;
@@ -6775,7 +6975,7 @@ async function processAgent(agent, agentStates) {
6775
6975
  };
6776
6976
  newHash = sha256(stripDynamicSections(artifact.content));
6777
6977
  try {
6778
- const projectClaudeMd = join8(config.configDir, agent.code_name, "project", "CLAUDE.md");
6978
+ const projectClaudeMd = join9(config.configDir, agent.code_name, "project", "CLAUDE.md");
6779
6979
  const existing = readFileSync9(projectClaudeMd, "utf-8");
6780
6980
  existingHash = sha256(stripDynamicSections(existing));
6781
6981
  } catch {
@@ -6816,12 +7016,12 @@ async function processAgent(agent, agentStates) {
6816
7016
  }
6817
7017
  }
6818
7018
  if (changedFiles.length > 0) {
6819
- const isFirst = !existsSync5(join8(agentDir, "CHARTER.md"));
7019
+ const isFirst = !existsSync5(join9(agentDir, "CHARTER.md"));
6820
7020
  const verb = isFirst ? "Provisioning" : "Updating";
6821
7021
  const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
6822
7022
  log(`${verb} '${agent.code_name}': ${fileNames}`);
6823
7023
  for (const file of changedFiles) {
6824
- const filePath = join8(agentDir, file.relativePath);
7024
+ const filePath = join9(agentDir, file.relativePath);
6825
7025
  mkdirSync4(dirname3(filePath), { recursive: true });
6826
7026
  if (file.relativePath === ".mcp.json") {
6827
7027
  safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
@@ -6830,12 +7030,12 @@ async function processAgent(agent, agentStates) {
6830
7030
  }
6831
7031
  }
6832
7032
  try {
6833
- const provSkillsDir = join8(agentDir, ".claude", "skills");
7033
+ const provSkillsDir = join9(agentDir, ".claude", "skills");
6834
7034
  if (existsSync5(provSkillsDir)) {
6835
7035
  for (const folder of readdirSync4(provSkillsDir)) {
6836
7036
  if (folder.startsWith("knowledge-")) {
6837
7037
  try {
6838
- rmSync2(join8(provSkillsDir, folder), { recursive: true });
7038
+ rmSync2(join9(provSkillsDir, folder), { recursive: true });
6839
7039
  } catch {
6840
7040
  }
6841
7041
  }
@@ -6848,7 +7048,7 @@ async function processAgent(agent, agentStates) {
6848
7048
  const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
6849
7049
  const hashes = /* @__PURE__ */ new Map();
6850
7050
  for (const file of trackedFiles2) {
6851
- const h = hashFile(join8(agentDir, file));
7051
+ const h = hashFile(join9(agentDir, file));
6852
7052
  if (h) hashes.set(file, h);
6853
7053
  }
6854
7054
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -6916,7 +7116,7 @@ async function processAgent(agent, agentStates) {
6916
7116
  if (written && existsSync5(agentDir)) {
6917
7117
  const driftedFiles = [];
6918
7118
  for (const [file, expectedHash] of written) {
6919
- const localHash = hashFile(join8(agentDir, file));
7119
+ const localHash = hashFile(join9(agentDir, file));
6920
7120
  if (localHash && localHash !== expectedHash) {
6921
7121
  driftedFiles.push(file);
6922
7122
  }
@@ -6927,7 +7127,7 @@ async function processAgent(agent, agentStates) {
6927
7127
  try {
6928
7128
  const localHashes = {};
6929
7129
  for (const file of driftedFiles) {
6930
- localHashes[file] = hashFile(join8(agentDir, file));
7130
+ localHashes[file] = hashFile(join9(agentDir, file));
6931
7131
  }
6932
7132
  await api.post("/host/drift", {
6933
7133
  agent_id: agent.agent_id,
@@ -7251,18 +7451,18 @@ async function processAgent(agent, agentStates) {
7251
7451
  if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
7252
7452
  try {
7253
7453
  const agentProvisionDir = agentDir;
7254
- const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
7454
+ const projectDir = join9(homedir5(), ".augmented", agent.code_name, "project");
7255
7455
  mkdirSync4(agentProvisionDir, { recursive: true });
7256
7456
  mkdirSync4(projectDir, { recursive: true });
7257
- const provisionMcpPath = join8(agentProvisionDir, ".mcp.json");
7258
- const projectMcpPath = join8(projectDir, ".mcp.json");
7457
+ const provisionMcpPath = join9(agentProvisionDir, ".mcp.json");
7458
+ const projectMcpPath = join9(projectDir, ".mcp.json");
7259
7459
  let mcpConfig = { mcpServers: {} };
7260
7460
  try {
7261
7461
  mcpConfig = JSON.parse(readFileSync9(provisionMcpPath, "utf-8"));
7262
7462
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
7263
7463
  } catch {
7264
7464
  }
7265
- const localDirectChatChannel = join8(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
7465
+ const localDirectChatChannel = join9(homedir5(), ".augmented", "_mcp", "direct-chat-channel.js");
7266
7466
  const directChatTeamSettings = refreshData.team?.settings;
7267
7467
  const directChatTz = (() => {
7268
7468
  const tz = directChatTeamSettings?.["timezone"];
@@ -7292,7 +7492,7 @@ async function processAgent(agent, agentStates) {
7292
7492
  log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
7293
7493
  }
7294
7494
  }
7295
- const staleChannelsPath = join8(projectDir, ".mcp-channels.json");
7495
+ const staleChannelsPath = join9(projectDir, ".mcp-channels.json");
7296
7496
  if (existsSync5(staleChannelsPath)) {
7297
7497
  try {
7298
7498
  rmSync2(staleChannelsPath, { force: true });
@@ -7357,7 +7557,7 @@ async function processAgent(agent, agentStates) {
7357
7557
  }
7358
7558
  if (process.env.AGT_CONNECTIVITY_PROBE_ENABLED === "true") {
7359
7559
  try {
7360
- const probeProjectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
7560
+ const probeProjectDir = join9(homedir5(), ".augmented", agent.code_name, "project");
7361
7561
  await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
7362
7562
  } catch (err) {
7363
7563
  log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
@@ -7375,8 +7575,8 @@ async function processAgent(agent, agentStates) {
7375
7575
  recordConfigChurnEvent(agent.agent_id, agent.code_name, FLAP_CHANNEL_INTEGRATIONS, intMembership);
7376
7576
  }
7377
7577
  if (intHash !== prevIntHash) {
7378
- const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
7379
- const envIntPath = join8(projectDir, ".env.integrations");
7578
+ const projectDir = join9(homedir5(), ".augmented", agent.code_name, "project");
7579
+ const envIntPath = join9(projectDir, ".env.integrations");
7380
7580
  let preWriteEnv;
7381
7581
  try {
7382
7582
  preWriteEnv = readFileSync9(envIntPath, "utf-8");
@@ -7391,7 +7591,7 @@ async function processAgent(agent, agentStates) {
7391
7591
  let rotationHandled = true;
7392
7592
  if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
7393
7593
  try {
7394
- const projectMcpPath = join8(projectDir, ".mcp.json");
7594
+ const projectMcpPath = join9(projectDir, ".mcp.json");
7395
7595
  const postWriteEnv = readFileSync9(envIntPath, "utf-8");
7396
7596
  const mcpContent = readFileSync9(projectMcpPath, "utf-8");
7397
7597
  const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
@@ -7575,7 +7775,7 @@ async function processAgent(agent, agentStates) {
7575
7775
  if (agent.status === "active") {
7576
7776
  if (frameworkAdapter.installPlugin) {
7577
7777
  try {
7578
- const pluginPath = join8(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
7778
+ const pluginPath = join9(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
7579
7779
  if (existsSync5(pluginPath)) {
7580
7780
  frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
7581
7781
  agtHost: requireHost(),
@@ -7642,18 +7842,18 @@ async function processAgent(agent, agentStates) {
7642
7842
  }
7643
7843
  try {
7644
7844
  const { readdirSync: readdirSync5, rmSync: rmSync3 } = await import("fs");
7645
- const { homedir: homedir5 } = await import("os");
7845
+ const { homedir: homedir6 } = await import("os");
7646
7846
  const frameworkId2 = frameworkAdapter.id;
7647
7847
  const candidateSkillDirs = [
7648
7848
  // Claude Code — framework runtime tree
7649
- join8(homedir5(), ".augmented", agent.code_name, "skills"),
7849
+ join9(homedir6(), ".augmented", agent.code_name, "skills"),
7650
7850
  // Claude Code — project tree
7651
- join8(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
7851
+ join9(homedir6(), ".augmented", agent.code_name, "project", ".claude", "skills"),
7652
7852
  // OpenClaw — framework runtime tree
7653
- join8(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
7853
+ join9(homedir6(), `.openclaw-${agent.code_name}`, "skills"),
7654
7854
  // Defensive: legacy provision-side path, not currently an
7655
7855
  // install target but cheap to sweep.
7656
- join8(agentDir, ".claude", "skills")
7856
+ join9(agentDir, ".claude", "skills")
7657
7857
  ];
7658
7858
  const existingDirs = candidateSkillDirs.filter((d) => existsSync5(d));
7659
7859
  const discoveredEntries = /* @__PURE__ */ new Set();
@@ -7669,7 +7869,7 @@ async function processAgent(agent, agentStates) {
7669
7869
  }
7670
7870
  const removeSkillFolder = (entry, reason) => {
7671
7871
  for (const dir of existingDirs) {
7672
- const p = join8(dir, entry);
7872
+ const p = join9(dir, entry);
7673
7873
  if (existsSync5(p)) {
7674
7874
  rmSync3(p, { recursive: true, force: true });
7675
7875
  }
@@ -7849,7 +8049,7 @@ async function processAgent(agent, agentStates) {
7849
8049
  const sess = getSessionState(agent.code_name);
7850
8050
  let mcpJsonParsed = null;
7851
8051
  try {
7852
- const mcpPath = join8(getProjectDir(agent.code_name), ".mcp.json");
8052
+ const mcpPath = join9(getProjectDir(agent.code_name), ".mcp.json");
7853
8053
  mcpJsonParsed = JSON.parse(readFileSync9(mcpPath, "utf-8"));
7854
8054
  } catch {
7855
8055
  }
@@ -7868,7 +8068,19 @@ async function processAgent(agent, agentStates) {
7868
8068
  // isolated and the agent keeps running degraded instead of being
7869
8069
  // paused wholesale. reaperRestartBreakerReason() encodes that
7870
8070
  // single-vs-multi decision; undefined means "restart, don't count".
7871
- stopSession: (codeName, ctx) => stopPersistentSessionAndForgetMcpBaseline(codeName, reaperRestartBreakerReason(ctx.activeKeys)),
8071
+ //
8072
+ // ENG-6264: the breaker-count reason (above, undefined for a single dead
8073
+ // MCP) is decoupled from the GATE reason. Pre-6264 an undefined breaker
8074
+ // reason also made the restart non-gateable → 'bypass' → the session was
8075
+ // torn down mid-turn (the common single-MCP case interrupted busy
8076
+ // agents). Always pass 'mcp-presence-reaper' as the gate reason so the
8077
+ // restart defers-until-idle, while breakerReason still governs whether it
8078
+ // counts against the breaker.
8079
+ stopSession: (codeName, ctx) => stopPersistentSessionAndForgetMcpBaseline(
8080
+ codeName,
8081
+ reaperRestartBreakerReason(ctx.activeKeys),
8082
+ "mcp-presence-reaper"
8083
+ ),
7872
8084
  // ENG-5292: when the reaper gives up on a managed MCP (cap from
7873
8085
  // ENG-5279 + state-preservation from ENG-5285 both said "this
7874
8086
  // MCP keeps failing after 3 restart cycles"), mark the matching
@@ -8047,7 +8259,7 @@ async function processAgent(agent, agentStates) {
8047
8259
  lastWorkTriggerAt.set(agent.code_name, triggerTs);
8048
8260
  if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
8049
8261
  const homeDir = process.env["HOME"] ?? "/tmp";
8050
- const jobsPath = join8(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
8262
+ const jobsPath = join9(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
8051
8263
  if (existsSync5(jobsPath)) {
8052
8264
  try {
8053
8265
  const jobsData = JSON.parse(readFileSync9(jobsPath, "utf-8"));
@@ -8187,7 +8399,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
8187
8399
  if (trackedFiles.length > 0 && existsSync5(agentDir)) {
8188
8400
  const hashes = /* @__PURE__ */ new Map();
8189
8401
  for (const file of trackedFiles) {
8190
- const h = hashFile(join8(agentDir, file));
8402
+ const h = hashFile(join9(agentDir, file));
8191
8403
  if (h) hashes.set(file, h);
8192
8404
  }
8193
8405
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -8221,16 +8433,16 @@ function cleanupStaleSessions(codeName) {
8221
8433
  lastCleanupAt.set(codeName, Date.now());
8222
8434
  const homeDir = process.env["HOME"] ?? "/tmp";
8223
8435
  for (const agentDir of ["main", codeName]) {
8224
- const sessionsDir = join8(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
8436
+ const sessionsDir = join9(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
8225
8437
  cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
8226
8438
  }
8227
- const cronRunsDir = join8(homeDir, `.openclaw-${codeName}`, "cron", "runs");
8439
+ const cronRunsDir = join9(homeDir, `.openclaw-${codeName}`, "cron", "runs");
8228
8440
  cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
8229
- const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
8441
+ const cronJobsPath = join9(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
8230
8442
  clearStaleCronRunState(cronJobsPath);
8231
8443
  }
8232
8444
  function cleanupCronSessions(sessionsDir, keepCount) {
8233
- const indexPath = join8(sessionsDir, "sessions.json");
8445
+ const indexPath = join9(sessionsDir, "sessions.json");
8234
8446
  if (!existsSync5(indexPath)) return;
8235
8447
  try {
8236
8448
  const raw = readFileSync9(indexPath, "utf-8");
@@ -8246,7 +8458,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
8246
8458
  for (const entry of toDelete) {
8247
8459
  delete index[entry.key];
8248
8460
  if (entry.sessionId) {
8249
- const sessionFile = join8(sessionsDir, `${entry.sessionId}.jsonl`);
8461
+ const sessionFile = join9(sessionsDir, `${entry.sessionId}.jsonl`);
8250
8462
  try {
8251
8463
  if (existsSync5(sessionFile)) {
8252
8464
  unlinkSync(sessionFile);
@@ -8268,7 +8480,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
8268
8480
  delete index[parentKey];
8269
8481
  if (parentSessionId) {
8270
8482
  try {
8271
- const f = join8(sessionsDir, `${parentSessionId}.jsonl`);
8483
+ const f = join9(sessionsDir, `${parentSessionId}.jsonl`);
8272
8484
  if (existsSync5(f)) {
8273
8485
  unlinkSync(f);
8274
8486
  deletedFiles++;
@@ -8326,7 +8538,7 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
8326
8538
  try {
8327
8539
  for (const f of readdirSync4(dir)) {
8328
8540
  if (!f.endsWith(ext)) continue;
8329
- const fullPath = join8(dir, f);
8541
+ const fullPath = join9(dir, f);
8330
8542
  try {
8331
8543
  const st = statSync3(fullPath);
8332
8544
  if (st.mtimeMs < cutoff) {
@@ -8366,7 +8578,7 @@ var inFlightClaudeTasks = /* @__PURE__ */ new Set();
8366
8578
  var claudeTaskConcurrency = /* @__PURE__ */ new Map();
8367
8579
  var MAX_CLAUDE_CONCURRENCY = 2;
8368
8580
  function claudePidFilePath() {
8369
- return join8(homedir4(), ".augmented", "manager-claude-pids.json");
8581
+ return join9(homedir5(), ".augmented", "manager-claude-pids.json");
8370
8582
  }
8371
8583
  var inFlightClaudePids = /* @__PURE__ */ new Map();
8372
8584
  function registerClaudeSpawn(record) {
@@ -8706,7 +8918,7 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
8706
8918
  }
8707
8919
  async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
8708
8920
  const projectDir = getProjectDir2(codeName);
8709
- const mcpConfigPath = join8(projectDir, ".mcp.json");
8921
+ const mcpConfigPath = join9(projectDir, ".mcp.json");
8710
8922
  let runId = null;
8711
8923
  let kanbanItemId = null;
8712
8924
  let taskResult;
@@ -8714,7 +8926,7 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
8714
8926
  const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
8715
8927
  prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
8716
8928
  try {
8717
- const claudeMdPath = join8(projectDir, "CLAUDE.md");
8929
+ const claudeMdPath = join9(projectDir, "CLAUDE.md");
8718
8930
  const serverNames = [];
8719
8931
  if (existsSync5(mcpConfigPath)) {
8720
8932
  try {
@@ -8741,7 +8953,7 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
8741
8953
  claudeArgs.push("--system-prompt-file", claudeMdPath);
8742
8954
  }
8743
8955
  const childEnv = { ...process.env };
8744
- const envIntPath = join8(projectDir, ".env.integrations");
8956
+ const envIntPath = join9(projectDir, ".env.integrations");
8745
8957
  if (existsSync5(envIntPath)) {
8746
8958
  try {
8747
8959
  Object.assign(childEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
@@ -8916,8 +9128,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
8916
9128
  async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
8917
9129
  const codeName = agent.code_name;
8918
9130
  const projectDir = getProjectDir(codeName);
8919
- const mcpConfigPath = join8(projectDir, ".mcp.json");
8920
- const claudeMdPath = join8(projectDir, "CLAUDE.md");
9131
+ const mcpConfigPath = join9(projectDir, ".mcp.json");
9132
+ const claudeMdPath = join9(projectDir, "CLAUDE.md");
8921
9133
  if (restartBreaker.isTripped(codeName)) {
8922
9134
  const trip = restartBreaker.getTrip(codeName);
8923
9135
  return {
@@ -9087,6 +9299,11 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash3("sha256").
9087
9299
  } catch (err) {
9088
9300
  log(`[persistent-session] Failed to provision auto-progress hook for '${codeName}': ${err.message}`);
9089
9301
  }
9302
+ try {
9303
+ provisionSessionStateHook(codeName);
9304
+ } catch (err) {
9305
+ log(`[persistent-session] Failed to provision session-state hook for '${codeName}': ${err.message}`);
9306
+ }
9090
9307
  const sessionRunResult = await startRun({
9091
9308
  agent_id: agent.agent_id,
9092
9309
  source_type: "system",
@@ -9546,7 +9763,7 @@ ${escapeXml(msg.content)}
9546
9763
  log(`[direct-chat] One-shot spawn for '${agent.codeName}' (msg=${msg.id}; host in-flight=${directChatSpawnGate.hostInFlight}, queued=${directChatSpawnGate.queuedCount})`);
9547
9764
  const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
9548
9765
  const projDir = ccProjectDir(agent.codeName);
9549
- const mcpConfigPath = join8(projDir, ".mcp.json");
9766
+ const mcpConfigPath = join9(projDir, ".mcp.json");
9550
9767
  const serverNames = [];
9551
9768
  if (existsSync5(mcpConfigPath)) {
9552
9769
  try {
@@ -9569,11 +9786,11 @@ ${escapeXml(msg.content)}
9569
9786
  "--allowedTools",
9570
9787
  allowedTools
9571
9788
  ];
9572
- const chatClaudeMd = join8(projDir, "CLAUDE.md");
9789
+ const chatClaudeMd = join9(projDir, "CLAUDE.md");
9573
9790
  if (existsSync5(chatClaudeMd)) {
9574
9791
  chatArgs.push("--system-prompt-file", chatClaudeMd);
9575
9792
  }
9576
- const envIntPath = join8(projDir, ".env.integrations");
9793
+ const envIntPath = join9(projDir, ".env.integrations");
9577
9794
  const childEnv = { ...process.env };
9578
9795
  if (existsSync5(envIntPath)) {
9579
9796
  try {
@@ -9955,8 +10172,8 @@ function getBuiltInSkillContent(skillId) {
9955
10172
  if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
9956
10173
  try {
9957
10174
  const candidates = [
9958
- join8(process.cwd(), "skills", skillId, "SKILL.md"),
9959
- join8(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
10175
+ join9(process.cwd(), "skills", skillId, "SKILL.md"),
10176
+ join9(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
9960
10177
  ];
9961
10178
  for (const candidate of candidates) {
9962
10179
  if (existsSync5(candidate)) {
@@ -10615,7 +10832,7 @@ async function processClaudePairSessions(agents) {
10615
10832
  killPairSession,
10616
10833
  pairTmuxSession,
10617
10834
  finalizeClaudePairOnboarding
10618
- } = await import("../claude-pair-runtime-GIUCD7IG.js");
10835
+ } = await import("../claude-pair-runtime-QYJUJUYY.js");
10619
10836
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
10620
10837
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
10621
10838
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -10676,11 +10893,11 @@ async function processClaudePairSessions(agents) {
10676
10893
  });
10677
10894
  } else {
10678
10895
  const errKind = result.error.kind;
10679
- const errMessage = "message" in result.error ? result.error.message : void 0;
10896
+ const errMessage2 = "message" in result.error ? result.error.message : void 0;
10680
10897
  await reportAndCleanup(session.pair_id, {
10681
10898
  status: errKind === "no-session" ? "session_missing" : "failure",
10682
10899
  error_code: errKind,
10683
- error_message: errMessage
10900
+ error_message: errMessage2
10684
10901
  });
10685
10902
  }
10686
10903
  } else if (session.status === "code_submitted" && session.code) {
@@ -10709,11 +10926,11 @@ async function processClaudePairSessions(agents) {
10709
10926
  });
10710
10927
  } else {
10711
10928
  const errKind = result.error.kind;
10712
- const errMessage = "message" in result.error ? result.error.message : void 0;
10929
+ const errMessage2 = "message" in result.error ? result.error.message : void 0;
10713
10930
  await reportAndCleanup(session.pair_id, {
10714
10931
  status: errKind === "no-session" ? "session_missing" : "failure",
10715
10932
  error_code: errKind,
10716
- error_message: errMessage
10933
+ error_message: errMessage2
10717
10934
  });
10718
10935
  }
10719
10936
  }
@@ -10925,8 +11142,8 @@ function parseMemoryFile(raw, fallbackName) {
10925
11142
  };
10926
11143
  }
10927
11144
  async function syncMemories(agent, configDir, log2) {
10928
- const projectDir = join8(configDir, agent.code_name, "project");
10929
- const memoryDir = join8(projectDir, "memory");
11145
+ const projectDir = join9(configDir, agent.code_name, "project");
11146
+ const memoryDir = join9(projectDir, "memory");
10930
11147
  const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
10931
11148
  if (isFreshSync) {
10932
11149
  log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
@@ -10944,7 +11161,7 @@ async function syncMemories(agent, configDir, log2) {
10944
11161
  for (const file of readdirSync4(memoryDir)) {
10945
11162
  if (!file.endsWith(".md")) continue;
10946
11163
  try {
10947
- const raw = readFileSync9(join8(memoryDir, file), "utf-8");
11164
+ const raw = readFileSync9(join9(memoryDir, file), "utf-8");
10948
11165
  const fileHash = createHash3("sha256").update(raw).digest("hex").slice(0, 16);
10949
11166
  currentHashes.set(file, fileHash);
10950
11167
  if (prevHashes.get(file) === fileHash) continue;
@@ -10969,7 +11186,7 @@ async function syncMemories(agent, configDir, log2) {
10969
11186
  } catch (err) {
10970
11187
  for (const mem of changedMemories) {
10971
11188
  for (const [file] of currentHashes) {
10972
- const parsed = parseMemoryFile(readFileSync9(join8(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
11189
+ const parsed = parseMemoryFile(readFileSync9(join9(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
10973
11190
  if (parsed?.name === mem.name) currentHashes.delete(file);
10974
11191
  }
10975
11192
  }
@@ -11004,7 +11221,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
11004
11221
  const mem = dbMemories.memories[i];
11005
11222
  const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
11006
11223
  const slug = rawSlug || `memory-${i}`;
11007
- const filePath = join8(memoryDir, `${slug}.md`);
11224
+ const filePath = join9(memoryDir, `${slug}.md`);
11008
11225
  const desired = `---
11009
11226
  name: ${JSON.stringify(mem.name)}
11010
11227
  type: ${mem.type}
@@ -11125,6 +11342,42 @@ function stopCaffeinate() {
11125
11342
  log("caffeinate stopped");
11126
11343
  }
11127
11344
  }
11345
+ var artifactScanners = /* @__PURE__ */ new Map();
11346
+ var artifactStreamingInFlight = false;
11347
+ async function driveArtifactStreaming() {
11348
+ if (artifactStreamingInFlight) return;
11349
+ artifactStreamingInFlight = true;
11350
+ try {
11351
+ await driveArtifactStreamingInner();
11352
+ } finally {
11353
+ artifactStreamingInFlight = false;
11354
+ }
11355
+ }
11356
+ async function driveArtifactStreamingInner() {
11357
+ const liveIds = /* @__PURE__ */ new Set();
11358
+ for (const agent of state6.agents) {
11359
+ if (!agent.agentId || !agent.codeName || agent.status !== "active") continue;
11360
+ liveIds.add(agent.agentId);
11361
+ let scanner = artifactScanners.get(agent.agentId);
11362
+ if (!scanner) {
11363
+ scanner = new ArtifactStreamScanner(
11364
+ agent.agentId,
11365
+ artifactsDirFor(agent.codeName),
11366
+ nodeArtifactFs,
11367
+ { api, log }
11368
+ );
11369
+ artifactScanners.set(agent.agentId, scanner);
11370
+ }
11371
+ try {
11372
+ await scanner.scanOnce();
11373
+ } catch (err) {
11374
+ log(`[artifact-stream] scan failed for ${agent.codeName}: ${err.message}`);
11375
+ }
11376
+ }
11377
+ for (const id of artifactScanners.keys()) {
11378
+ if (!liveIds.has(id)) artifactScanners.delete(id);
11379
+ }
11380
+ }
11128
11381
  function startPolling() {
11129
11382
  if (!config || running) return;
11130
11383
  running = true;
@@ -11136,6 +11389,7 @@ function startPolling() {
11136
11389
  startGatewayPool();
11137
11390
  return pollCycle();
11138
11391
  }).then(() => {
11392
+ void driveArtifactStreaming();
11139
11393
  scheduleNext();
11140
11394
  });
11141
11395
  }
@@ -11158,7 +11412,10 @@ function scheduleNext() {
11158
11412
  restartNow();
11159
11413
  return;
11160
11414
  }
11161
- void pollCycle().then(scheduleNext);
11415
+ void pollCycle().then(() => {
11416
+ void driveArtifactStreaming();
11417
+ scheduleNext();
11418
+ });
11162
11419
  }, delayMs);
11163
11420
  }
11164
11421
  async function killAllAgtTmuxSessions() {
@@ -11251,7 +11508,7 @@ function startManager(opts) {
11251
11508
  log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
11252
11509
  }
11253
11510
  log(
11254
- `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join8(homedir4(), ".augmented", "manager.log")}`
11511
+ `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join9(homedir5(), ".augmented", "manager.log")}`
11255
11512
  );
11256
11513
  deployMcpAssets();
11257
11514
  reapOrphanChannelMcps({ log });
@@ -11382,14 +11639,14 @@ function restartRunningChannelMcps(basenames) {
11382
11639
  }
11383
11640
  }
11384
11641
  function deployMcpAssets() {
11385
- const targetDir = join8(homedir4(), ".augmented", "_mcp");
11642
+ const targetDir = join9(homedir5(), ".augmented", "_mcp");
11386
11643
  mkdirSync4(targetDir, { recursive: true });
11387
11644
  const moduleDir = dirname3(fileURLToPath(import.meta.url));
11388
11645
  let mcpSourceDir = "";
11389
11646
  let dir = moduleDir;
11390
11647
  for (let i = 0; i < 6; i++) {
11391
- const candidate = join8(dir, "dist", "mcp");
11392
- if (existsSync5(join8(candidate, "index.js"))) {
11648
+ const candidate = join9(dir, "dist", "mcp");
11649
+ if (existsSync5(join9(candidate, "index.js"))) {
11393
11650
  mcpSourceDir = candidate;
11394
11651
  break;
11395
11652
  }
@@ -11432,8 +11689,8 @@ function deployMcpAssets() {
11432
11689
  // natural session restart.
11433
11690
  "augmented-admin.js"
11434
11691
  ]) {
11435
- const src = join8(mcpSourceDir, file);
11436
- const dst = join8(targetDir, file);
11692
+ const src = join9(mcpSourceDir, file);
11693
+ const dst = join9(targetDir, file);
11437
11694
  if (!existsSync5(src)) continue;
11438
11695
  const before = fileHash(dst);
11439
11696
  try {
@@ -11451,14 +11708,14 @@ function deployMcpAssets() {
11451
11708
  log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
11452
11709
  restartRunningChannelMcps(changedBasenames);
11453
11710
  }
11454
- const localMcpPath = join8(targetDir, "index.js");
11711
+ const localMcpPath = join9(targetDir, "index.js");
11455
11712
  try {
11456
- const agentsDir = join8(homedir4(), ".augmented", "agents");
11713
+ const agentsDir = join9(homedir5(), ".augmented", "agents");
11457
11714
  if (existsSync5(agentsDir)) {
11458
11715
  for (const entry of readdirSync4(agentsDir, { withFileTypes: true })) {
11459
11716
  if (!entry.isDirectory()) continue;
11460
11717
  for (const subdir of ["provision", "project"]) {
11461
- const mcpJsonPath = join8(agentsDir, entry.name, subdir, ".mcp.json");
11718
+ const mcpJsonPath = join9(agentsDir, entry.name, subdir, ".mcp.json");
11462
11719
  try {
11463
11720
  const raw = readFileSync9(mcpJsonPath, "utf-8");
11464
11721
  if (!raw.includes("@integrity-labs/augmented-mcp")) continue;