@integrity-labs/agt-cli 0.23.2 → 0.24.0

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.
@@ -12,7 +12,7 @@ import {
12
12
  provisionOrientHook,
13
13
  provisionStopHook,
14
14
  requireHost
15
- } from "../chunk-WUYKMPYW.js";
15
+ } from "../chunk-WZTRMJM4.js";
16
16
  import {
17
17
  findTaskByTemplate,
18
18
  getProjectDir as getProjectDir2,
@@ -67,11 +67,11 @@ import {
67
67
 
68
68
  // src/lib/manager-worker.ts
69
69
  import { createHash as createHash2 } from "crypto";
70
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, appendFileSync, mkdirSync as mkdirSync2, chmodSync, existsSync as existsSync3, rmSync as rmSync2, readdirSync as readdirSync2, statSync, unlinkSync, copyFileSync } from "fs";
70
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, appendFileSync, mkdirSync as mkdirSync2, chmodSync, existsSync as existsSync4, rmSync as rmSync2, readdirSync as readdirSync2, statSync, unlinkSync, copyFileSync } from "fs";
71
71
  import https from "https";
72
72
  import { execFileSync as syncExecFile } from "child_process";
73
- import { join as join4, dirname } from "path";
74
- import { homedir as homedir3 } from "os";
73
+ import { join as join5, dirname } from "path";
74
+ import { homedir as homedir4 } from "os";
75
75
  import { fileURLToPath } from "url";
76
76
 
77
77
  // src/lib/mcp-config-drift.ts
@@ -435,12 +435,12 @@ function reapMissingMcpSessions(args) {
435
435
  if (!missingRaw.includes(key)) liveKeys.add(key);
436
436
  }
437
437
  for (const key of liveKeys) {
438
- const state3 = presenceReaperState.get(stateKey(codeName, key));
439
- if (state3) {
440
- state3.attempts = 0;
441
- state3.lastSeenLiveAt = now();
442
- state3.lastAttemptedSessionStartedAt = null;
443
- state3.gaveUpLogged = false;
438
+ const state4 = presenceReaperState.get(stateKey(codeName, key));
439
+ if (state4) {
440
+ state4.attempts = 0;
441
+ state4.lastSeenLiveAt = now();
442
+ state4.lastAttemptedSessionStartedAt = null;
443
+ state4.gaveUpLogged = false;
444
444
  }
445
445
  }
446
446
  if (missing.length === 0) {
@@ -455,24 +455,24 @@ function reapMissingMcpSessions(args) {
455
455
  const active = [];
456
456
  for (const key of missing) {
457
457
  const sk = stateKey(codeName, key);
458
- const state3 = presenceReaperState.get(sk) ?? {
458
+ const state4 = presenceReaperState.get(sk) ?? {
459
459
  attempts: 0,
460
460
  lastSeenLiveAt: null,
461
461
  lastAttemptedSessionStartedAt: null,
462
462
  gaveUpLogged: false
463
463
  };
464
- if (state3.lastAttemptedSessionStartedAt !== sessionStartedAt) {
465
- state3.attempts += 1;
466
- state3.lastAttemptedSessionStartedAt = sessionStartedAt;
464
+ if (state4.lastAttemptedSessionStartedAt !== sessionStartedAt) {
465
+ state4.attempts += 1;
466
+ state4.lastAttemptedSessionStartedAt = sessionStartedAt;
467
467
  }
468
- presenceReaperState.set(sk, state3);
469
- if (state3.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
468
+ presenceReaperState.set(sk, state4);
469
+ if (state4.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
470
470
  givenUp.push(key);
471
- if (!state3.gaveUpLogged) {
471
+ if (!state4.gaveUpLogged) {
472
472
  log2(
473
473
  `[mcp-presence-reaper] giving up on '${codeName}:${key}' after ${MAX_PRESENCE_RESTART_ATTEMPTS} consecutive failed restarts \u2014 declared but never recovers (likely orphaned config; see ENG-5279)`
474
474
  );
475
- state3.gaveUpLogged = true;
475
+ state4.gaveUpLogged = true;
476
476
  if (onGiveUp) {
477
477
  try {
478
478
  onGiveUp(codeName, key);
@@ -710,6 +710,79 @@ async function maybeReportUsageBanner(args) {
710
710
  }
711
711
  }
712
712
 
713
+ // src/lib/activity-cache-monitor.ts
714
+ import { existsSync, readFileSync } from "fs";
715
+ import { homedir } from "os";
716
+ import { join } from "path";
717
+ var MIN_CHECK_INTERVAL_MS2 = 6e4;
718
+ var STATS_CACHE_PATH = join(homedir(), ".claude", "stats-cache.json");
719
+ var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
720
+ var state2 = { lastObservedDate: null, lastCheckedAt: 0 };
721
+ function selectNewDailyRows(raw, lastObservedDate) {
722
+ let parsed;
723
+ try {
724
+ parsed = JSON.parse(raw);
725
+ } catch {
726
+ return [];
727
+ }
728
+ const days = Array.isArray(parsed.dailyActivity) ? parsed.dailyActivity : [];
729
+ const nonNegInt = (v) => {
730
+ if (typeof v !== "number" || !Number.isFinite(v)) return null;
731
+ const n = Math.trunc(v);
732
+ return n >= 0 ? n : null;
733
+ };
734
+ const result = [];
735
+ for (const d of days) {
736
+ if (typeof d.date !== "string" || !ISO_DATE_RE.test(d.date)) continue;
737
+ if (lastObservedDate !== null && d.date <= lastObservedDate) continue;
738
+ const m = nonNegInt(d.messageCount);
739
+ const s = nonNegInt(d.sessionCount);
740
+ const t = nonNegInt(d.toolCallCount);
741
+ if (m === null || s === null || t === null) continue;
742
+ const breakdownForDay = parsed.dailyModelTokens?.[d.date];
743
+ const model_breakdown = breakdownForDay && typeof breakdownForDay === "object" && !Array.isArray(breakdownForDay) ? breakdownForDay : null;
744
+ result.push({
745
+ date: d.date,
746
+ message_count: m,
747
+ session_count: s,
748
+ tool_call_count: t,
749
+ model_breakdown
750
+ });
751
+ }
752
+ result.sort((a, b) => a.date.localeCompare(b.date));
753
+ return result;
754
+ }
755
+ async function maybeReportActivityCache(args) {
756
+ const { api: api2, log: log2 } = args;
757
+ const now = args.now ?? /* @__PURE__ */ new Date();
758
+ const nowMs = now.getTime();
759
+ if (nowMs - state2.lastCheckedAt < MIN_CHECK_INTERVAL_MS2) return;
760
+ state2.lastCheckedAt = nowMs;
761
+ if (!existsSync(STATS_CACHE_PATH)) {
762
+ return;
763
+ }
764
+ let raw;
765
+ try {
766
+ raw = readFileSync(STATS_CACHE_PATH, "utf-8");
767
+ } catch (err) {
768
+ log2(`[activity-cache] readFileSync failed: ${err.message}`);
769
+ return;
770
+ }
771
+ const rows = selectNewDailyRows(raw, state2.lastObservedDate);
772
+ if (rows.length === 0) return;
773
+ for (const row of rows) {
774
+ try {
775
+ await api2.post("/host/activity-observations", row);
776
+ state2.lastObservedDate = row.date;
777
+ } catch (err) {
778
+ log2(
779
+ `[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
780
+ );
781
+ return;
782
+ }
783
+ }
784
+ }
785
+
713
786
  // src/lib/poll-backoff.ts
714
787
  var POLL_BACKOFF_BASE_MS = 1e3;
715
788
  var POLL_BACKOFF_MAX_MS = 6e4;
@@ -1139,8 +1212,8 @@ var GatewayClientPool = class extends EventEmitter {
1139
1212
 
1140
1213
  // src/lib/claude-auth-detect.ts
1141
1214
  import { readFile, readdir } from "fs/promises";
1142
- import { homedir, platform } from "os";
1143
- import { join } from "path";
1215
+ import { homedir as homedir2, platform } from "os";
1216
+ import { join as join2 } from "path";
1144
1217
  import { execFile } from "child_process";
1145
1218
  import { promisify } from "util";
1146
1219
  var execFileAsync = promisify(execFile);
@@ -1155,8 +1228,8 @@ async function detectClaudeAuth() {
1155
1228
  }
1156
1229
  async function findClaudeCredentialsPaths() {
1157
1230
  const candidates = [
1158
- join(homedir(), ".claude", ".credentials.json"),
1159
- join(homedir(), ".claude", "credentials.json")
1231
+ join2(homedir2(), ".claude", ".credentials.json"),
1232
+ join2(homedir2(), ".claude", "credentials.json")
1160
1233
  ];
1161
1234
  const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
1162
1235
  if (isLinuxRoot) {
@@ -1164,8 +1237,8 @@ async function findClaudeCredentialsPaths() {
1164
1237
  const entries = await readdir("/home", { withFileTypes: true });
1165
1238
  for (const entry of entries) {
1166
1239
  if (!entry.isDirectory()) continue;
1167
- candidates.push(join("/home", entry.name, ".claude", ".credentials.json"));
1168
- candidates.push(join("/home", entry.name, ".claude", "credentials.json"));
1240
+ candidates.push(join2("/home", entry.name, ".claude", ".credentials.json"));
1241
+ candidates.push(join2("/home", entry.name, ".claude", "credentials.json"));
1169
1242
  }
1170
1243
  } catch {
1171
1244
  }
@@ -1257,18 +1330,18 @@ function normalize(value) {
1257
1330
  }
1258
1331
 
1259
1332
  // src/lib/channel-hash-cache.ts
1260
- import { existsSync, readFileSync, writeFileSync } from "fs";
1261
- import { join as join2 } from "path";
1333
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
1334
+ import { join as join3 } from "path";
1262
1335
  var CACHE_FILENAME = "channel-hash-cache.json";
1263
1336
  function getChannelHashCacheFile(configDir) {
1264
- return join2(configDir, CACHE_FILENAME);
1337
+ return join3(configDir, CACHE_FILENAME);
1265
1338
  }
1266
1339
  function loadChannelHashCache(target, configDir) {
1267
1340
  const path = getChannelHashCacheFile(configDir);
1268
- if (!existsSync(path)) return;
1341
+ if (!existsSync2(path)) return;
1269
1342
  let parsed;
1270
1343
  try {
1271
- parsed = JSON.parse(readFileSync(path, "utf-8"));
1344
+ parsed = JSON.parse(readFileSync2(path, "utf-8"));
1272
1345
  } catch {
1273
1346
  return;
1274
1347
  }
@@ -1741,24 +1814,24 @@ function withScheduleLinkFooter(opts) {
1741
1814
  }
1742
1815
 
1743
1816
  // src/lib/restart-flags.ts
1744
- import { existsSync as existsSync2, mkdirSync, readdirSync, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
1745
- import { homedir as homedir2 } from "os";
1746
- import { join as join3 } from "path";
1817
+ import { existsSync as existsSync3, mkdirSync, readdirSync, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
1818
+ import { homedir as homedir3 } from "os";
1819
+ import { join as join4 } from "path";
1747
1820
  import { randomUUID } from "crypto";
1748
1821
  function restartFlagsDir() {
1749
- return join3(homedir2(), ".augmented", "restart-flags");
1822
+ return join4(homedir3(), ".augmented", "restart-flags");
1750
1823
  }
1751
1824
  function flagPath(codeName) {
1752
- return join3(restartFlagsDir(), `${codeName}.flag`);
1825
+ return join4(restartFlagsDir(), `${codeName}.flag`);
1753
1826
  }
1754
1827
  function readRestartFlags() {
1755
1828
  const dir = restartFlagsDir();
1756
- if (!existsSync2(dir)) return [];
1829
+ if (!existsSync3(dir)) return [];
1757
1830
  const out = [];
1758
1831
  for (const entry of readdirSync(dir)) {
1759
1832
  if (!entry.endsWith(".flag")) continue;
1760
1833
  try {
1761
- const raw = readFileSync2(join3(dir, entry), "utf8");
1834
+ const raw = readFileSync3(join4(dir, entry), "utf8");
1762
1835
  const parsed = JSON.parse(raw);
1763
1836
  if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
1764
1837
  parsed.codeName = entry.replace(/\.flag$/, "");
@@ -1776,7 +1849,7 @@ function readRestartFlags() {
1776
1849
  }
1777
1850
  function deleteRestartFlag(codeName) {
1778
1851
  const path = flagPath(codeName);
1779
- if (existsSync2(path)) {
1852
+ if (existsSync3(path)) {
1780
1853
  rmSync(path, { force: true });
1781
1854
  }
1782
1855
  }
@@ -2274,8 +2347,8 @@ function applyRestartAcks(args) {
2274
2347
  var GATEWAY_PORT_BASE = 18800;
2275
2348
  var GATEWAY_PORT_STEP = 10;
2276
2349
  var GATEWAY_PORT_MAX = 18899;
2277
- var AUGMENTED_DIR = join4(process.env["HOME"] ?? "/tmp", ".augmented");
2278
- var GATEWAY_PORTS_FILE = join4(AUGMENTED_DIR, "gateway-ports.json");
2350
+ var AUGMENTED_DIR = join5(process.env["HOME"] ?? "/tmp", ".augmented");
2351
+ var GATEWAY_PORTS_FILE = join5(AUGMENTED_DIR, "gateway-ports.json");
2279
2352
  var CHANNEL_SWEEP_INTERVAL_MS = (() => {
2280
2353
  const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
2281
2354
  if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
@@ -2382,8 +2455,8 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
2382
2455
  function shouldSkipRevokedCleanup(previousKnownStatus) {
2383
2456
  return previousKnownStatus === "revoked";
2384
2457
  }
2385
- function hasRevokedResiduals(state3) {
2386
- return state3.gatewayRunning || state3.portAllocated || state3.provisionDirExists;
2458
+ function hasRevokedResiduals(state4) {
2459
+ return state4.gatewayRunning || state4.portAllocated || state4.provisionDirExists;
2387
2460
  }
2388
2461
  var knownVersions = /* @__PURE__ */ new Map();
2389
2462
  var knownStatuses = /* @__PURE__ */ new Map();
@@ -2449,7 +2522,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
2449
2522
  var runningMcpServerKeys = /* @__PURE__ */ new Map();
2450
2523
  function projectMcpHash(_codeName, projectDir) {
2451
2524
  try {
2452
- const raw = readFileSync3(join4(projectDir, ".mcp.json"), "utf-8");
2525
+ const raw = readFileSync4(join5(projectDir, ".mcp.json"), "utf-8");
2453
2526
  return createHash2("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
2454
2527
  } catch {
2455
2528
  return null;
@@ -2457,7 +2530,7 @@ function projectMcpHash(_codeName, projectDir) {
2457
2530
  }
2458
2531
  function projectMcpKeys(_codeName, projectDir) {
2459
2532
  try {
2460
- const raw = readFileSync3(join4(projectDir, ".mcp.json"), "utf-8");
2533
+ const raw = readFileSync4(join5(projectDir, ".mcp.json"), "utf-8");
2461
2534
  const parsed = JSON.parse(raw);
2462
2535
  const servers = parsed.mcpServers;
2463
2536
  if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
@@ -2554,7 +2627,7 @@ var codeNameToAgentId = /* @__PURE__ */ new Map();
2554
2627
  var agentChannelTokens = /* @__PURE__ */ new Map();
2555
2628
  var activeChannels = /* @__PURE__ */ new Map();
2556
2629
  var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
2557
- var state2 = {
2630
+ var state3 = {
2558
2631
  pid: process.pid,
2559
2632
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2560
2633
  lastPollAt: null,
@@ -2588,6 +2661,7 @@ function clearAgentCaches(agentId, codeName) {
2588
2661
  lastHarvestAt.delete(codeName);
2589
2662
  claudeSchedulerStates.delete(codeName);
2590
2663
  claudeTaskConcurrency.delete(codeName);
2664
+ lastMcpFailedBannerCount.delete(codeName);
2591
2665
  memoryFileHashes.delete(agentId);
2592
2666
  lastDownloadHash.delete(agentId);
2593
2667
  lastLocalFileHash.delete(agentId);
@@ -2610,7 +2684,7 @@ var cachedFrameworkVersion = null;
2610
2684
  var lastVersionCheckAt = 0;
2611
2685
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
2612
2686
  var lastResponsivenessProbeAt = 0;
2613
- var agtCliVersion = true ? "0.23.2" : "dev";
2687
+ var agtCliVersion = true ? "0.24.0" : "dev";
2614
2688
  function resolveBrewPath(execFileSync4) {
2615
2689
  try {
2616
2690
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -2623,7 +2697,7 @@ function resolveBrewPath(execFileSync4) {
2623
2697
  "/usr/local/bin/brew"
2624
2698
  ];
2625
2699
  for (const path of fallbacks) {
2626
- if (existsSync3(path)) return path;
2700
+ if (existsSync4(path)) return path;
2627
2701
  }
2628
2702
  return null;
2629
2703
  }
@@ -2633,7 +2707,7 @@ function claudeBinaryInstalled(execFileSync4) {
2633
2707
  "/opt/homebrew/bin/claude",
2634
2708
  "/usr/local/bin/claude"
2635
2709
  ];
2636
- if (canonical.some((path) => existsSync3(path))) return true;
2710
+ if (canonical.some((path) => existsSync4(path))) return true;
2637
2711
  try {
2638
2712
  execFileSync4("which", ["claude"], { timeout: 5e3 });
2639
2713
  return true;
@@ -2815,7 +2889,7 @@ async function ensureFrameworkBinary(frameworkId) {
2815
2889
  if (!process.env.PATH?.split(":").includes(brewBinDir)) {
2816
2890
  process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
2817
2891
  }
2818
- if (existsSync3("/home/linuxbrew/.linuxbrew/bin/claude")) {
2892
+ if (existsSync4("/home/linuxbrew/.linuxbrew/bin/claude")) {
2819
2893
  log("Claude Code installed successfully");
2820
2894
  } else {
2821
2895
  log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
@@ -2827,7 +2901,7 @@ async function ensureFrameworkBinary(frameworkId) {
2827
2901
  var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
2828
2902
  var claudeCodeUpgradeInFlight = false;
2829
2903
  function claudeCodeUpgradeMarkerPath() {
2830
- return join4(homedir3(), ".augmented", ".last-claude-code-upgrade-check");
2904
+ return join5(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
2831
2905
  }
2832
2906
  function stampClaudeCodeUpgradeMarker() {
2833
2907
  try {
@@ -2837,7 +2911,7 @@ function stampClaudeCodeUpgradeMarker() {
2837
2911
  }
2838
2912
  function claudeCodeUpgradeThrottled() {
2839
2913
  try {
2840
- const lastCheck = parseInt(readFileSync3(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
2914
+ const lastCheck = parseInt(readFileSync4(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
2841
2915
  if (!Number.isFinite(lastCheck)) return false;
2842
2916
  return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
2843
2917
  } catch {
@@ -2907,7 +2981,7 @@ async function checkAndUpdateCli() {
2907
2981
  const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
2908
2982
  if (!isBrewFormula && !isNpmGlobal) return;
2909
2983
  const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
2910
- const markerPath = join4(homedir3(), ".augmented", ".last-update-check");
2984
+ const markerPath = join5(homedir4(), ".augmented", ".last-update-check");
2911
2985
  try {
2912
2986
  const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
2913
2987
  if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
@@ -2972,17 +3046,18 @@ async function checkAndUpdateCliViaBrew() {
2972
3046
  async function checkAndUpdateCliViaNpm() {
2973
3047
  const { execFileSync: execFileSync4 } = await import("child_process");
2974
3048
  if (agtCliVersion === "dev") return;
3049
+ const channel = process.env.AGT_CLI_RELEASE_CHANNEL || "latest";
2975
3050
  let latest;
2976
3051
  try {
2977
3052
  const res = await fetch(
2978
- "https://registry.npmjs.org/@integrity-labs/agt-cli/latest",
3053
+ `https://registry.npmjs.org/@integrity-labs/agt-cli/${encodeURIComponent(channel)}`,
2979
3054
  {
2980
3055
  signal: AbortSignal.timeout(1e4),
2981
3056
  headers: { Accept: "application/json" }
2982
3057
  }
2983
3058
  );
2984
3059
  if (!res.ok) {
2985
- log(`[self-update] npm registry returned ${res.status}`);
3060
+ log(`[self-update] npm registry returned ${res.status} (channel=${channel})`);
2986
3061
  return;
2987
3062
  }
2988
3063
  const body = await res.json();
@@ -2995,14 +3070,15 @@ async function checkAndUpdateCliViaNpm() {
2995
3070
  log(`[self-update] npm registry fetch failed: ${err.message}`);
2996
3071
  return;
2997
3072
  }
2998
- if (!isNewerSemver(agtCliVersion, latest)) {
3073
+ const shouldUpdate = channel === "latest" ? isNewerSemver(agtCliVersion, latest) : latest !== agtCliVersion;
3074
+ if (!shouldUpdate) {
2999
3075
  if (!selfUpdateUpToDateLogged) {
3000
- log(`[self-update] agt CLI is up to date (npm, ${agtCliVersion})`);
3076
+ log(`[self-update] agt CLI is up to date (npm, channel=${channel}, ${agtCliVersion})`);
3001
3077
  selfUpdateUpToDateLogged = true;
3002
3078
  }
3003
3079
  return;
3004
3080
  }
3005
- log(`[self-update] agt CLI update available: ${agtCliVersion} \u2192 ${latest}. Upgrading via npm...`);
3081
+ log(`[self-update] agt CLI update available: ${agtCliVersion} \u2192 ${latest} (channel=${channel}). Upgrading via npm...`);
3006
3082
  const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
3007
3083
  const cmd = isRoot ? "npm" : "sudo";
3008
3084
  const args = isRoot ? [
@@ -3069,10 +3145,10 @@ async function applyClaudeAuthToEnv(childEnv, label) {
3069
3145
  throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
3070
3146
  }
3071
3147
  childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
3072
- const claudeDir = join4(homedir3(), ".claude");
3148
+ const claudeDir = join5(homedir4(), ".claude");
3073
3149
  for (const filename of [".credentials.json", "credentials.json"]) {
3074
- const p = join4(claudeDir, filename);
3075
- if (existsSync3(p)) {
3150
+ const p = join5(claudeDir, filename);
3151
+ if (existsSync4(p)) {
3076
3152
  try {
3077
3153
  rmSync2(p, { force: true });
3078
3154
  log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
@@ -3086,7 +3162,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
3086
3162
  }
3087
3163
  function loadGatewayPorts() {
3088
3164
  try {
3089
- return JSON.parse(readFileSync3(GATEWAY_PORTS_FILE, "utf-8"));
3165
+ return JSON.parse(readFileSync4(GATEWAY_PORTS_FILE, "utf-8"));
3090
3166
  } catch {
3091
3167
  return {};
3092
3168
  }
@@ -3116,10 +3192,10 @@ function freePort(codeName) {
3116
3192
  }
3117
3193
  }
3118
3194
  function getStateFile() {
3119
- return join4(config?.configDir ?? join4(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
3195
+ return join5(config?.configDir ?? join5(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
3120
3196
  }
3121
3197
  function channelHashCacheDir() {
3122
- return config?.configDir ?? join4(process.env["HOME"] ?? "/tmp", ".augmented");
3198
+ return config?.configDir ?? join5(process.env["HOME"] ?? "/tmp", ".augmented");
3123
3199
  }
3124
3200
  function loadChannelHashCache2() {
3125
3201
  loadChannelHashCache(knownChannelConfigHashes, channelHashCacheDir());
@@ -3161,9 +3237,9 @@ function log(msg) {
3161
3237
  `;
3162
3238
  if (!managerLogPath) {
3163
3239
  try {
3164
- managerLogPath = join4(homedir3(), ".augmented", "manager.log");
3240
+ managerLogPath = join5(homedir4(), ".augmented", "manager.log");
3165
3241
  mkdirSync2(dirname(managerLogPath), { recursive: true });
3166
- if (existsSync3(managerLogPath)) {
3242
+ if (existsSync4(managerLogPath)) {
3167
3243
  chmodSync(managerLogPath, 384);
3168
3244
  }
3169
3245
  } catch {
@@ -3191,7 +3267,7 @@ function sha256(content) {
3191
3267
  }
3192
3268
  function hashFile(filePath) {
3193
3269
  try {
3194
- const content = readFileSync3(filePath, "utf-8");
3270
+ const content = readFileSync4(filePath, "utf-8");
3195
3271
  return sha256(content);
3196
3272
  } catch {
3197
3273
  return null;
@@ -3216,12 +3292,12 @@ function parseSkillFrontmatter(content) {
3216
3292
  }
3217
3293
  async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
3218
3294
  const { readdirSync: readdirSync3, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync4 } = await import("fs");
3219
- const skillsDir = join4(configDir, codeName, "project", ".claude", "skills");
3220
- const claudeMdPath = join4(configDir, codeName, "project", "CLAUDE.md");
3295
+ const skillsDir = join5(configDir, codeName, "project", ".claude", "skills");
3296
+ const claudeMdPath = join5(configDir, codeName, "project", "CLAUDE.md");
3221
3297
  if (!ex(skillsDir) || !ex(claudeMdPath)) return;
3222
3298
  const entries = [];
3223
3299
  for (const dir of readdirSync3(skillsDir).sort()) {
3224
- const skillFile = join4(skillsDir, dir, "SKILL.md");
3300
+ const skillFile = join5(skillsDir, dir, "SKILL.md");
3225
3301
  if (!ex(skillFile)) continue;
3226
3302
  try {
3227
3303
  const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
@@ -3269,10 +3345,10 @@ ${SKILLS_INDEX_END}`;
3269
3345
  }
3270
3346
  async function migrateToProfiles() {
3271
3347
  const homeDir = process.env["HOME"] ?? "/tmp";
3272
- const sharedConfigPath = join4(homeDir, ".openclaw", "openclaw.json");
3348
+ const sharedConfigPath = join5(homeDir, ".openclaw", "openclaw.json");
3273
3349
  let sharedConfig;
3274
3350
  try {
3275
- sharedConfig = JSON.parse(readFileSync3(sharedConfigPath, "utf-8"));
3351
+ sharedConfig = JSON.parse(readFileSync4(sharedConfigPath, "utf-8"));
3276
3352
  } catch {
3277
3353
  return;
3278
3354
  }
@@ -3285,19 +3361,19 @@ async function migrateToProfiles() {
3285
3361
  const codeName = agentEntry["id"];
3286
3362
  if (!codeName) continue;
3287
3363
  if (codeName === "main") continue;
3288
- const profileDir = join4(homeDir, `.openclaw-${codeName}`);
3289
- if (existsSync3(join4(profileDir, "openclaw.json"))) continue;
3364
+ const profileDir = join5(homeDir, `.openclaw-${codeName}`);
3365
+ if (existsSync4(join5(profileDir, "openclaw.json"))) continue;
3290
3366
  log(`Migrating agent '${codeName}' to per-agent profile`);
3291
3367
  if (adapter.seedProfileConfig) {
3292
3368
  adapter.seedProfileConfig(codeName);
3293
3369
  }
3294
- const sharedAuthDir = join4(homeDir, ".openclaw", "agents", codeName, "agent");
3295
- const profileAuthDir = join4(profileDir, "agents", codeName, "agent");
3296
- const authFile = join4(sharedAuthDir, "auth-profiles.json");
3297
- if (existsSync3(authFile)) {
3370
+ const sharedAuthDir = join5(homeDir, ".openclaw", "agents", codeName, "agent");
3371
+ const profileAuthDir = join5(profileDir, "agents", codeName, "agent");
3372
+ const authFile = join5(sharedAuthDir, "auth-profiles.json");
3373
+ if (existsSync4(authFile)) {
3298
3374
  mkdirSync2(profileAuthDir, { recursive: true });
3299
- const authContent = readFileSync3(authFile, "utf-8");
3300
- writeFileSync3(join4(profileAuthDir, "auth-profiles.json"), authContent);
3375
+ const authContent = readFileSync4(authFile, "utf-8");
3376
+ writeFileSync3(join5(profileAuthDir, "auth-profiles.json"), authContent);
3301
3377
  }
3302
3378
  allocatePort(codeName);
3303
3379
  migrated++;
@@ -3335,7 +3411,7 @@ function readGatewayToken(codeName) {
3335
3411
  }
3336
3412
  const homeDir = process.env["HOME"] ?? "/tmp";
3337
3413
  try {
3338
- const cfg = JSON.parse(readFileSync3(join4(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
3414
+ const cfg = JSON.parse(readFileSync4(join5(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
3339
3415
  return cfg?.gateway?.auth?.token;
3340
3416
  } catch {
3341
3417
  return void 0;
@@ -3344,18 +3420,18 @@ function readGatewayToken(codeName) {
3344
3420
  var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
3345
3421
  function isGatewayHung(codeName) {
3346
3422
  const homeDir = process.env["HOME"] ?? "/tmp";
3347
- const jobsPath = join4(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
3348
- if (!existsSync3(jobsPath)) return false;
3423
+ const jobsPath = join5(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
3424
+ if (!existsSync4(jobsPath)) return false;
3349
3425
  try {
3350
- const data = JSON.parse(readFileSync3(jobsPath, "utf-8"));
3426
+ const data = JSON.parse(readFileSync4(jobsPath, "utf-8"));
3351
3427
  const jobs = data.jobs ?? data;
3352
3428
  if (!Array.isArray(jobs)) return false;
3353
3429
  const now = Date.now();
3354
3430
  for (const job of jobs) {
3355
- const state3 = job.state;
3356
- if (!state3) continue;
3357
- const runStartedAt = state3.runStartedAtMs;
3358
- const isRunning = state3.status === "running" || state3.running === true;
3431
+ const state4 = job.state;
3432
+ if (!state4) continue;
3433
+ const runStartedAt = state4.runStartedAtMs;
3434
+ const isRunning = state4.status === "running" || state4.running === true;
3359
3435
  if (isRunning && runStartedAt && now - runStartedAt > GATEWAY_HUNG_TIMEOUT_MS) {
3360
3436
  return true;
3361
3437
  }
@@ -3380,15 +3456,15 @@ async function ensureGatewayRunning(codeName, adapter) {
3380
3456
  }
3381
3457
  await new Promise((r) => setTimeout(r, 2e3));
3382
3458
  const homeDir = process.env["HOME"] ?? "/tmp";
3383
- const cronJobsPath = join4(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
3459
+ const cronJobsPath = join5(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
3384
3460
  clearStaleCronRunState(cronJobsPath);
3385
3461
  } else {
3386
3462
  if (status.port) {
3387
3463
  try {
3388
3464
  const homeDir = process.env["HOME"] ?? "/tmp";
3389
- const configPath = join4(homeDir, `.openclaw-${codeName}`, "openclaw.json");
3390
- if (existsSync3(configPath)) {
3391
- const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
3465
+ const configPath = join5(homeDir, `.openclaw-${codeName}`, "openclaw.json");
3466
+ if (existsSync4(configPath)) {
3467
+ const cfg = JSON.parse(readFileSync4(configPath, "utf-8"));
3392
3468
  if (cfg.gateway?.port !== status.port) {
3393
3469
  if (!cfg.gateway) cfg.gateway = {};
3394
3470
  cfg.gateway.port = status.port;
@@ -3412,9 +3488,9 @@ async function ensureGatewayRunning(codeName, adapter) {
3412
3488
  gatewaysStartedThisCycle.add(codeName);
3413
3489
  try {
3414
3490
  const homeDir = process.env["HOME"] ?? "/tmp";
3415
- const configPath = join4(homeDir, `.openclaw-${codeName}`, "openclaw.json");
3416
- if (existsSync3(configPath)) {
3417
- const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
3491
+ const configPath = join5(homeDir, `.openclaw-${codeName}`, "openclaw.json");
3492
+ if (existsSync4(configPath)) {
3493
+ const cfg = JSON.parse(readFileSync4(configPath, "utf-8"));
3418
3494
  if (!cfg.gateway) cfg.gateway = {};
3419
3495
  cfg.gateway.port = port;
3420
3496
  writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
@@ -3556,7 +3632,7 @@ async function pollCycle() {
3556
3632
  const now = Date.now();
3557
3633
  if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
3558
3634
  try {
3559
- const firstAgent = state2.agents[0];
3635
+ const firstAgent = state3.agents[0];
3560
3636
  const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework("openclaw");
3561
3637
  if (versionAdapter.getVersion) {
3562
3638
  cachedFrameworkVersion = await versionAdapter.getVersion();
@@ -3635,13 +3711,50 @@ async function pollCycle() {
3635
3711
  } catch (err) {
3636
3712
  log(`[responsiveness-probe] collection failed: ${err.message}`);
3637
3713
  }
3714
+ try {
3715
+ const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
3716
+ const observations = [];
3717
+ const pendingCacheCommits = [];
3718
+ for (const codeName of persistentSessionAgents) {
3719
+ const agentId = codeNameToAgentId.get(codeName);
3720
+ if (!agentId) continue;
3721
+ const tail = readPaneLogTail(codeName, 40);
3722
+ if (!tail) continue;
3723
+ const observed = scrapeMcpFailedBannerCount(tail);
3724
+ const count = observed ?? 0;
3725
+ const last = lastMcpFailedBannerCount.get(codeName);
3726
+ if (last === count) continue;
3727
+ observations.push({ agent_id: agentId, count });
3728
+ pendingCacheCommits.push({ codeName, count });
3729
+ }
3730
+ if (observations.length > 0) {
3731
+ void api.post(
3732
+ "/host/mcp-banner-observation",
3733
+ { observations }
3734
+ ).then((resp) => {
3735
+ const accepted = new Set(
3736
+ (resp?.results ?? []).filter((r) => r.ok).map((r) => r.agent_id)
3737
+ );
3738
+ for (const { codeName, count } of pendingCacheCommits) {
3739
+ const agentId = codeNameToAgentId.get(codeName);
3740
+ if (agentId && accepted.has(agentId)) {
3741
+ lastMcpFailedBannerCount.set(codeName, count);
3742
+ }
3743
+ }
3744
+ }).catch((err) => {
3745
+ log(`[mcp-banner] post failed: ${err.message}`);
3746
+ });
3747
+ }
3748
+ } catch (err) {
3749
+ log(`[mcp-banner] collection failed: ${err.message}`);
3750
+ }
3638
3751
  const data = await api.post("/host/agents", { host_id: hostId });
3639
3752
  const agents = data.agents ?? [];
3640
3753
  const restartAcks = /* @__PURE__ */ new Map();
3641
3754
  for (const agent of agents) {
3642
3755
  const requested = agent.restart_requested_at ?? null;
3643
3756
  if (!requested) continue;
3644
- const prev = state2.agents.find((a) => a.agentId === agent.agent_id);
3757
+ const prev = state3.agents.find((a) => a.agentId === agent.agent_id);
3645
3758
  const lastProcessed = prev?.lastRestartProcessedAt ?? null;
3646
3759
  if (lastProcessed && Date.parse(lastProcessed) >= Date.parse(requested)) continue;
3647
3760
  log(`[restart] Dashboard requested restart for '${agent.code_name}' at ${requested}`);
@@ -3664,7 +3777,7 @@ async function pollCycle() {
3664
3777
  await processAgent(agent, agentStates);
3665
3778
  } catch (err) {
3666
3779
  log(`Error processing agent '${agent.code_name}': ${err.message}`);
3667
- const existing = state2.agents.find((a) => a.agentId === agent.agent_id);
3780
+ const existing = state3.agents.find((a) => a.agentId === agent.agent_id);
3668
3781
  if (existing) {
3669
3782
  agentStates.push(existing);
3670
3783
  } else {
@@ -3687,14 +3800,15 @@ async function pollCycle() {
3687
3800
  }
3688
3801
  }
3689
3802
  }
3803
+ void maybeReportActivityCache({ api, log });
3690
3804
  const restartAckStateChanged = applyRestartAcks({
3691
3805
  agentStates,
3692
- priorAgents: state2.agents,
3806
+ priorAgents: state3.agents,
3693
3807
  restartAcks
3694
3808
  });
3695
3809
  if (restartAckStateChanged) {
3696
3810
  try {
3697
- const ackedState = { ...state2, agents: agentStates };
3811
+ const ackedState = { ...state3, agents: agentStates };
3698
3812
  writeFileSync3(getStateFile(), JSON.stringify(ackedState, null, 2));
3699
3813
  } catch (err) {
3700
3814
  log(`[restart] failed to persist ack immediately: ${err.message}`);
@@ -3712,7 +3826,7 @@ async function pollCycle() {
3712
3826
  } catch {
3713
3827
  }
3714
3828
  const currentIds = new Set(agents.map((a) => a.agent_id));
3715
- for (const prev of state2.agents) {
3829
+ for (const prev of state3.agents) {
3716
3830
  if (!currentIds.has(prev.agentId)) {
3717
3831
  log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
3718
3832
  const adapter = resolveAgentFramework(prev.codeName);
@@ -3725,7 +3839,7 @@ async function pollCycle() {
3725
3839
  }
3726
3840
  killAgentChannelProcesses(prev.codeName, { log });
3727
3841
  freePort(prev.codeName);
3728
- const agentDir = join4(adapter.getAgentDir(prev.codeName), "provision");
3842
+ const agentDir = join5(adapter.getAgentDir(prev.codeName), "provision");
3729
3843
  await cleanupAgentFiles(prev.codeName, agentDir);
3730
3844
  clearAgentCaches(prev.agentId, prev.codeName);
3731
3845
  }
@@ -3812,10 +3926,10 @@ async function pollCycle() {
3812
3926
  }
3813
3927
  } catch {
3814
3928
  }
3815
- state2 = {
3816
- ...state2,
3929
+ state3 = {
3930
+ ...state3,
3817
3931
  lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
3818
- pollCount: state2.pollCount + 1,
3932
+ pollCount: state3.pollCount + 1,
3819
3933
  agents: agentStates,
3820
3934
  // ENG-5441: serialise trip state on every poll so manager restarts
3821
3935
  // never silently clear a tripped breaker. Cheap — only tripped
@@ -3826,9 +3940,9 @@ async function pollCycle() {
3826
3940
  log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
3827
3941
  consecutivePollFailures = 0;
3828
3942
  }
3829
- send({ type: "state-update", state: state2 });
3943
+ send({ type: "state-update", state: state3 });
3830
3944
  } catch (err) {
3831
- state2.errorCount++;
3945
+ state3.errorCount++;
3832
3946
  const message = err.message;
3833
3947
  log(`Poll error: ${message}`);
3834
3948
  send({ type: "error", message });
@@ -3869,7 +3983,7 @@ async function processAgent(agent, agentStates) {
3869
3983
  }
3870
3984
  const now = (/* @__PURE__ */ new Date()).toISOString();
3871
3985
  const adapter = resolveAgentFramework(agent.code_name);
3872
- let agentDir = join4(adapter.getAgentDir(agent.code_name), "provision");
3986
+ let agentDir = join5(adapter.getAgentDir(agent.code_name), "provision");
3873
3987
  if (agent.status === "draft" || agent.status === "paused") {
3874
3988
  if (previousKnownStatus !== agent.status) {
3875
3989
  log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
@@ -3908,7 +4022,7 @@ async function processAgent(agent, agentStates) {
3908
4022
  const residuals = {
3909
4023
  gatewayRunning: gatewayLiveness.running,
3910
4024
  portAllocated: Object.prototype.hasOwnProperty.call(ports, agent.code_name),
3911
- provisionDirExists: existsSync3(agentDir)
4025
+ provisionDirExists: existsSync4(agentDir)
3912
4026
  };
3913
4027
  if (!hasRevokedResiduals(residuals)) {
3914
4028
  agentStates.push({
@@ -3990,7 +4104,7 @@ async function processAgent(agent, agentStates) {
3990
4104
  });
3991
4105
  } catch (err) {
3992
4106
  log(`Refresh failed for '${agent.code_name}': ${err.message}`);
3993
- const existing = state2.agents.find((a) => a.agentId === agent.agent_id);
4107
+ const existing = state3.agents.find((a) => a.agentId === agent.agent_id);
3994
4108
  agentStates.push(existing ?? {
3995
4109
  agentId: agent.agent_id,
3996
4110
  codeName: agent.code_name,
@@ -4038,7 +4152,7 @@ async function processAgent(agent, agentStates) {
4038
4152
  const frameworkId = refreshData.agent.framework ?? "openclaw";
4039
4153
  agentFrameworkCache.set(agent.code_name, frameworkId);
4040
4154
  const frameworkAdapter = getFramework(frameworkId);
4041
- agentDir = join4(frameworkAdapter.getAgentDir(agent.code_name), "provision");
4155
+ agentDir = join5(frameworkAdapter.getAgentDir(agent.code_name), "provision");
4042
4156
  cacheAgentDeliveryMetadata(agent.code_name, refreshData);
4043
4157
  if (frameworkAdapter.seedProfileConfig) {
4044
4158
  frameworkAdapter.seedProfileConfig(agent.code_name);
@@ -4046,7 +4160,7 @@ async function processAgent(agent, agentStates) {
4046
4160
  const charterVersion = refreshData.charter.version;
4047
4161
  const toolsVersion = refreshData.tools.version;
4048
4162
  const known = knownVersions.get(agent.agent_id);
4049
- let lastProvisionAt = state2.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
4163
+ let lastProvisionAt = state3.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
4050
4164
  const currentChannelIds = launchableChannelIds(refreshData.channel_configs);
4051
4165
  const previousChannelIds = knownChannels.get(agent.agent_id);
4052
4166
  const channelsChanged = !previousChannelIds || currentChannelIds.size !== previousChannelIds.size || [...currentChannelIds].some((ch) => !previousChannelIds.has(ch)) || [...previousChannelIds].some((ch) => !currentChannelIds.has(ch));
@@ -4069,7 +4183,7 @@ async function processAgent(agent, agentStates) {
4069
4183
  const changedFiles = [];
4070
4184
  mkdirSync2(agentDir, { recursive: true });
4071
4185
  for (const artifact of artifacts) {
4072
- const filePath = join4(agentDir, artifact.relativePath);
4186
+ const filePath = join5(agentDir, artifact.relativePath);
4073
4187
  let existingHash;
4074
4188
  let newHash;
4075
4189
  let writeContent = artifact.content;
@@ -4081,8 +4195,8 @@ async function processAgent(agent, agentStates) {
4081
4195
  };
4082
4196
  newHash = sha256(stripDynamicSections(artifact.content));
4083
4197
  try {
4084
- const projectClaudeMd = join4(config.configDir, agent.code_name, "project", "CLAUDE.md");
4085
- const existing = readFileSync3(projectClaudeMd, "utf-8");
4198
+ const projectClaudeMd = join5(config.configDir, agent.code_name, "project", "CLAUDE.md");
4199
+ const existing = readFileSync4(projectClaudeMd, "utf-8");
4086
4200
  existingHash = sha256(stripDynamicSections(existing));
4087
4201
  } catch {
4088
4202
  existingHash = null;
@@ -4100,7 +4214,7 @@ async function processAgent(agent, agentStates) {
4100
4214
  const generatorKeys = Object.keys(generatorServers);
4101
4215
  let existingRaw = "";
4102
4216
  try {
4103
- existingRaw = readFileSync3(filePath, "utf-8");
4217
+ existingRaw = readFileSync4(filePath, "utf-8");
4104
4218
  } catch {
4105
4219
  }
4106
4220
  const existingServers = parseMcp(existingRaw);
@@ -4122,22 +4236,22 @@ async function processAgent(agent, agentStates) {
4122
4236
  }
4123
4237
  }
4124
4238
  if (changedFiles.length > 0) {
4125
- const isFirst = !existsSync3(join4(agentDir, "CHARTER.md"));
4239
+ const isFirst = !existsSync4(join5(agentDir, "CHARTER.md"));
4126
4240
  const verb = isFirst ? "Provisioning" : "Updating";
4127
4241
  const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
4128
4242
  log(`${verb} '${agent.code_name}': ${fileNames}`);
4129
4243
  for (const file of changedFiles) {
4130
- const filePath = join4(agentDir, file.relativePath);
4244
+ const filePath = join5(agentDir, file.relativePath);
4131
4245
  mkdirSync2(dirname(filePath), { recursive: true });
4132
4246
  writeFileSync3(filePath, file.content);
4133
4247
  }
4134
4248
  try {
4135
- const provSkillsDir = join4(agentDir, ".claude", "skills");
4136
- if (existsSync3(provSkillsDir)) {
4249
+ const provSkillsDir = join5(agentDir, ".claude", "skills");
4250
+ if (existsSync4(provSkillsDir)) {
4137
4251
  for (const folder of readdirSync2(provSkillsDir)) {
4138
4252
  if (folder.startsWith("knowledge-")) {
4139
4253
  try {
4140
- rmSync2(join4(provSkillsDir, folder), { recursive: true });
4254
+ rmSync2(join5(provSkillsDir, folder), { recursive: true });
4141
4255
  } catch {
4142
4256
  }
4143
4257
  }
@@ -4150,7 +4264,7 @@ async function processAgent(agent, agentStates) {
4150
4264
  const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
4151
4265
  const hashes = /* @__PURE__ */ new Map();
4152
4266
  for (const file of trackedFiles2) {
4153
- const h = hashFile(join4(agentDir, file));
4267
+ const h = hashFile(join5(agentDir, file));
4154
4268
  if (h) hashes.set(file, h);
4155
4269
  }
4156
4270
  writtenHashes.set(agent.agent_id, hashes);
@@ -4194,10 +4308,10 @@ async function processAgent(agent, agentStates) {
4194
4308
  }
4195
4309
  let lastDriftCheckAt = now;
4196
4310
  const written = writtenHashes.get(agent.agent_id);
4197
- if (written && existsSync3(agentDir)) {
4311
+ if (written && existsSync4(agentDir)) {
4198
4312
  const driftedFiles = [];
4199
4313
  for (const [file, expectedHash] of written) {
4200
- const localHash = hashFile(join4(agentDir, file));
4314
+ const localHash = hashFile(join5(agentDir, file));
4201
4315
  if (localHash && localHash !== expectedHash) {
4202
4316
  driftedFiles.push(file);
4203
4317
  }
@@ -4208,7 +4322,7 @@ async function processAgent(agent, agentStates) {
4208
4322
  try {
4209
4323
  const localHashes = {};
4210
4324
  for (const file of driftedFiles) {
4211
- localHashes[file] = hashFile(join4(agentDir, file));
4325
+ localHashes[file] = hashFile(join5(agentDir, file));
4212
4326
  }
4213
4327
  await api.post("/host/drift", {
4214
4328
  agent_id: agent.agent_id,
@@ -4366,24 +4480,24 @@ async function processAgent(agent, agentStates) {
4366
4480
  if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
4367
4481
  try {
4368
4482
  const agentProvisionDir = agentDir;
4369
- const projectDir = join4(homedir3(), ".augmented", agent.code_name, "project");
4483
+ const projectDir = join5(homedir4(), ".augmented", agent.code_name, "project");
4370
4484
  mkdirSync2(agentProvisionDir, { recursive: true });
4371
4485
  mkdirSync2(projectDir, { recursive: true });
4372
- const provisionMcpPath = join4(agentProvisionDir, ".mcp.json");
4373
- const projectMcpPath = join4(projectDir, ".mcp.json");
4486
+ const provisionMcpPath = join5(agentProvisionDir, ".mcp.json");
4487
+ const projectMcpPath = join5(projectDir, ".mcp.json");
4374
4488
  let mcpConfig = { mcpServers: {} };
4375
4489
  try {
4376
- mcpConfig = JSON.parse(readFileSync3(provisionMcpPath, "utf-8"));
4490
+ mcpConfig = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
4377
4491
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
4378
4492
  } catch {
4379
4493
  }
4380
- const localDirectChatChannel = join4(homedir3(), ".augmented", "_mcp", "direct-chat-channel.js");
4494
+ const localDirectChatChannel = join5(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
4381
4495
  const directChatTeamSettings = refreshData.team?.settings;
4382
4496
  const directChatTz = (() => {
4383
4497
  const tz = directChatTeamSettings?.["timezone"];
4384
4498
  return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
4385
4499
  })();
4386
- if (existsSync3(localDirectChatChannel) && !mcpConfig.mcpServers["direct-chat"]) {
4500
+ if (existsSync4(localDirectChatChannel) && !mcpConfig.mcpServers["direct-chat"]) {
4387
4501
  mcpConfig.mcpServers["direct-chat"] = {
4388
4502
  command: "node",
4389
4503
  args: [localDirectChatChannel],
@@ -4399,8 +4513,8 @@ async function processAgent(agent, agentStates) {
4399
4513
  writeFileSync3(projectMcpPath, serialized);
4400
4514
  log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
4401
4515
  }
4402
- const staleChannelsPath = join4(projectDir, ".mcp-channels.json");
4403
- if (existsSync3(staleChannelsPath)) {
4516
+ const staleChannelsPath = join5(projectDir, ".mcp-channels.json");
4517
+ if (existsSync4(staleChannelsPath)) {
4404
4518
  try {
4405
4519
  rmSync2(staleChannelsPath, { force: true });
4406
4520
  } catch {
@@ -4410,7 +4524,7 @@ async function processAgent(agent, agentStates) {
4410
4524
  log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
4411
4525
  }
4412
4526
  }
4413
- let lastSecretsProvisionAt = state2.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
4527
+ let lastSecretsProvisionAt = state3.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
4414
4528
  let secretsHash = knownSecretsHashes.get(agent.agent_id) ?? null;
4415
4529
  try {
4416
4530
  const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
@@ -4466,11 +4580,11 @@ async function processAgent(agent, agentStates) {
4466
4580
  const intHash = computeIntegrationsHash(integrations);
4467
4581
  const prevIntHash = knownIntegrationHashes.get(agent.agent_id);
4468
4582
  if (intHash !== prevIntHash) {
4469
- const projectDir = join4(homedir3(), ".augmented", agent.code_name, "project");
4470
- const envIntPath = join4(projectDir, ".env.integrations");
4583
+ const projectDir = join5(homedir4(), ".augmented", agent.code_name, "project");
4584
+ const envIntPath = join5(projectDir, ".env.integrations");
4471
4585
  let preWriteEnv;
4472
4586
  try {
4473
- preWriteEnv = readFileSync3(envIntPath, "utf-8");
4587
+ preWriteEnv = readFileSync4(envIntPath, "utf-8");
4474
4588
  } catch {
4475
4589
  preWriteEnv = void 0;
4476
4590
  }
@@ -4482,9 +4596,9 @@ async function processAgent(agent, agentStates) {
4482
4596
  let rotationHandled = true;
4483
4597
  if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
4484
4598
  try {
4485
- const projectMcpPath = join4(projectDir, ".mcp.json");
4486
- const postWriteEnv = readFileSync3(envIntPath, "utf-8");
4487
- const mcpContent = readFileSync3(projectMcpPath, "utf-8");
4599
+ const projectMcpPath = join5(projectDir, ".mcp.json");
4600
+ const postWriteEnv = readFileSync4(envIntPath, "utf-8");
4601
+ const mcpContent = readFileSync4(projectMcpPath, "utf-8");
4488
4602
  const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
4489
4603
  const mcpJsonForReap = JSON.parse(mcpContent);
4490
4604
  const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
@@ -4550,8 +4664,8 @@ async function processAgent(agent, agentStates) {
4550
4664
  const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
4551
4665
  if (mcpPath) {
4552
4666
  try {
4553
- const { readFileSync: readFileSync4 } = await import("fs");
4554
- const mcpConfig = JSON.parse(readFileSync4(mcpPath, "utf-8"));
4667
+ const { readFileSync: readFileSync5 } = await import("fs");
4668
+ const mcpConfig = JSON.parse(readFileSync5(mcpPath, "utf-8"));
4555
4669
  if (mcpConfig.mcpServers) {
4556
4670
  const managedPrefixes = [
4557
4671
  "composio_",
@@ -4640,8 +4754,8 @@ async function processAgent(agent, agentStates) {
4640
4754
  if (agent.status === "active") {
4641
4755
  if (frameworkAdapter.installPlugin) {
4642
4756
  try {
4643
- const pluginPath = join4(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
4644
- if (existsSync3(pluginPath)) {
4757
+ const pluginPath = join5(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
4758
+ if (existsSync4(pluginPath)) {
4645
4759
  frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
4646
4760
  agtHost: requireHost(),
4647
4761
  agtApiKey: getApiKey() ?? void 0,
@@ -4707,20 +4821,20 @@ async function processAgent(agent, agentStates) {
4707
4821
  }
4708
4822
  try {
4709
4823
  const { readdirSync: readdirSync3, rmSync: rmSync3 } = await import("fs");
4710
- const { homedir: homedir4 } = await import("os");
4824
+ const { homedir: homedir5 } = await import("os");
4711
4825
  const frameworkId2 = frameworkAdapter.id;
4712
4826
  const candidateSkillDirs = [
4713
4827
  // Claude Code — framework runtime tree
4714
- join4(homedir4(), ".augmented", agent.code_name, "skills"),
4828
+ join5(homedir5(), ".augmented", agent.code_name, "skills"),
4715
4829
  // Claude Code — project tree
4716
- join4(homedir4(), ".augmented", agent.code_name, "project", ".claude", "skills"),
4830
+ join5(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
4717
4831
  // OpenClaw — framework runtime tree
4718
- join4(homedir4(), `.openclaw-${agent.code_name}`, "skills"),
4832
+ join5(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
4719
4833
  // Defensive: legacy provision-side path, not currently an
4720
4834
  // install target but cheap to sweep.
4721
- join4(agentDir, ".claude", "skills")
4835
+ join5(agentDir, ".claude", "skills")
4722
4836
  ];
4723
- const existingDirs = candidateSkillDirs.filter((d) => existsSync3(d));
4837
+ const existingDirs = candidateSkillDirs.filter((d) => existsSync4(d));
4724
4838
  const discoveredEntries = /* @__PURE__ */ new Set();
4725
4839
  for (const dir of existingDirs) {
4726
4840
  try {
@@ -4734,8 +4848,8 @@ async function processAgent(agent, agentStates) {
4734
4848
  }
4735
4849
  const removeSkillFolder = (entry, reason) => {
4736
4850
  for (const dir of existingDirs) {
4737
- const p = join4(dir, entry);
4738
- if (existsSync3(p)) {
4851
+ const p = join5(dir, entry);
4852
+ if (existsSync4(p)) {
4739
4853
  rmSync3(p, { recursive: true, force: true });
4740
4854
  }
4741
4855
  }
@@ -4895,8 +5009,8 @@ async function processAgent(agent, agentStates) {
4895
5009
  const sess = getSessionState(agent.code_name);
4896
5010
  let mcpJsonParsed = null;
4897
5011
  try {
4898
- const mcpPath = join4(getProjectDir(agent.code_name), ".mcp.json");
4899
- mcpJsonParsed = JSON.parse(readFileSync3(mcpPath, "utf-8"));
5012
+ const mcpPath = join5(getProjectDir(agent.code_name), ".mcp.json");
5013
+ mcpJsonParsed = JSON.parse(readFileSync4(mcpPath, "utf-8"));
4900
5014
  } catch {
4901
5015
  }
4902
5016
  reapMissingMcpSessions({
@@ -5045,10 +5159,10 @@ async function processAgent(agent, agentStates) {
5045
5159
  lastWorkTriggerAt.set(agent.code_name, triggerTs);
5046
5160
  if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
5047
5161
  const homeDir = process.env["HOME"] ?? "/tmp";
5048
- const jobsPath = join4(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
5049
- if (existsSync3(jobsPath)) {
5162
+ const jobsPath = join5(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
5163
+ if (existsSync4(jobsPath)) {
5050
5164
  try {
5051
- const jobsData = JSON.parse(readFileSync3(jobsPath, "utf-8"));
5165
+ const jobsData = JSON.parse(readFileSync4(jobsPath, "utf-8"));
5052
5166
  const kanbanJob = (jobsData.jobs ?? []).find(
5053
5167
  (j) => typeof j.name === "string" && j.name.includes("kanban-work")
5054
5168
  );
@@ -5189,10 +5303,10 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
5189
5303
  }
5190
5304
  }
5191
5305
  const trackedFiles = frameworkAdapter.driftTrackedFiles();
5192
- if (trackedFiles.length > 0 && existsSync3(agentDir)) {
5306
+ if (trackedFiles.length > 0 && existsSync4(agentDir)) {
5193
5307
  const hashes = /* @__PURE__ */ new Map();
5194
5308
  for (const file of trackedFiles) {
5195
- const h = hashFile(join4(agentDir, file));
5309
+ const h = hashFile(join5(agentDir, file));
5196
5310
  if (h) hashes.set(file, h);
5197
5311
  }
5198
5312
  writtenHashes.set(agent.agent_id, hashes);
@@ -5226,19 +5340,19 @@ function cleanupStaleSessions(codeName) {
5226
5340
  lastCleanupAt.set(codeName, Date.now());
5227
5341
  const homeDir = process.env["HOME"] ?? "/tmp";
5228
5342
  for (const agentDir of ["main", codeName]) {
5229
- const sessionsDir = join4(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
5343
+ const sessionsDir = join5(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
5230
5344
  cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
5231
5345
  }
5232
- const cronRunsDir = join4(homeDir, `.openclaw-${codeName}`, "cron", "runs");
5346
+ const cronRunsDir = join5(homeDir, `.openclaw-${codeName}`, "cron", "runs");
5233
5347
  cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
5234
- const cronJobsPath = join4(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5348
+ const cronJobsPath = join5(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
5235
5349
  clearStaleCronRunState(cronJobsPath);
5236
5350
  }
5237
5351
  function cleanupCronSessions(sessionsDir, keepCount) {
5238
- const indexPath = join4(sessionsDir, "sessions.json");
5239
- if (!existsSync3(indexPath)) return;
5352
+ const indexPath = join5(sessionsDir, "sessions.json");
5353
+ if (!existsSync4(indexPath)) return;
5240
5354
  try {
5241
- const raw = readFileSync3(indexPath, "utf-8");
5355
+ const raw = readFileSync4(indexPath, "utf-8");
5242
5356
  const index = JSON.parse(raw);
5243
5357
  const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
5244
5358
  key: k,
@@ -5251,9 +5365,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
5251
5365
  for (const entry of toDelete) {
5252
5366
  delete index[entry.key];
5253
5367
  if (entry.sessionId) {
5254
- const sessionFile = join4(sessionsDir, `${entry.sessionId}.jsonl`);
5368
+ const sessionFile = join5(sessionsDir, `${entry.sessionId}.jsonl`);
5255
5369
  try {
5256
- if (existsSync3(sessionFile)) {
5370
+ if (existsSync4(sessionFile)) {
5257
5371
  unlinkSync(sessionFile);
5258
5372
  deletedFiles++;
5259
5373
  }
@@ -5273,8 +5387,8 @@ function cleanupCronSessions(sessionsDir, keepCount) {
5273
5387
  delete index[parentKey];
5274
5388
  if (parentSessionId) {
5275
5389
  try {
5276
- const f = join4(sessionsDir, `${parentSessionId}.jsonl`);
5277
- if (existsSync3(f)) {
5390
+ const f = join5(sessionsDir, `${parentSessionId}.jsonl`);
5391
+ if (existsSync4(f)) {
5278
5392
  unlinkSync(f);
5279
5393
  deletedFiles++;
5280
5394
  }
@@ -5292,28 +5406,28 @@ function cleanupCronSessions(sessionsDir, keepCount) {
5292
5406
  }
5293
5407
  var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
5294
5408
  function clearStaleCronRunState(jobsPath) {
5295
- if (!existsSync3(jobsPath)) return;
5409
+ if (!existsSync4(jobsPath)) return;
5296
5410
  try {
5297
- const raw = readFileSync3(jobsPath, "utf-8");
5411
+ const raw = readFileSync4(jobsPath, "utf-8");
5298
5412
  const data = JSON.parse(raw);
5299
5413
  const jobs = data.jobs ?? data;
5300
5414
  if (!Array.isArray(jobs)) return;
5301
5415
  let changed = false;
5302
5416
  const now = Date.now();
5303
5417
  for (const job of jobs) {
5304
- const state3 = job.state;
5305
- if (!state3) continue;
5306
- const runStartedAt = state3.runningAtMs ?? state3.runStartedAtMs;
5307
- const isRunning = state3.running === true || state3.status === "running";
5418
+ const state4 = job.state;
5419
+ if (!state4) continue;
5420
+ const runStartedAt = state4.runningAtMs ?? state4.runStartedAtMs;
5421
+ const isRunning = state4.running === true || state4.status === "running";
5308
5422
  if (isRunning && runStartedAt && now - runStartedAt > STALE_RUN_TIMEOUT_MS) {
5309
- state3.running = false;
5310
- delete state3.status;
5311
- delete state3.runStartedAtMs;
5312
- delete state3.currentRunId;
5423
+ state4.running = false;
5424
+ delete state4.status;
5425
+ delete state4.runStartedAtMs;
5426
+ delete state4.currentRunId;
5313
5427
  changed = true;
5314
5428
  log(`Cleared stale running state for cron job '${job.name}' (stuck for ${Math.round((now - runStartedAt) / 6e4)}min)`);
5315
5429
  } else if (isRunning && !runStartedAt) {
5316
- state3.running = false;
5430
+ state4.running = false;
5317
5431
  changed = true;
5318
5432
  log(`Cleared stale running state for cron job '${job.name}' (no start timestamp)`);
5319
5433
  }
@@ -5325,13 +5439,13 @@ function clearStaleCronRunState(jobsPath) {
5325
5439
  }
5326
5440
  }
5327
5441
  function cleanupOldFiles(dir, maxAgeDays, ext) {
5328
- if (!existsSync3(dir)) return;
5442
+ if (!existsSync4(dir)) return;
5329
5443
  const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
5330
5444
  let removed = 0;
5331
5445
  try {
5332
5446
  for (const f of readdirSync2(dir)) {
5333
5447
  if (!f.endsWith(ext)) continue;
5334
- const fullPath = join4(dir, f);
5448
+ const fullPath = join5(dir, f);
5335
5449
  try {
5336
5450
  const st = statSync(fullPath);
5337
5451
  if (st.mtimeMs < cutoff) {
@@ -5377,16 +5491,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
5377
5491
  enabled: t.enabled ?? true,
5378
5492
  triggered_at: t.triggered_at ?? null
5379
5493
  }));
5380
- const state4 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
5381
- claudeSchedulerStates.set(codeName, state4);
5494
+ const state5 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
5495
+ claudeSchedulerStates.set(codeName, state5);
5382
5496
  knownTasksHashes.set(agent.agent_id, combinedHash);
5383
5497
  log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
5384
5498
  }
5385
5499
  if (!claudeSchedulerStates.has(codeName)) {
5386
5500
  claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
5387
5501
  }
5388
- const state3 = claudeSchedulerStates.get(codeName);
5389
- const ready = getReadyTasks(state3, inFlightClaudeTasks);
5502
+ const state4 = claudeSchedulerStates.get(codeName);
5503
+ const ready = getReadyTasks(state4, inFlightClaudeTasks);
5390
5504
  if (ready.length === 0) return;
5391
5505
  for (const task of ready) {
5392
5506
  if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
@@ -5475,7 +5589,7 @@ async function fetchPriorScheduledRuns(agentId, taskId) {
5475
5589
  }
5476
5590
  async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
5477
5591
  const projectDir = getProjectDir2(codeName);
5478
- const mcpConfigPath = join4(projectDir, ".mcp.json");
5592
+ const mcpConfigPath = join5(projectDir, ".mcp.json");
5479
5593
  let runId = null;
5480
5594
  let kanbanItemId = null;
5481
5595
  let taskResult;
@@ -5483,11 +5597,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
5483
5597
  const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
5484
5598
  prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
5485
5599
  try {
5486
- const claudeMdPath = join4(projectDir, "CLAUDE.md");
5600
+ const claudeMdPath = join5(projectDir, "CLAUDE.md");
5487
5601
  const serverNames = [];
5488
- if (existsSync3(mcpConfigPath)) {
5602
+ if (existsSync4(mcpConfigPath)) {
5489
5603
  try {
5490
- const d = JSON.parse(readFileSync3(mcpConfigPath, "utf-8"));
5604
+ const d = JSON.parse(readFileSync4(mcpConfigPath, "utf-8"));
5491
5605
  if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
5492
5606
  } catch {
5493
5607
  }
@@ -5506,14 +5620,14 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
5506
5620
  "--allowedTools",
5507
5621
  allowedTools
5508
5622
  ];
5509
- if (existsSync3(claudeMdPath)) {
5623
+ if (existsSync4(claudeMdPath)) {
5510
5624
  claudeArgs.push("--system-prompt-file", claudeMdPath);
5511
5625
  }
5512
5626
  const childEnv = { ...process.env };
5513
- const envIntPath = join4(projectDir, ".env.integrations");
5514
- if (existsSync3(envIntPath)) {
5627
+ const envIntPath = join5(projectDir, ".env.integrations");
5628
+ if (existsSync4(envIntPath)) {
5515
5629
  try {
5516
- for (const line of readFileSync3(envIntPath, "utf-8").split("\n")) {
5630
+ for (const line of readFileSync4(envIntPath, "utf-8").split("\n")) {
5517
5631
  if (!line || line.startsWith("#") || !line.includes("=")) continue;
5518
5632
  const eqIdx = line.indexOf("=");
5519
5633
  childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
@@ -5667,8 +5781,8 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
5667
5781
  }
5668
5782
  }
5669
5783
  function fireClaudeWorkTrigger(codeName, agentId, boardItems) {
5670
- const state3 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
5671
- const kanbanTask = findTaskByTemplate(state3, "kanban-work");
5784
+ const state4 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
5785
+ const kanbanTask = findTaskByTemplate(state4, "kanban-work");
5672
5786
  if (!kanbanTask) {
5673
5787
  log(`[claude-scheduler] Work trigger: no kanban-work task found for '${codeName}'`);
5674
5788
  return;
@@ -5691,13 +5805,14 @@ function fireClaudeWorkTrigger(codeName, agentId, boardItems) {
5691
5805
  });
5692
5806
  }
5693
5807
  var persistentSessionAgents = /* @__PURE__ */ new Set();
5808
+ var lastMcpFailedBannerCount = /* @__PURE__ */ new Map();
5694
5809
  var persistentSessionStuckTracker = new PersistentSessionStuckTracker();
5695
5810
  var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
5696
5811
  async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
5697
5812
  const codeName = agent.code_name;
5698
5813
  const projectDir = getProjectDir(codeName);
5699
- const mcpConfigPath = join4(projectDir, ".mcp.json");
5700
- const claudeMdPath = join4(projectDir, "CLAUDE.md");
5814
+ const mcpConfigPath = join5(projectDir, ".mcp.json");
5815
+ const claudeMdPath = join5(projectDir, "CLAUDE.md");
5701
5816
  if (restartBreaker.isTripped(codeName)) {
5702
5817
  const trip = restartBreaker.getTrip(codeName);
5703
5818
  return {
@@ -5934,9 +6049,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash2("sha256").
5934
6049
  } else if (!claudeSchedulerStates.has(codeName)) {
5935
6050
  claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
5936
6051
  }
5937
- const state3 = claudeSchedulerStates.get(codeName);
5938
- if (state3) {
5939
- const ready = getReadyTasks(state3, inFlightClaudeTasks);
6052
+ const state4 = claudeSchedulerStates.get(codeName);
6053
+ if (state4) {
6054
+ const ready = getReadyTasks(state4, inFlightClaudeTasks);
5940
6055
  if (ready.length > 0) {
5941
6056
  log(`[persistent-session] ${ready.length} ready task(s) for '${codeName}': ${ready.map((t) => `${t.name}(next=${t.nextFireAt ? new Date(t.nextFireAt).toISOString() : "null"})`).join(", ")}`);
5942
6057
  }
@@ -6315,11 +6430,11 @@ ${escapeXml(msg.content)}
6315
6430
  if (fw === "claude-code") {
6316
6431
  const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-XHJS2MZV.js");
6317
6432
  const projDir = ccProjectDir(agent.codeName);
6318
- const mcpConfigPath = join4(projDir, ".mcp.json");
6433
+ const mcpConfigPath = join5(projDir, ".mcp.json");
6319
6434
  const serverNames = [];
6320
- if (existsSync3(mcpConfigPath)) {
6435
+ if (existsSync4(mcpConfigPath)) {
6321
6436
  try {
6322
- const d = JSON.parse(readFileSync3(mcpConfigPath, "utf-8"));
6437
+ const d = JSON.parse(readFileSync4(mcpConfigPath, "utf-8"));
6323
6438
  if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
6324
6439
  } catch {
6325
6440
  }
@@ -6338,15 +6453,15 @@ ${escapeXml(msg.content)}
6338
6453
  "--allowedTools",
6339
6454
  allowedTools
6340
6455
  ];
6341
- const chatClaudeMd = join4(projDir, "CLAUDE.md");
6342
- if (existsSync3(chatClaudeMd)) {
6456
+ const chatClaudeMd = join5(projDir, "CLAUDE.md");
6457
+ if (existsSync4(chatClaudeMd)) {
6343
6458
  chatArgs.push("--system-prompt-file", chatClaudeMd);
6344
6459
  }
6345
- const envIntPath = join4(projDir, ".env.integrations");
6460
+ const envIntPath = join5(projDir, ".env.integrations");
6346
6461
  const childEnv = { ...process.env };
6347
- if (existsSync3(envIntPath)) {
6462
+ if (existsSync4(envIntPath)) {
6348
6463
  try {
6349
- for (const line of readFileSync3(envIntPath, "utf-8").split("\n")) {
6464
+ for (const line of readFileSync4(envIntPath, "utf-8").split("\n")) {
6350
6465
  if (!line || line.startsWith("#") || !line.includes("=")) continue;
6351
6466
  const eqIdx = line.indexOf("=");
6352
6467
  childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
@@ -6689,12 +6804,12 @@ function getBuiltInSkillContent(skillId) {
6689
6804
  if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
6690
6805
  try {
6691
6806
  const candidates = [
6692
- join4(process.cwd(), "skills", skillId, "SKILL.md"),
6693
- join4(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
6807
+ join5(process.cwd(), "skills", skillId, "SKILL.md"),
6808
+ join5(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
6694
6809
  ];
6695
6810
  for (const candidate of candidates) {
6696
- if (existsSync3(candidate)) {
6697
- const content = readFileSync3(candidate, "utf-8");
6811
+ if (existsSync4(candidate)) {
6812
+ const content = readFileSync4(candidate, "utf-8");
6698
6813
  const files = [{ relativePath: "SKILL.md", content }];
6699
6814
  builtInSkillCache.set(skillId, files);
6700
6815
  return files;
@@ -7602,8 +7717,8 @@ function parseMemoryFile(raw, fallbackName) {
7602
7717
  };
7603
7718
  }
7604
7719
  async function syncMemories(agent, configDir, log2) {
7605
- const projectDir = join4(configDir, agent.code_name, "project");
7606
- const memoryDir = join4(projectDir, "memory");
7720
+ const projectDir = join5(configDir, agent.code_name, "project");
7721
+ const memoryDir = join5(projectDir, "memory");
7607
7722
  const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
7608
7723
  if (isFreshSync) {
7609
7724
  log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
@@ -7614,14 +7729,14 @@ async function syncMemories(agent, configDir, log2) {
7614
7729
  }
7615
7730
  pendingFreshMemorySync.delete(agent.agent_id);
7616
7731
  }
7617
- if (existsSync3(memoryDir)) {
7732
+ if (existsSync4(memoryDir)) {
7618
7733
  const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
7619
7734
  const currentHashes = /* @__PURE__ */ new Map();
7620
7735
  const changedMemories = [];
7621
7736
  for (const file of readdirSync2(memoryDir)) {
7622
7737
  if (!file.endsWith(".md")) continue;
7623
7738
  try {
7624
- const raw = readFileSync3(join4(memoryDir, file), "utf-8");
7739
+ const raw = readFileSync4(join5(memoryDir, file), "utf-8");
7625
7740
  const fileHash = createHash2("sha256").update(raw).digest("hex").slice(0, 16);
7626
7741
  currentHashes.set(file, fileHash);
7627
7742
  if (prevHashes.get(file) === fileHash) continue;
@@ -7646,7 +7761,7 @@ async function syncMemories(agent, configDir, log2) {
7646
7761
  } catch (err) {
7647
7762
  for (const mem of changedMemories) {
7648
7763
  for (const [file] of currentHashes) {
7649
- const parsed = parseMemoryFile(readFileSync3(join4(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
7764
+ const parsed = parseMemoryFile(readFileSync4(join5(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
7650
7765
  if (parsed?.name === mem.name) currentHashes.delete(file);
7651
7766
  }
7652
7767
  }
@@ -7659,7 +7774,7 @@ async function syncMemories(agent, configDir, log2) {
7659
7774
  }
7660
7775
  }
7661
7776
  async function downloadMemories(agent, memoryDir, log2, { force }) {
7662
- const localFiles = existsSync3(memoryDir) ? readdirSync2(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
7777
+ const localFiles = existsSync4(memoryDir) ? readdirSync2(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
7663
7778
  const localListHash = createHash2("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
7664
7779
  const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
7665
7780
  const prevDownload = lastDownloadHash.get(agent.agent_id);
@@ -7681,7 +7796,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
7681
7796
  const mem = dbMemories.memories[i];
7682
7797
  const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
7683
7798
  const slug = rawSlug || `memory-${i}`;
7684
- const filePath = join4(memoryDir, `${slug}.md`);
7799
+ const filePath = join5(memoryDir, `${slug}.md`);
7685
7800
  const desired = `---
7686
7801
  name: ${JSON.stringify(mem.name)}
7687
7802
  type: ${mem.type}
@@ -7690,10 +7805,10 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
7690
7805
 
7691
7806
  ${mem.content}
7692
7807
  `;
7693
- if (existsSync3(filePath)) {
7808
+ if (existsSync4(filePath)) {
7694
7809
  let existing = "";
7695
7810
  try {
7696
- existing = readFileSync3(filePath, "utf-8");
7811
+ existing = readFileSync4(filePath, "utf-8");
7697
7812
  } catch {
7698
7813
  }
7699
7814
  if (existing === desired) continue;
@@ -7717,7 +7832,7 @@ ${mem.content}
7717
7832
  }
7718
7833
  }
7719
7834
  async function cleanupAgentFiles(codeName, agentDir) {
7720
- if (existsSync3(agentDir)) {
7835
+ if (existsSync4(agentDir)) {
7721
7836
  try {
7722
7837
  rmSync2(agentDir, { recursive: true, force: true });
7723
7838
  log(`Removed provision directory for '${codeName}'`);
@@ -7888,12 +8003,12 @@ function startManager(opts) {
7888
8003
  config = opts;
7889
8004
  try {
7890
8005
  const stateFile = getStateFile();
7891
- if (existsSync3(stateFile)) {
7892
- const raw = readFileSync3(stateFile, "utf-8");
8006
+ if (existsSync4(stateFile)) {
8007
+ const raw = readFileSync4(stateFile, "utf-8");
7893
8008
  const parsed = JSON.parse(raw);
7894
8009
  if (Array.isArray(parsed.agents)) {
7895
- state2.agents = parsed.agents;
7896
- log(`[startup] rehydrated ${state2.agents.length} agent state(s) from ${stateFile}`);
8010
+ state3.agents = parsed.agents;
8011
+ log(`[startup] rehydrated ${state3.agents.length} agent state(s) from ${stateFile}`);
7897
8012
  }
7898
8013
  if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
7899
8014
  restartBreaker.hydrate(parsed.circuitBreakerTrips);
@@ -7905,7 +8020,7 @@ function startManager(opts) {
7905
8020
  log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
7906
8021
  }
7907
8022
  log(
7908
- `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join4(homedir3(), ".augmented", "manager.log")}`
8023
+ `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join5(homedir4(), ".augmented", "manager.log")}`
7909
8024
  );
7910
8025
  warnOnConflictingKanbanModes();
7911
8026
  deployMcpAssets();
@@ -7968,14 +8083,14 @@ function restartRunningChannelMcps(basenames) {
7968
8083
  }
7969
8084
  }
7970
8085
  function deployMcpAssets() {
7971
- const targetDir = join4(homedir3(), ".augmented", "_mcp");
8086
+ const targetDir = join5(homedir4(), ".augmented", "_mcp");
7972
8087
  mkdirSync2(targetDir, { recursive: true });
7973
8088
  const moduleDir = dirname(fileURLToPath(import.meta.url));
7974
8089
  let mcpSourceDir = "";
7975
8090
  let dir = moduleDir;
7976
8091
  for (let i = 0; i < 6; i++) {
7977
- const candidate = join4(dir, "dist", "mcp");
7978
- if (existsSync3(join4(candidate, "index.js"))) {
8092
+ const candidate = join5(dir, "dist", "mcp");
8093
+ if (existsSync4(join5(candidate, "index.js"))) {
7979
8094
  mcpSourceDir = candidate;
7980
8095
  break;
7981
8096
  }
@@ -7990,8 +8105,8 @@ function deployMcpAssets() {
7990
8105
  const changedBasenames = [];
7991
8106
  const fileHash = (p) => {
7992
8107
  try {
7993
- if (!existsSync3(p)) return null;
7994
- return createHash2("sha256").update(readFileSync3(p)).digest("hex");
8108
+ if (!existsSync4(p)) return null;
8109
+ return createHash2("sha256").update(readFileSync4(p)).digest("hex");
7995
8110
  } catch {
7996
8111
  return null;
7997
8112
  }
@@ -8002,9 +8117,9 @@ function deployMcpAssets() {
8002
8117
  "telegram-channel.js"
8003
8118
  ]);
8004
8119
  for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
8005
- const src = join4(mcpSourceDir, file);
8006
- const dst = join4(targetDir, file);
8007
- if (!existsSync3(src)) continue;
8120
+ const src = join5(mcpSourceDir, file);
8121
+ const dst = join5(targetDir, file);
8122
+ if (!existsSync4(src)) continue;
8008
8123
  const before = fileHash(dst);
8009
8124
  try {
8010
8125
  copyFileSync(src, dst);
@@ -8021,16 +8136,16 @@ function deployMcpAssets() {
8021
8136
  log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
8022
8137
  restartRunningChannelMcps(changedBasenames);
8023
8138
  }
8024
- const localMcpPath = join4(targetDir, "index.js");
8139
+ const localMcpPath = join5(targetDir, "index.js");
8025
8140
  try {
8026
- const agentsDir = join4(homedir3(), ".augmented", "agents");
8027
- if (existsSync3(agentsDir)) {
8141
+ const agentsDir = join5(homedir4(), ".augmented", "agents");
8142
+ if (existsSync4(agentsDir)) {
8028
8143
  for (const entry of readdirSync2(agentsDir, { withFileTypes: true })) {
8029
8144
  if (!entry.isDirectory()) continue;
8030
8145
  for (const subdir of ["provision", "project"]) {
8031
- const mcpJsonPath = join4(agentsDir, entry.name, subdir, ".mcp.json");
8146
+ const mcpJsonPath = join5(agentsDir, entry.name, subdir, ".mcp.json");
8032
8147
  try {
8033
- const raw = readFileSync3(mcpJsonPath, "utf-8");
8148
+ const raw = readFileSync4(mcpJsonPath, "utf-8");
8034
8149
  if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
8035
8150
  const mcpConfig = JSON.parse(raw);
8036
8151
  const augServer = mcpConfig.mcpServers?.["augmented"];