@integrity-labs/agt-cli 0.27.84 → 0.27.86

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.
@@ -16,7 +16,7 @@ import {
16
16
  provisionStopHook,
17
17
  requireHost,
18
18
  safeWriteJsonAtomic
19
- } from "../chunk-WISOJYIV.js";
19
+ } from "../chunk-SXE77YAJ.js";
20
20
  import {
21
21
  getProjectDir as getProjectDir2,
22
22
  getReadyTasks,
@@ -53,7 +53,7 @@ import {
53
53
  stopPersistentSession,
54
54
  takeWatchdogGiveUpCount,
55
55
  takeZombieDetection
56
- } from "../chunk-S2QLE5BQ.js";
56
+ } from "../chunk-4LHN3FAL.js";
57
57
  import {
58
58
  KANBAN_CHECK_COMMAND,
59
59
  appendDmFooter,
@@ -76,7 +76,7 @@ import {
76
76
  resolveConnectivityProbe,
77
77
  resolveDmTarget,
78
78
  wrapScheduledTaskPrompt
79
- } from "../chunk-TXE2LLKI.js";
79
+ } from "../chunk-5IWPCN3V.js";
80
80
  import {
81
81
  parsePsRows,
82
82
  reapOrphanChannelMcps
@@ -84,10 +84,10 @@ import {
84
84
 
85
85
  // src/lib/manager-worker.ts
86
86
  import { createHash as createHash3 } from "crypto";
87
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, appendFileSync, mkdirSync as mkdirSync4, chmodSync, existsSync as existsSync5, rmSync as rmSync2, readdirSync as readdirSync3, statSync as statSync2, unlinkSync, copyFileSync } from "fs";
87
+ 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";
88
88
  import https from "https";
89
89
  import { execFileSync as syncExecFile } from "child_process";
90
- import { join as join7, dirname as dirname3 } from "path";
90
+ import { join as join8, dirname as dirname3 } from "path";
91
91
  import { homedir as homedir4 } from "os";
92
92
  import { fileURLToPath } from "url";
93
93
 
@@ -473,14 +473,14 @@ function reapMissingMcpSessions(args) {
473
473
  if (!missingRaw.includes(key)) liveKeys.add(key);
474
474
  }
475
475
  for (const key of liveKeys) {
476
- const state5 = presenceReaperState.get(stateKey(codeName, key));
477
- if (state5) {
478
- state5.attempts = 0;
479
- state5.lastSeenLiveAt = now();
480
- state5.lastAttemptedSessionStartedAt = null;
481
- state5.gaveUpLogged = false;
482
- state5.firstMissingAt = null;
483
- state5.quarantineLogged = false;
476
+ const state6 = presenceReaperState.get(stateKey(codeName, key));
477
+ if (state6) {
478
+ state6.attempts = 0;
479
+ state6.lastSeenLiveAt = now();
480
+ state6.lastAttemptedSessionStartedAt = null;
481
+ state6.gaveUpLogged = false;
482
+ state6.firstMissingAt = null;
483
+ state6.quarantineLogged = false;
484
484
  }
485
485
  }
486
486
  if (missing.length === 0) {
@@ -498,7 +498,7 @@ function reapMissingMcpSessions(args) {
498
498
  const wouldQuarantine = [];
499
499
  for (const key of missing) {
500
500
  const sk = stateKey(codeName, key);
501
- const state5 = presenceReaperState.get(sk) ?? {
501
+ const state6 = presenceReaperState.get(sk) ?? {
502
502
  attempts: 0,
503
503
  lastSeenLiveAt: null,
504
504
  lastAttemptedSessionStartedAt: null,
@@ -506,19 +506,19 @@ function reapMissingMcpSessions(args) {
506
506
  firstMissingAt: null,
507
507
  quarantineLogged: false
508
508
  };
509
- if (state5.firstMissingAt === null) state5.firstMissingAt = nowMs;
510
- if (state5.lastAttemptedSessionStartedAt !== sessionStartedAt) {
511
- state5.attempts += 1;
512
- state5.lastAttemptedSessionStartedAt = sessionStartedAt;
509
+ if (state6.firstMissingAt === null) state6.firstMissingAt = nowMs;
510
+ if (state6.lastAttemptedSessionStartedAt !== sessionStartedAt) {
511
+ state6.attempts += 1;
512
+ state6.lastAttemptedSessionStartedAt = sessionStartedAt;
513
513
  }
514
- presenceReaperState.set(sk, state5);
515
- if (state5.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
514
+ presenceReaperState.set(sk, state6);
515
+ if (state6.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
516
516
  givenUp.push(key);
517
- if (!state5.gaveUpLogged) {
517
+ if (!state6.gaveUpLogged) {
518
518
  log2(
519
519
  `[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)`
520
520
  );
521
- state5.gaveUpLogged = true;
521
+ state6.gaveUpLogged = true;
522
522
  if (onGiveUp) {
523
523
  try {
524
524
  onGiveUp(codeName, key);
@@ -529,10 +529,10 @@ function reapMissingMcpSessions(args) {
529
529
  }
530
530
  }
531
531
  }
532
- if (quarantineMode !== "off" && !state5.quarantineLogged && classifyKey(key) === "optional") {
533
- const dwellElapsed = quarantineDwellMs <= 0 || state5.firstMissingAt !== null && nowMs - state5.firstMissingAt >= quarantineDwellMs;
532
+ if (quarantineMode !== "off" && !state6.quarantineLogged && classifyKey(key) === "optional") {
533
+ const dwellElapsed = quarantineDwellMs <= 0 || state6.firstMissingAt !== null && nowMs - state6.firstMissingAt >= quarantineDwellMs;
534
534
  if (dwellElapsed) {
535
- state5.quarantineLogged = true;
535
+ state6.quarantineLogged = true;
536
536
  if (quarantineMode === "enforce") {
537
537
  quarantined.push(key);
538
538
  log2(
@@ -1647,14 +1647,272 @@ async function maybeReportTokenUsage(args) {
1647
1647
  state2.set(codeName, next);
1648
1648
  }
1649
1649
 
1650
+ // src/lib/conversation-evaluator.ts
1651
+ import { readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
1652
+ import { join as join3 } from "path";
1653
+ var MIN_CHECK_INTERVAL_MS3 = 5 * 6e4;
1654
+ var TRANSCRIPT_MTIME_WINDOW_MS2 = 3 * 24 * 60 * 60 * 1e3;
1655
+ var WINDOW_PAD_MS = 5 * 6e4;
1656
+ var MAX_TURN_CHARS = 1500;
1657
+ var MAX_TRANSCRIPT_CHARS = 6e3;
1658
+ var DEFAULT_CLAUDE_EVAL_MODEL = "claude-haiku-4-5-20251001";
1659
+ var DEFAULT_LOCAL_EVAL_URL = "http://localhost:11434/v1/chat/completions";
1660
+ var DEFAULT_LOCAL_EVAL_MODEL = "gemma4:12b";
1661
+ var EVAL_TIMEOUT_MS = 12e4;
1662
+ async function runLocalEvalChat(prompt, opts) {
1663
+ const endpoint = new URL(opts.url);
1664
+ const hostname = endpoint.hostname.toLowerCase();
1665
+ const isLoopback = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
1666
+ if (!isLoopback) {
1667
+ throw new Error(`local eval url must be loopback-only, got host '${endpoint.hostname}'`);
1668
+ }
1669
+ const doFetch = opts.fetchImpl ?? fetch;
1670
+ const res = await doFetch(opts.url, {
1671
+ method: "POST",
1672
+ headers: {
1673
+ "Content-Type": "application/json",
1674
+ ...opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : {}
1675
+ },
1676
+ body: JSON.stringify({
1677
+ model: opts.model,
1678
+ messages: [{ role: "user", content: prompt }],
1679
+ temperature: 0,
1680
+ max_tokens: 512,
1681
+ stream: false
1682
+ }),
1683
+ signal: AbortSignal.timeout(opts.timeoutMs ?? EVAL_TIMEOUT_MS)
1684
+ });
1685
+ if (!res.ok) {
1686
+ throw new Error(`local eval endpoint ${opts.url} returned ${res.status}`);
1687
+ }
1688
+ const data = await res.json();
1689
+ const text = data.choices?.[0]?.message?.content;
1690
+ if (typeof text !== "string" || !text.trim()) {
1691
+ throw new Error("local eval endpoint returned no message content");
1692
+ }
1693
+ return text;
1694
+ }
1695
+ var state3 = /* @__PURE__ */ new Map();
1696
+ function channelRefTokens(channelRef) {
1697
+ return channelRef.split(":").slice(1).filter((p) => p && p !== "dm");
1698
+ }
1699
+ function contentToText(content) {
1700
+ if (typeof content === "string") return content;
1701
+ if (Array.isArray(content)) {
1702
+ return content.map((b) => {
1703
+ if (typeof b === "string") return b;
1704
+ if (b && typeof b === "object" && b.type === "text") {
1705
+ return String(b.text ?? "");
1706
+ }
1707
+ return "";
1708
+ }).filter(Boolean).join(" ");
1709
+ }
1710
+ return "";
1711
+ }
1712
+ function parseTranscriptTurns(jsonl) {
1713
+ const turns = [];
1714
+ for (const line of jsonl.split("\n")) {
1715
+ const trimmed = line.trim();
1716
+ if (!trimmed) continue;
1717
+ let obj;
1718
+ try {
1719
+ obj = JSON.parse(trimmed);
1720
+ } catch {
1721
+ continue;
1722
+ }
1723
+ const role = obj.type === "user" || obj.type === "assistant" ? obj.type : void 0;
1724
+ if (!role) continue;
1725
+ const text = contentToText(obj.message?.content).trim();
1726
+ if (!text) continue;
1727
+ const ts = obj.timestamp ? Date.parse(obj.timestamp) : NaN;
1728
+ turns.push({ role, text, ts: Number.isFinite(ts) ? ts : 0 });
1729
+ }
1730
+ return turns;
1731
+ }
1732
+ function stripChannelTag(text) {
1733
+ return text.replace(/<channel\b[^>]*\/?>/i, "").replace(/<\/channel>/i, "").trim();
1734
+ }
1735
+ function reconstructConversation(allTurns, tokens, windowStartMs, windowEndMs) {
1736
+ if (tokens.length === 0) return [];
1737
+ const sorted = [...allTurns].sort((a, b) => a.ts - b.ts);
1738
+ const out = [];
1739
+ let inConversation = false;
1740
+ for (const turn of sorted) {
1741
+ if (turn.ts && (turn.ts < windowStartMs || turn.ts > windowEndMs)) continue;
1742
+ if (turn.role === "user") {
1743
+ inConversation = tokens.every((t) => turn.text.includes(t));
1744
+ if (inConversation) {
1745
+ out.push({ ...turn, text: stripChannelTag(turn.text) });
1746
+ }
1747
+ } else if (inConversation) {
1748
+ out.push(turn);
1749
+ }
1750
+ }
1751
+ return out;
1752
+ }
1753
+ function renderTranscript(turns) {
1754
+ const lines = [];
1755
+ let total = 0;
1756
+ for (const turn of turns) {
1757
+ const label = turn.role === "user" ? "User" : "Agent";
1758
+ const body = turn.text.length > MAX_TURN_CHARS ? `${turn.text.slice(0, MAX_TURN_CHARS)}\u2026` : turn.text;
1759
+ const line = `${label}: ${body}`;
1760
+ if (total + line.length > MAX_TRANSCRIPT_CHARS) break;
1761
+ lines.push(line);
1762
+ total += line.length;
1763
+ }
1764
+ return lines.join("\n\n");
1765
+ }
1766
+ function buildEvalPrompt(channel, transcript) {
1767
+ return `You are a strict QA evaluator scoring whether an AI agent successfully helped an end-user in a single ${channel} conversation. Judge ONLY the agent's effectiveness for the user \u2014 not tone, length, or policy.
1768
+
1769
+ Conversation transcript:
1770
+ """
1771
+ ${transcript}
1772
+ """
1773
+
1774
+ Score the agent's success:
1775
+ - score: integer 0-100 (0 = ignored/unhelpful/wrong, 100 = fully resolved the user's need)
1776
+ - verdict: "success" (need clearly met), "partial" (some help but incomplete/ambiguous), or "failure" (did not help / made it worse)
1777
+ - summary: ONE short sentence (max 140 chars) explaining the score. Do NOT quote sensitive user content.
1778
+
1779
+ Respond with ONLY a JSON object, no other text:
1780
+ {"score": 0-100, "verdict": "success|partial|failure", "summary": "..."}`;
1781
+ }
1782
+ function parseVerdict(raw) {
1783
+ const match = raw.match(/\{[\s\S]*\}/);
1784
+ if (!match) return null;
1785
+ let obj;
1786
+ try {
1787
+ obj = JSON.parse(match[0]);
1788
+ } catch {
1789
+ return null;
1790
+ }
1791
+ const score = typeof obj.score === "number" ? obj.score : Number(obj.score);
1792
+ if (!Number.isFinite(score) || score < 0 || score > 100) return null;
1793
+ if (obj.verdict !== "success" && obj.verdict !== "partial" && obj.verdict !== "failure") return null;
1794
+ const summary = typeof obj.summary === "string" ? obj.summary.slice(0, 140) : "";
1795
+ return { score: Math.round(score), verdict: obj.verdict, summary };
1796
+ }
1797
+ async function maybeEvaluateConversations(args) {
1798
+ const { api: api2, backend, codeName, agentId, log: log2 } = args;
1799
+ const now = args.now ?? /* @__PURE__ */ new Date();
1800
+ const nowMs = now.getTime();
1801
+ const existing = state3.get(codeName);
1802
+ if (existing && nowMs - existing.lastCheckedAt < MIN_CHECK_INTERVAL_MS3) {
1803
+ return;
1804
+ }
1805
+ state3.set(codeName, { lastCheckedAt: nowMs });
1806
+ let pending;
1807
+ try {
1808
+ const resp = await api2.get(
1809
+ `/host/conversations/pending-evaluation?agent_id=${encodeURIComponent(agentId)}`
1810
+ );
1811
+ pending = resp?.conversations ?? [];
1812
+ } catch (err) {
1813
+ log2(`[conversation-eval] ${codeName}: pending fetch failed: ${err.message}`);
1814
+ return;
1815
+ }
1816
+ if (pending.length === 0) return;
1817
+ const dir = args.transcriptDir ?? sessionTranscriptDir(getProjectDir(codeName));
1818
+ const allTurns = readRecentTurns(dir, nowMs);
1819
+ if (allTurns.length === 0) {
1820
+ for (const conv of pending) {
1821
+ await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
1822
+ }
1823
+ return;
1824
+ }
1825
+ for (const conv of pending) {
1826
+ const tokens = channelRefTokens(conv.channel_ref);
1827
+ const windowStart = Date.parse(conv.started_at) - WINDOW_PAD_MS;
1828
+ const windowEnd = Date.parse(conv.last_message_at) + WINDOW_PAD_MS;
1829
+ const turns = reconstructConversation(allTurns, tokens, windowStart, windowEnd);
1830
+ if (turns.length === 0) {
1831
+ await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
1832
+ continue;
1833
+ }
1834
+ const transcript = renderTranscript(turns);
1835
+ if (!transcript.trim()) {
1836
+ await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
1837
+ continue;
1838
+ }
1839
+ let verdict;
1840
+ try {
1841
+ const out = await backend.run(buildEvalPrompt(conv.channel, transcript));
1842
+ verdict = parseVerdict(out);
1843
+ } catch (err) {
1844
+ log2(`[conversation-eval] ${codeName}: scoring failed: ${err.message}`);
1845
+ continue;
1846
+ }
1847
+ if (!verdict) {
1848
+ log2(`[conversation-eval] ${codeName}: unparseable verdict for ${conv.conversation_id.slice(0, 8)}`);
1849
+ continue;
1850
+ }
1851
+ try {
1852
+ await api2.post("/host/conversations/evaluation", {
1853
+ agent_id: agentId,
1854
+ conversation_id: conv.conversation_id,
1855
+ score: verdict.score,
1856
+ verdict: verdict.verdict,
1857
+ summary: verdict.summary,
1858
+ model: backend.model
1859
+ });
1860
+ log2(
1861
+ `[conversation-eval] ${codeName}: ${conv.conversation_id.slice(0, 8)} \u2192 ${verdict.verdict} (${verdict.score})`
1862
+ );
1863
+ } catch (err) {
1864
+ log2(`[conversation-eval] ${codeName}: report failed: ${err.message}`);
1865
+ }
1866
+ }
1867
+ }
1868
+ async function reportSkip(api2, agentId, conversationId, log2, codeName) {
1869
+ try {
1870
+ await api2.post("/host/conversations/evaluation", {
1871
+ agent_id: agentId,
1872
+ conversation_id: conversationId,
1873
+ skipped: true
1874
+ });
1875
+ } catch (err) {
1876
+ log2(`[conversation-eval] ${codeName}: skip report failed: ${err.message}`);
1877
+ }
1878
+ }
1879
+ function readRecentTurns(dir, nowMs) {
1880
+ let entries;
1881
+ try {
1882
+ entries = readdirSync2(dir);
1883
+ } catch {
1884
+ return [];
1885
+ }
1886
+ const turns = [];
1887
+ for (const name of entries) {
1888
+ if (!name.endsWith(".jsonl")) continue;
1889
+ const full = join3(dir, name);
1890
+ let mtimeMs;
1891
+ try {
1892
+ mtimeMs = statSync2(full).mtimeMs;
1893
+ } catch {
1894
+ continue;
1895
+ }
1896
+ if (nowMs - mtimeMs > TRANSCRIPT_MTIME_WINDOW_MS2) continue;
1897
+ let content;
1898
+ try {
1899
+ content = readFileSync5(full, "utf8");
1900
+ } catch {
1901
+ continue;
1902
+ }
1903
+ turns.push(...parseTranscriptTurns(content));
1904
+ }
1905
+ return turns;
1906
+ }
1907
+
1650
1908
  // src/lib/activity-cache-monitor.ts
1651
- import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
1909
+ import { existsSync as existsSync2, readFileSync as readFileSync6 } from "fs";
1652
1910
  import { homedir } from "os";
1653
- import { join as join3 } from "path";
1654
- var MIN_CHECK_INTERVAL_MS3 = 6e4;
1655
- var STATS_CACHE_PATH = join3(homedir(), ".claude", "stats-cache.json");
1911
+ import { join as join4 } from "path";
1912
+ var MIN_CHECK_INTERVAL_MS4 = 6e4;
1913
+ var STATS_CACHE_PATH = join4(homedir(), ".claude", "stats-cache.json");
1656
1914
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
1657
- var state3 = { lastObservedDate: null, lastCheckedAt: 0 };
1915
+ var state4 = { lastObservedDate: null, lastCheckedAt: 0 };
1658
1916
  function selectNewDailyRows(raw, lastObservedDate) {
1659
1917
  let parsed;
1660
1918
  try {
@@ -1693,24 +1951,24 @@ async function maybeReportActivityCache(args) {
1693
1951
  const { api: api2, log: log2 } = args;
1694
1952
  const now = args.now ?? /* @__PURE__ */ new Date();
1695
1953
  const nowMs = now.getTime();
1696
- if (nowMs - state3.lastCheckedAt < MIN_CHECK_INTERVAL_MS3) return;
1697
- state3.lastCheckedAt = nowMs;
1954
+ if (nowMs - state4.lastCheckedAt < MIN_CHECK_INTERVAL_MS4) return;
1955
+ state4.lastCheckedAt = nowMs;
1698
1956
  if (!existsSync2(STATS_CACHE_PATH)) {
1699
1957
  return;
1700
1958
  }
1701
1959
  let raw;
1702
1960
  try {
1703
- raw = readFileSync5(STATS_CACHE_PATH, "utf-8");
1961
+ raw = readFileSync6(STATS_CACHE_PATH, "utf-8");
1704
1962
  } catch (err) {
1705
1963
  log2(`[activity-cache] readFileSync failed: ${err.message}`);
1706
1964
  return;
1707
1965
  }
1708
- const rows = selectNewDailyRows(raw, state3.lastObservedDate);
1966
+ const rows = selectNewDailyRows(raw, state4.lastObservedDate);
1709
1967
  if (rows.length === 0) return;
1710
1968
  for (const row of rows) {
1711
1969
  try {
1712
1970
  await api2.post("/host/activity-observations", row);
1713
- state3.lastObservedDate = row.date;
1971
+ state4.lastObservedDate = row.date;
1714
1972
  } catch (err) {
1715
1973
  log2(
1716
1974
  `[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
@@ -2150,7 +2408,7 @@ var GatewayClientPool = class extends EventEmitter {
2150
2408
  // src/lib/claude-auth-detect.ts
2151
2409
  import { readFile, readdir } from "fs/promises";
2152
2410
  import { homedir as homedir2, platform } from "os";
2153
- import { join as join4 } from "path";
2411
+ import { join as join5 } from "path";
2154
2412
  import { execFile as execFile2 } from "child_process";
2155
2413
  import { promisify } from "util";
2156
2414
  var execFileAsync = promisify(execFile2);
@@ -2165,8 +2423,8 @@ async function detectClaudeAuth() {
2165
2423
  }
2166
2424
  async function findClaudeCredentialsPaths() {
2167
2425
  const candidates = [
2168
- join4(homedir2(), ".claude", ".credentials.json"),
2169
- join4(homedir2(), ".claude", "credentials.json")
2426
+ join5(homedir2(), ".claude", ".credentials.json"),
2427
+ join5(homedir2(), ".claude", "credentials.json")
2170
2428
  ];
2171
2429
  const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
2172
2430
  if (isLinuxRoot) {
@@ -2174,8 +2432,8 @@ async function findClaudeCredentialsPaths() {
2174
2432
  const entries = await readdir("/home", { withFileTypes: true });
2175
2433
  for (const entry of entries) {
2176
2434
  if (!entry.isDirectory()) continue;
2177
- candidates.push(join4("/home", entry.name, ".claude", ".credentials.json"));
2178
- candidates.push(join4("/home", entry.name, ".claude", "credentials.json"));
2435
+ candidates.push(join5("/home", entry.name, ".claude", ".credentials.json"));
2436
+ candidates.push(join5("/home", entry.name, ".claude", "credentials.json"));
2179
2437
  }
2180
2438
  } catch {
2181
2439
  }
@@ -2267,18 +2525,18 @@ function normalize(value) {
2267
2525
  }
2268
2526
 
2269
2527
  // src/lib/channel-hash-cache.ts
2270
- import { existsSync as existsSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
2271
- import { join as join5 } from "path";
2528
+ import { existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
2529
+ import { join as join6 } from "path";
2272
2530
  var CACHE_FILENAME = "channel-hash-cache.json";
2273
2531
  function getChannelHashCacheFile(configDir) {
2274
- return join5(configDir, CACHE_FILENAME);
2532
+ return join6(configDir, CACHE_FILENAME);
2275
2533
  }
2276
2534
  function loadChannelHashCache(target, configDir) {
2277
2535
  const path = getChannelHashCacheFile(configDir);
2278
2536
  if (!existsSync3(path)) return;
2279
2537
  let parsed;
2280
2538
  try {
2281
- parsed = JSON.parse(readFileSync6(path, "utf-8"));
2539
+ parsed = JSON.parse(readFileSync7(path, "utf-8"));
2282
2540
  } catch {
2283
2541
  return;
2284
2542
  }
@@ -2732,24 +2990,24 @@ function clearAgentState(agentId, codeName) {
2732
2990
  }
2733
2991
 
2734
2992
  // src/lib/restart-flags.ts
2735
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync7, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
2993
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync8, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
2736
2994
  import { homedir as homedir3 } from "os";
2737
- import { join as join6 } from "path";
2995
+ import { join as join7 } from "path";
2738
2996
  import { randomUUID } from "crypto";
2739
2997
  function restartFlagsDir() {
2740
- return join6(homedir3(), ".augmented", "restart-flags");
2998
+ return join7(homedir3(), ".augmented", "restart-flags");
2741
2999
  }
2742
3000
  function flagPath(codeName) {
2743
- return join6(restartFlagsDir(), `${codeName}.flag`);
3001
+ return join7(restartFlagsDir(), `${codeName}.flag`);
2744
3002
  }
2745
3003
  function readRestartFlags() {
2746
3004
  const dir = restartFlagsDir();
2747
3005
  if (!existsSync4(dir)) return [];
2748
3006
  const out = [];
2749
- for (const entry of readdirSync2(dir)) {
3007
+ for (const entry of readdirSync3(dir)) {
2750
3008
  if (!entry.endsWith(".flag")) continue;
2751
3009
  try {
2752
- const raw = readFileSync7(join6(dir, entry), "utf8");
3010
+ const raw = readFileSync8(join7(dir, entry), "utf8");
2753
3011
  const parsed = JSON.parse(raw);
2754
3012
  if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
2755
3013
  parsed.codeName = entry.replace(/\.flag$/, "");
@@ -3296,8 +3554,8 @@ function applyRestartAcks(args) {
3296
3554
  var GATEWAY_PORT_BASE = 18800;
3297
3555
  var GATEWAY_PORT_STEP = 10;
3298
3556
  var GATEWAY_PORT_MAX = 18899;
3299
- var AUGMENTED_DIR = join7(process.env["HOME"] ?? "/tmp", ".augmented");
3300
- var GATEWAY_PORTS_FILE = join7(AUGMENTED_DIR, "gateway-ports.json");
3557
+ var AUGMENTED_DIR = join8(process.env["HOME"] ?? "/tmp", ".augmented");
3558
+ var GATEWAY_PORTS_FILE = join8(AUGMENTED_DIR, "gateway-ports.json");
3301
3559
  var CHANNEL_SWEEP_INTERVAL_MS = (() => {
3302
3560
  const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
3303
3561
  if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
@@ -3404,8 +3662,8 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
3404
3662
  function shouldSkipRevokedCleanup(previousKnownStatus) {
3405
3663
  return previousKnownStatus === "revoked";
3406
3664
  }
3407
- function hasRevokedResiduals(state5) {
3408
- return state5.gatewayRunning || state5.portAllocated || state5.provisionDirExists;
3665
+ function hasRevokedResiduals(state6) {
3666
+ return state6.gatewayRunning || state6.portAllocated || state6.provisionDirExists;
3409
3667
  }
3410
3668
  var pendingSessionRestarts = /* @__PURE__ */ new Map();
3411
3669
  var restartBreaker = new RestartBreaker();
@@ -3482,7 +3740,7 @@ function inboundAgeSecondsFor(codeName) {
3482
3740
  }
3483
3741
  function paneLogAgeSecondsFor(codeName) {
3484
3742
  try {
3485
- const mtimeMs = statSync2(paneLogPath(codeName)).mtimeMs;
3743
+ const mtimeMs = statSync3(paneLogPath(codeName)).mtimeMs;
3486
3744
  return Math.max(0, Math.floor((Date.now() - mtimeMs) / 1e3));
3487
3745
  } catch (err) {
3488
3746
  if (err?.code === "ENOENT") return null;
@@ -3502,7 +3760,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
3502
3760
  var runningMcpServerKeys = /* @__PURE__ */ new Map();
3503
3761
  function projectMcpHash(_codeName, projectDir) {
3504
3762
  try {
3505
- const raw = readFileSync8(join7(projectDir, ".mcp.json"), "utf-8");
3763
+ const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
3506
3764
  return createHash3("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
3507
3765
  } catch {
3508
3766
  return null;
@@ -3510,7 +3768,7 @@ function projectMcpHash(_codeName, projectDir) {
3510
3768
  }
3511
3769
  function projectMcpKeys(_codeName, projectDir) {
3512
3770
  try {
3513
- const raw = readFileSync8(join7(projectDir, ".mcp.json"), "utf-8");
3771
+ const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
3514
3772
  const parsed = JSON.parse(raw);
3515
3773
  const servers = parsed.mcpServers;
3516
3774
  if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
@@ -3521,7 +3779,7 @@ function projectMcpKeys(_codeName, projectDir) {
3521
3779
  }
3522
3780
  function readMcpHttpServerConfig(projectDir, serverKey) {
3523
3781
  try {
3524
- const raw = readFileSync8(join7(projectDir, ".mcp.json"), "utf-8");
3782
+ const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
3525
3783
  const servers = JSON.parse(raw).mcpServers ?? {};
3526
3784
  const entry = servers[serverKey];
3527
3785
  if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
@@ -3674,7 +3932,7 @@ var taskDisplayInfo = /* @__PURE__ */ new Map();
3674
3932
  var agentChannelTokens = /* @__PURE__ */ new Map();
3675
3933
  var activeChannels = /* @__PURE__ */ new Map();
3676
3934
  var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
3677
- var state4 = {
3935
+ var state5 = {
3678
3936
  pid: process.pid,
3679
3937
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
3680
3938
  lastPollAt: null,
@@ -3713,7 +3971,7 @@ var cachedMaintenanceWindow = null;
3713
3971
  var lastVersionCheckAt = 0;
3714
3972
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
3715
3973
  var lastResponsivenessProbeAt = 0;
3716
- var agtCliVersion = true ? "0.27.84" : "dev";
3974
+ var agtCliVersion = true ? "0.27.86" : "dev";
3717
3975
  function resolveBrewPath(execFileSync4) {
3718
3976
  try {
3719
3977
  const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -3890,7 +4148,7 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
3890
4148
  try {
3891
4149
  let settings = {};
3892
4150
  if (existsSync5(path)) {
3893
- const raw = readFileSync8(path, "utf-8").trim();
4151
+ const raw = readFileSync9(path, "utf-8").trim();
3894
4152
  if (raw) {
3895
4153
  let parsed;
3896
4154
  try {
@@ -3962,7 +4220,7 @@ async function ensureFrameworkBinary(frameworkId) {
3962
4220
  var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
3963
4221
  var claudeCodeUpgradeInFlight = false;
3964
4222
  function claudeCodeUpgradeMarkerPath() {
3965
- return join7(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
4223
+ return join8(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
3966
4224
  }
3967
4225
  function stampClaudeCodeUpgradeMarker() {
3968
4226
  try {
@@ -3972,7 +4230,7 @@ function stampClaudeCodeUpgradeMarker() {
3972
4230
  }
3973
4231
  function claudeCodeUpgradeThrottled() {
3974
4232
  try {
3975
- const lastCheck = parseInt(readFileSync8(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
4233
+ const lastCheck = parseInt(readFileSync9(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
3976
4234
  if (!Number.isFinite(lastCheck)) return false;
3977
4235
  return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
3978
4236
  } catch {
@@ -4025,7 +4283,7 @@ ${r.stderr}`;
4025
4283
  }
4026
4284
  var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
4027
4285
  function selfUpdateAppliedMarkerPath() {
4028
- return join7(homedir4(), ".augmented", ".last-self-update-applied");
4286
+ return join8(homedir4(), ".augmented", ".last-self-update-applied");
4029
4287
  }
4030
4288
  var selfUpdateUpToDateLogged = false;
4031
4289
  var restartAfterUpgrade = false;
@@ -4048,7 +4306,7 @@ async function checkAndUpdateCli() {
4048
4306
  const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
4049
4307
  if (!isBrewFormula && !isNpmGlobal) return;
4050
4308
  const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
4051
- const markerPath = join7(homedir4(), ".augmented", ".last-update-check");
4309
+ const markerPath = join8(homedir4(), ".augmented", ".last-update-check");
4052
4310
  try {
4053
4311
  const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
4054
4312
  if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
@@ -4305,9 +4563,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
4305
4563
  throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
4306
4564
  }
4307
4565
  childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
4308
- const claudeDir = join7(homedir4(), ".claude");
4566
+ const claudeDir = join8(homedir4(), ".claude");
4309
4567
  for (const filename of [".credentials.json", "credentials.json"]) {
4310
- const p = join7(claudeDir, filename);
4568
+ const p = join8(claudeDir, filename);
4311
4569
  if (existsSync5(p)) {
4312
4570
  try {
4313
4571
  rmSync2(p, { force: true });
@@ -4320,9 +4578,86 @@ async function applyClaudeAuthToEnv(childEnv, label) {
4320
4578
  delete childEnv.ANTHROPIC_API_KEY;
4321
4579
  }
4322
4580
  }
4581
+ var evalEmptyMcpConfigPath = null;
4582
+ function ensureEvalEmptyMcpConfig() {
4583
+ if (evalEmptyMcpConfigPath && existsSync5(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
4584
+ const dir = join8(homedir4(), ".augmented");
4585
+ try {
4586
+ mkdirSync4(dir, { recursive: true });
4587
+ } catch {
4588
+ }
4589
+ const p = join8(dir, ".eval-empty-mcp.json");
4590
+ writeFileSync4(p, JSON.stringify({ mcpServers: {} }));
4591
+ evalEmptyMcpConfigPath = p;
4592
+ return p;
4593
+ }
4594
+ async function runEvalClaude(prompt, model) {
4595
+ const childEnv = { ...process.env };
4596
+ await applyClaudeAuthToEnv(childEnv, "conversation-eval");
4597
+ const emptyMcp = ensureEvalEmptyMcpConfig();
4598
+ const args = [
4599
+ "-p",
4600
+ prompt,
4601
+ "--model",
4602
+ model,
4603
+ "--output-format",
4604
+ "text",
4605
+ // Isolation: only the empty MCP config, ignore project/user config + no tools.
4606
+ "--mcp-config",
4607
+ emptyMcp,
4608
+ "--strict-mcp-config",
4609
+ "--permission-mode",
4610
+ "auto",
4611
+ "--allowedTools",
4612
+ ""
4613
+ ];
4614
+ const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
4615
+ cwd: homedir4(),
4616
+ timeout: 12e4,
4617
+ stdin: "ignore",
4618
+ env: childEnv,
4619
+ onSpawn: (pid) => registerClaudeSpawn({ pid, started_at: Date.now(), kind: "other" }),
4620
+ onExit: (pid) => unregisterClaudeSpawn(pid)
4621
+ });
4622
+ return stdout;
4623
+ }
4624
+ var conversationEvalBackend = null;
4625
+ function resolveConversationEvalBackend() {
4626
+ if (conversationEvalBackend) return conversationEvalBackend;
4627
+ const kind = (process.env["AGT_CONV_EVAL_BACKEND"] ?? "claude-p").trim().toLowerCase();
4628
+ if (kind === "local") {
4629
+ const url = process.env["AGT_CONV_EVAL_LOCAL_URL"]?.trim() || DEFAULT_LOCAL_EVAL_URL;
4630
+ const model = process.env["AGT_CONV_EVAL_LOCAL_MODEL"]?.trim() || DEFAULT_LOCAL_EVAL_MODEL;
4631
+ const apiKey = process.env["AGT_CONV_EVAL_LOCAL_API_KEY"]?.trim() || void 0;
4632
+ const safeUrl = (() => {
4633
+ try {
4634
+ const parsed = new URL(url);
4635
+ return `${parsed.origin}${parsed.pathname}`;
4636
+ } catch {
4637
+ return "[invalid-url]";
4638
+ }
4639
+ })();
4640
+ log(`[conversation-eval] backend=local url=${safeUrl} model=${model}`);
4641
+ conversationEvalBackend = { model, run: (prompt) => runLocalEvalChat(prompt, { url, model, apiKey }) };
4642
+ return conversationEvalBackend;
4643
+ }
4644
+ if (kind === "" || kind === "claude-p") {
4645
+ const model = process.env["AGT_CONV_EVAL_CLAUDE_MODEL"]?.trim() || DEFAULT_CLAUDE_EVAL_MODEL;
4646
+ conversationEvalBackend = { model, run: (prompt) => runEvalClaude(prompt, model) };
4647
+ return conversationEvalBackend;
4648
+ }
4649
+ log(`[conversation-eval] invalid AGT_CONV_EVAL_BACKEND='${kind}' \u2014 expected 'claude-p' or 'local'; evaluation disabled`);
4650
+ conversationEvalBackend = {
4651
+ model: "invalid-conversation-eval-backend",
4652
+ run: async () => {
4653
+ throw new Error(`Unsupported AGT_CONV_EVAL_BACKEND='${kind}'. Expected 'claude-p' or 'local'.`);
4654
+ }
4655
+ };
4656
+ return conversationEvalBackend;
4657
+ }
4323
4658
  function loadGatewayPorts() {
4324
4659
  try {
4325
- return JSON.parse(readFileSync8(GATEWAY_PORTS_FILE, "utf-8"));
4660
+ return JSON.parse(readFileSync9(GATEWAY_PORTS_FILE, "utf-8"));
4326
4661
  } catch {
4327
4662
  return {};
4328
4663
  }
@@ -4352,10 +4687,10 @@ function freePort(codeName) {
4352
4687
  }
4353
4688
  }
4354
4689
  function getStateFile() {
4355
- return join7(config?.configDir ?? join7(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
4690
+ return join8(config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
4356
4691
  }
4357
4692
  function channelHashCacheDir() {
4358
- return config?.configDir ?? join7(process.env["HOME"] ?? "/tmp", ".augmented");
4693
+ return config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
4359
4694
  }
4360
4695
  function loadChannelHashCache2() {
4361
4696
  loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
@@ -4366,7 +4701,7 @@ function saveChannelHashCache2() {
4366
4701
  var _channelQuarantineStore = null;
4367
4702
  function channelQuarantineStore() {
4368
4703
  if (!_channelQuarantineStore) {
4369
- const dir = config?.configDir ?? join7(process.env["HOME"] ?? "/tmp", ".augmented");
4704
+ const dir = config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
4370
4705
  _channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
4371
4706
  }
4372
4707
  return _channelQuarantineStore;
@@ -4421,7 +4756,7 @@ function log(msg) {
4421
4756
  `;
4422
4757
  if (!managerLogPath) {
4423
4758
  try {
4424
- managerLogPath = join7(homedir4(), ".augmented", "manager.log");
4759
+ managerLogPath = join8(homedir4(), ".augmented", "manager.log");
4425
4760
  mkdirSync4(dirname3(managerLogPath), { recursive: true });
4426
4761
  if (existsSync5(managerLogPath)) {
4427
4762
  chmodSync(managerLogPath, 384);
@@ -4451,7 +4786,7 @@ function sha256(content) {
4451
4786
  }
4452
4787
  function hashFile(filePath) {
4453
4788
  try {
4454
- const content = readFileSync8(filePath, "utf-8");
4789
+ const content = readFileSync9(filePath, "utf-8");
4455
4790
  return sha256(content);
4456
4791
  } catch {
4457
4792
  return null;
@@ -4475,13 +4810,13 @@ function parseSkillFrontmatter(content) {
4475
4810
  return out;
4476
4811
  }
4477
4812
  async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
4478
- const { readdirSync: readdirSync4, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync5 } = await import("fs");
4479
- const skillsDir = join7(configDir, codeName, "project", ".claude", "skills");
4480
- const claudeMdPath = join7(configDir, codeName, "project", "CLAUDE.md");
4813
+ const { readdirSync: readdirSync5, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync5 } = await import("fs");
4814
+ const skillsDir = join8(configDir, codeName, "project", ".claude", "skills");
4815
+ const claudeMdPath = join8(configDir, codeName, "project", "CLAUDE.md");
4481
4816
  if (!ex(skillsDir) || !ex(claudeMdPath)) return;
4482
4817
  const entries = [];
4483
- for (const dir of readdirSync4(skillsDir).sort()) {
4484
- const skillFile = join7(skillsDir, dir, "SKILL.md");
4818
+ for (const dir of readdirSync5(skillsDir).sort()) {
4819
+ const skillFile = join8(skillsDir, dir, "SKILL.md");
4485
4820
  if (!ex(skillFile)) continue;
4486
4821
  try {
4487
4822
  const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
@@ -4529,10 +4864,10 @@ ${SKILLS_INDEX_END}`;
4529
4864
  }
4530
4865
  async function migrateToProfiles() {
4531
4866
  const homeDir = process.env["HOME"] ?? "/tmp";
4532
- const sharedConfigPath = join7(homeDir, ".openclaw", "openclaw.json");
4867
+ const sharedConfigPath = join8(homeDir, ".openclaw", "openclaw.json");
4533
4868
  let sharedConfig;
4534
4869
  try {
4535
- sharedConfig = JSON.parse(readFileSync8(sharedConfigPath, "utf-8"));
4870
+ sharedConfig = JSON.parse(readFileSync9(sharedConfigPath, "utf-8"));
4536
4871
  } catch {
4537
4872
  return;
4538
4873
  }
@@ -4545,19 +4880,19 @@ async function migrateToProfiles() {
4545
4880
  const codeName = agentEntry["id"];
4546
4881
  if (!codeName) continue;
4547
4882
  if (codeName === "main") continue;
4548
- const profileDir = join7(homeDir, `.openclaw-${codeName}`);
4549
- if (existsSync5(join7(profileDir, "openclaw.json"))) continue;
4883
+ const profileDir = join8(homeDir, `.openclaw-${codeName}`);
4884
+ if (existsSync5(join8(profileDir, "openclaw.json"))) continue;
4550
4885
  log(`Migrating agent '${codeName}' to per-agent profile`);
4551
4886
  if (adapter.seedProfileConfig) {
4552
4887
  adapter.seedProfileConfig(codeName);
4553
4888
  }
4554
- const sharedAuthDir = join7(homeDir, ".openclaw", "agents", codeName, "agent");
4555
- const profileAuthDir = join7(profileDir, "agents", codeName, "agent");
4556
- const authFile = join7(sharedAuthDir, "auth-profiles.json");
4889
+ const sharedAuthDir = join8(homeDir, ".openclaw", "agents", codeName, "agent");
4890
+ const profileAuthDir = join8(profileDir, "agents", codeName, "agent");
4891
+ const authFile = join8(sharedAuthDir, "auth-profiles.json");
4557
4892
  if (existsSync5(authFile)) {
4558
4893
  mkdirSync4(profileAuthDir, { recursive: true });
4559
- const authContent = readFileSync8(authFile, "utf-8");
4560
- writeFileSync4(join7(profileAuthDir, "auth-profiles.json"), authContent);
4894
+ const authContent = readFileSync9(authFile, "utf-8");
4895
+ writeFileSync4(join8(profileAuthDir, "auth-profiles.json"), authContent);
4561
4896
  }
4562
4897
  allocatePort(codeName);
4563
4898
  migrated++;
@@ -4595,7 +4930,7 @@ function readGatewayToken(codeName) {
4595
4930
  }
4596
4931
  const homeDir = process.env["HOME"] ?? "/tmp";
4597
4932
  try {
4598
- const cfg = JSON.parse(readFileSync8(join7(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
4933
+ const cfg = JSON.parse(readFileSync9(join8(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
4599
4934
  return cfg?.gateway?.auth?.token;
4600
4935
  } catch {
4601
4936
  return void 0;
@@ -4604,18 +4939,18 @@ function readGatewayToken(codeName) {
4604
4939
  var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
4605
4940
  function isGatewayHung(codeName) {
4606
4941
  const homeDir = process.env["HOME"] ?? "/tmp";
4607
- const jobsPath = join7(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
4942
+ const jobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
4608
4943
  if (!existsSync5(jobsPath)) return false;
4609
4944
  try {
4610
- const data = JSON.parse(readFileSync8(jobsPath, "utf-8"));
4945
+ const data = JSON.parse(readFileSync9(jobsPath, "utf-8"));
4611
4946
  const jobs = data.jobs ?? data;
4612
4947
  if (!Array.isArray(jobs)) return false;
4613
4948
  const now = Date.now();
4614
4949
  for (const job of jobs) {
4615
- const state5 = job.state;
4616
- if (!state5) continue;
4617
- const runStartedAt = state5.runStartedAtMs;
4618
- const isRunning = state5.status === "running" || state5.running === true;
4950
+ const state6 = job.state;
4951
+ if (!state6) continue;
4952
+ const runStartedAt = state6.runStartedAtMs;
4953
+ const isRunning = state6.status === "running" || state6.running === true;
4619
4954
  if (isRunning && runStartedAt && now - runStartedAt > GATEWAY_HUNG_TIMEOUT_MS) {
4620
4955
  return true;
4621
4956
  }
@@ -4640,15 +4975,15 @@ async function ensureGatewayRunning(codeName, adapter) {
4640
4975
  }
4641
4976
  await new Promise((r) => setTimeout(r, 2e3));
4642
4977
  const homeDir = process.env["HOME"] ?? "/tmp";
4643
- const cronJobsPath = join7(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
4978
+ const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
4644
4979
  clearStaleCronRunState(cronJobsPath);
4645
4980
  } else {
4646
4981
  if (status.port) {
4647
4982
  try {
4648
4983
  const homeDir = process.env["HOME"] ?? "/tmp";
4649
- const configPath = join7(homeDir, `.openclaw-${codeName}`, "openclaw.json");
4984
+ const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
4650
4985
  if (existsSync5(configPath)) {
4651
- const cfg = JSON.parse(readFileSync8(configPath, "utf-8"));
4986
+ const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
4652
4987
  if (cfg.gateway?.port !== status.port) {
4653
4988
  if (!cfg.gateway) cfg.gateway = {};
4654
4989
  cfg.gateway.port = status.port;
@@ -4672,9 +5007,9 @@ async function ensureGatewayRunning(codeName, adapter) {
4672
5007
  gatewaysStartedThisCycle.add(codeName);
4673
5008
  try {
4674
5009
  const homeDir = process.env["HOME"] ?? "/tmp";
4675
- const configPath = join7(homeDir, `.openclaw-${codeName}`, "openclaw.json");
5010
+ const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
4676
5011
  if (existsSync5(configPath)) {
4677
- const cfg = JSON.parse(readFileSync8(configPath, "utf-8"));
5012
+ const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
4678
5013
  if (!cfg.gateway) cfg.gateway = {};
4679
5014
  cfg.gateway.port = port;
4680
5015
  writeFileSync4(configPath, JSON.stringify(cfg, null, 2));
@@ -4816,7 +5151,7 @@ async function pollCycle() {
4816
5151
  const now = Date.now();
4817
5152
  if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
4818
5153
  try {
4819
- const firstAgent = state4.agents[0];
5154
+ const firstAgent = state5.agents[0];
4820
5155
  const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework("openclaw");
4821
5156
  if (versionAdapter.getVersion) {
4822
5157
  cachedFrameworkVersion = await versionAdapter.getVersion();
@@ -4827,7 +5162,7 @@ async function pollCycle() {
4827
5162
  }
4828
5163
  try {
4829
5164
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
4830
- const { collectDiagnostics } = await import("../persistent-session-BTXWOKJ6.js");
5165
+ const { collectDiagnostics } = await import("../persistent-session-YNVCVUK3.js");
4831
5166
  const diagCodeNames = [...agentState.persistentSessionAgents];
4832
5167
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
4833
5168
  let tailscaleHostname;
@@ -4900,7 +5235,7 @@ async function pollCycle() {
4900
5235
  const {
4901
5236
  collectResponsivenessProbes,
4902
5237
  getResponsivenessIntervalMs
4903
- } = await import("../responsiveness-probe-Y6TCM6T4.js");
5238
+ } = await import("../responsiveness-probe-5ICEBNCM.js");
4904
5239
  const probeIntervalMs = getResponsivenessIntervalMs();
4905
5240
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
4906
5241
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -4963,7 +5298,7 @@ async function pollCycle() {
4963
5298
  for (const agent of agents) {
4964
5299
  const requested = agent.restart_requested_at ?? null;
4965
5300
  if (!requested) continue;
4966
- const prev = state4.agents.find((a) => a.agentId === agent.agent_id);
5301
+ const prev = state5.agents.find((a) => a.agentId === agent.agent_id);
4967
5302
  const lastProcessed = prev?.lastRestartProcessedAt ?? null;
4968
5303
  if (lastProcessed && Date.parse(lastProcessed) >= Date.parse(requested)) continue;
4969
5304
  log(`[restart] Dashboard requested restart for '${agent.code_name}' at ${requested}`);
@@ -4986,7 +5321,7 @@ async function pollCycle() {
4986
5321
  await processAgent(agent, agentStates);
4987
5322
  } catch (err) {
4988
5323
  log(`Error processing agent '${agent.code_name}': ${err.message}`);
4989
- const existing = state4.agents.find((a) => a.agentId === agent.agent_id);
5324
+ const existing = state5.agents.find((a) => a.agentId === agent.agent_id);
4990
5325
  if (existing) {
4991
5326
  agentStates.push(existing);
4992
5327
  } else {
@@ -5012,12 +5347,12 @@ async function pollCycle() {
5012
5347
  void maybeReportActivityCache({ api, log });
5013
5348
  const restartAckStateChanged = applyRestartAcks({
5014
5349
  agentStates,
5015
- priorAgents: state4.agents,
5350
+ priorAgents: state5.agents,
5016
5351
  restartAcks
5017
5352
  });
5018
5353
  if (restartAckStateChanged) {
5019
5354
  try {
5020
- const ackedState = { ...state4, agents: agentStates };
5355
+ const ackedState = { ...state5, agents: agentStates };
5021
5356
  atomicWriteFileSync(getStateFile(), JSON.stringify(ackedState, null, 2));
5022
5357
  } catch (err) {
5023
5358
  log(`[restart] failed to persist ack immediately: ${err.message}`);
@@ -5035,7 +5370,7 @@ async function pollCycle() {
5035
5370
  } catch {
5036
5371
  }
5037
5372
  const currentIds = new Set(agents.map((a) => a.agent_id));
5038
- for (const prev of state4.agents) {
5373
+ for (const prev of state5.agents) {
5039
5374
  if (!currentIds.has(prev.agentId)) {
5040
5375
  log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
5041
5376
  const adapter = resolveAgentFramework(prev.codeName);
@@ -5048,7 +5383,7 @@ async function pollCycle() {
5048
5383
  }
5049
5384
  killAgentChannelProcesses(prev.codeName, { log });
5050
5385
  freePort(prev.codeName);
5051
- const agentDir = join7(adapter.getAgentDir(prev.codeName), "provision");
5386
+ const agentDir = join8(adapter.getAgentDir(prev.codeName), "provision");
5052
5387
  await cleanupAgentFiles(prev.codeName, agentDir);
5053
5388
  clearAgentCaches(prev.agentId, prev.codeName);
5054
5389
  }
@@ -5156,10 +5491,10 @@ async function pollCycle() {
5156
5491
  }
5157
5492
  } catch {
5158
5493
  }
5159
- state4 = {
5160
- ...state4,
5494
+ state5 = {
5495
+ ...state5,
5161
5496
  lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
5162
- pollCount: state4.pollCount + 1,
5497
+ pollCount: state5.pollCount + 1,
5163
5498
  agents: agentStates,
5164
5499
  // ENG-5441: serialise trip state on every poll so manager restarts
5165
5500
  // never silently clear a tripped breaker. Cheap — only tripped
@@ -5170,9 +5505,9 @@ async function pollCycle() {
5170
5505
  log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
5171
5506
  consecutivePollFailures = 0;
5172
5507
  }
5173
- send({ type: "state-update", state: state4 });
5508
+ send({ type: "state-update", state: state5 });
5174
5509
  } catch (err) {
5175
- state4.errorCount++;
5510
+ state5.errorCount++;
5176
5511
  const message = err.message;
5177
5512
  log(`Poll error: ${message}`);
5178
5513
  send({ type: "error", message });
@@ -5216,10 +5551,17 @@ async function processAgent(agent, agentStates) {
5216
5551
  agentId: agent.agent_id,
5217
5552
  log
5218
5553
  });
5554
+ void maybeEvaluateConversations({
5555
+ api,
5556
+ backend: resolveConversationEvalBackend(),
5557
+ codeName: agent.code_name,
5558
+ agentId: agent.agent_id,
5559
+ log
5560
+ });
5219
5561
  }
5220
5562
  const now = (/* @__PURE__ */ new Date()).toISOString();
5221
5563
  const adapter = resolveAgentFramework(agent.code_name);
5222
- let agentDir = join7(adapter.getAgentDir(agent.code_name), "provision");
5564
+ let agentDir = join8(adapter.getAgentDir(agent.code_name), "provision");
5223
5565
  if (agent.status === "draft" || agent.status === "paused") {
5224
5566
  if (previousKnownStatus !== agent.status) {
5225
5567
  log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
@@ -5340,7 +5682,7 @@ async function processAgent(agent, agentStates) {
5340
5682
  });
5341
5683
  } catch (err) {
5342
5684
  log(`Refresh failed for '${agent.code_name}': ${err.message}`);
5343
- const existing = state4.agents.find((a) => a.agentId === agent.agent_id);
5685
+ const existing = state5.agents.find((a) => a.agentId === agent.agent_id);
5344
5686
  agentStates.push(existing ?? {
5345
5687
  agentId: agent.agent_id,
5346
5688
  codeName: agent.code_name,
@@ -5388,7 +5730,7 @@ async function processAgent(agent, agentStates) {
5388
5730
  const frameworkId = refreshData.agent.framework ?? "openclaw";
5389
5731
  agentFrameworkCache.set(agent.code_name, frameworkId);
5390
5732
  const frameworkAdapter = getFramework(frameworkId);
5391
- agentDir = join7(frameworkAdapter.getAgentDir(agent.code_name), "provision");
5733
+ agentDir = join8(frameworkAdapter.getAgentDir(agent.code_name), "provision");
5392
5734
  cacheAgentDeliveryMetadata(agent.code_name, refreshData);
5393
5735
  if (frameworkAdapter.migrateSecretStorage && !migratedSecretStorage.has(agent.code_name)) {
5394
5736
  try {
@@ -5404,7 +5746,7 @@ async function processAgent(agent, agentStates) {
5404
5746
  const charterVersion = refreshData.charter.version;
5405
5747
  const toolsVersion = refreshData.tools.version;
5406
5748
  const known = agentState.knownVersions.get(agent.agent_id);
5407
- let lastProvisionAt = state4.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
5749
+ let lastProvisionAt = state5.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
5408
5750
  const quarantinedChannels = channelQuarantineStore().getQuarantinedKeys(agent.code_name);
5409
5751
  const currentChannelIds = setWithout(
5410
5752
  launchableChannelIds(refreshData.channel_configs),
@@ -5431,7 +5773,7 @@ async function processAgent(agent, agentStates) {
5431
5773
  const changedFiles = [];
5432
5774
  mkdirSync4(agentDir, { recursive: true });
5433
5775
  for (const artifact of artifacts) {
5434
- const filePath = join7(agentDir, artifact.relativePath);
5776
+ const filePath = join8(agentDir, artifact.relativePath);
5435
5777
  let existingHash;
5436
5778
  let newHash;
5437
5779
  let writeContent = artifact.content;
@@ -5450,8 +5792,8 @@ async function processAgent(agent, agentStates) {
5450
5792
  };
5451
5793
  newHash = sha256(stripDynamicSections(artifact.content));
5452
5794
  try {
5453
- const projectClaudeMd = join7(config.configDir, agent.code_name, "project", "CLAUDE.md");
5454
- const existing = readFileSync8(projectClaudeMd, "utf-8");
5795
+ const projectClaudeMd = join8(config.configDir, agent.code_name, "project", "CLAUDE.md");
5796
+ const existing = readFileSync9(projectClaudeMd, "utf-8");
5455
5797
  existingHash = sha256(stripDynamicSections(existing));
5456
5798
  } catch {
5457
5799
  existingHash = null;
@@ -5469,7 +5811,7 @@ async function processAgent(agent, agentStates) {
5469
5811
  const generatorKeys = Object.keys(generatorServers);
5470
5812
  let existingRaw = "";
5471
5813
  try {
5472
- existingRaw = readFileSync8(filePath, "utf-8");
5814
+ existingRaw = readFileSync9(filePath, "utf-8");
5473
5815
  } catch {
5474
5816
  }
5475
5817
  const existingServers = parseMcp(existingRaw);
@@ -5491,12 +5833,12 @@ async function processAgent(agent, agentStates) {
5491
5833
  }
5492
5834
  }
5493
5835
  if (changedFiles.length > 0) {
5494
- const isFirst = !existsSync5(join7(agentDir, "CHARTER.md"));
5836
+ const isFirst = !existsSync5(join8(agentDir, "CHARTER.md"));
5495
5837
  const verb = isFirst ? "Provisioning" : "Updating";
5496
5838
  const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
5497
5839
  log(`${verb} '${agent.code_name}': ${fileNames}`);
5498
5840
  for (const file of changedFiles) {
5499
- const filePath = join7(agentDir, file.relativePath);
5841
+ const filePath = join8(agentDir, file.relativePath);
5500
5842
  mkdirSync4(dirname3(filePath), { recursive: true });
5501
5843
  if (file.relativePath === ".mcp.json") {
5502
5844
  safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
@@ -5505,12 +5847,12 @@ async function processAgent(agent, agentStates) {
5505
5847
  }
5506
5848
  }
5507
5849
  try {
5508
- const provSkillsDir = join7(agentDir, ".claude", "skills");
5850
+ const provSkillsDir = join8(agentDir, ".claude", "skills");
5509
5851
  if (existsSync5(provSkillsDir)) {
5510
- for (const folder of readdirSync3(provSkillsDir)) {
5852
+ for (const folder of readdirSync4(provSkillsDir)) {
5511
5853
  if (folder.startsWith("knowledge-")) {
5512
5854
  try {
5513
- rmSync2(join7(provSkillsDir, folder), { recursive: true });
5855
+ rmSync2(join8(provSkillsDir, folder), { recursive: true });
5514
5856
  } catch {
5515
5857
  }
5516
5858
  }
@@ -5523,7 +5865,7 @@ async function processAgent(agent, agentStates) {
5523
5865
  const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
5524
5866
  const hashes = /* @__PURE__ */ new Map();
5525
5867
  for (const file of trackedFiles2) {
5526
- const h = hashFile(join7(agentDir, file));
5868
+ const h = hashFile(join8(agentDir, file));
5527
5869
  if (h) hashes.set(file, h);
5528
5870
  }
5529
5871
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -5591,7 +5933,7 @@ async function processAgent(agent, agentStates) {
5591
5933
  if (written && existsSync5(agentDir)) {
5592
5934
  const driftedFiles = [];
5593
5935
  for (const [file, expectedHash] of written) {
5594
- const localHash = hashFile(join7(agentDir, file));
5936
+ const localHash = hashFile(join8(agentDir, file));
5595
5937
  if (localHash && localHash !== expectedHash) {
5596
5938
  driftedFiles.push(file);
5597
5939
  }
@@ -5602,7 +5944,7 @@ async function processAgent(agent, agentStates) {
5602
5944
  try {
5603
5945
  const localHashes = {};
5604
5946
  for (const file of driftedFiles) {
5605
- localHashes[file] = hashFile(join7(agentDir, file));
5947
+ localHashes[file] = hashFile(join8(agentDir, file));
5606
5948
  }
5607
5949
  await api.post("/host/drift", {
5608
5950
  agent_id: agent.agent_id,
@@ -5865,18 +6207,18 @@ async function processAgent(agent, agentStates) {
5865
6207
  if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
5866
6208
  try {
5867
6209
  const agentProvisionDir = agentDir;
5868
- const projectDir = join7(homedir4(), ".augmented", agent.code_name, "project");
6210
+ const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
5869
6211
  mkdirSync4(agentProvisionDir, { recursive: true });
5870
6212
  mkdirSync4(projectDir, { recursive: true });
5871
- const provisionMcpPath = join7(agentProvisionDir, ".mcp.json");
5872
- const projectMcpPath = join7(projectDir, ".mcp.json");
6213
+ const provisionMcpPath = join8(agentProvisionDir, ".mcp.json");
6214
+ const projectMcpPath = join8(projectDir, ".mcp.json");
5873
6215
  let mcpConfig = { mcpServers: {} };
5874
6216
  try {
5875
- mcpConfig = JSON.parse(readFileSync8(provisionMcpPath, "utf-8"));
6217
+ mcpConfig = JSON.parse(readFileSync9(provisionMcpPath, "utf-8"));
5876
6218
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
5877
6219
  } catch {
5878
6220
  }
5879
- const localDirectChatChannel = join7(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
6221
+ const localDirectChatChannel = join8(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
5880
6222
  const directChatTeamSettings = refreshData.team?.settings;
5881
6223
  const directChatTz = (() => {
5882
6224
  const tz = directChatTeamSettings?.["timezone"];
@@ -5906,7 +6248,7 @@ async function processAgent(agent, agentStates) {
5906
6248
  log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
5907
6249
  }
5908
6250
  }
5909
- const staleChannelsPath = join7(projectDir, ".mcp-channels.json");
6251
+ const staleChannelsPath = join8(projectDir, ".mcp-channels.json");
5910
6252
  if (existsSync5(staleChannelsPath)) {
5911
6253
  try {
5912
6254
  rmSync2(staleChannelsPath, { force: true });
@@ -5917,7 +6259,7 @@ async function processAgent(agent, agentStates) {
5917
6259
  log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
5918
6260
  }
5919
6261
  }
5920
- let lastSecretsProvisionAt = state4.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
6262
+ let lastSecretsProvisionAt = state5.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
5921
6263
  let secretsHash = agentState.knownSecretsHashes.get(agent.agent_id) ?? null;
5922
6264
  try {
5923
6265
  const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
@@ -5971,7 +6313,7 @@ async function processAgent(agent, agentStates) {
5971
6313
  }
5972
6314
  if (process.env.AGT_CONNECTIVITY_PROBE_ENABLED === "true") {
5973
6315
  try {
5974
- const probeProjectDir = join7(homedir4(), ".augmented", agent.code_name, "project");
6316
+ const probeProjectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
5975
6317
  await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
5976
6318
  } catch (err) {
5977
6319
  log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
@@ -5981,11 +6323,11 @@ async function processAgent(agent, agentStates) {
5981
6323
  const intHash = computeIntegrationsHash(integrations);
5982
6324
  const prevIntHash = agentState.knownIntegrationHashes.get(agent.agent_id);
5983
6325
  if (intHash !== prevIntHash) {
5984
- const projectDir = join7(homedir4(), ".augmented", agent.code_name, "project");
5985
- const envIntPath = join7(projectDir, ".env.integrations");
6326
+ const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
6327
+ const envIntPath = join8(projectDir, ".env.integrations");
5986
6328
  let preWriteEnv;
5987
6329
  try {
5988
- preWriteEnv = readFileSync8(envIntPath, "utf-8");
6330
+ preWriteEnv = readFileSync9(envIntPath, "utf-8");
5989
6331
  } catch {
5990
6332
  preWriteEnv = void 0;
5991
6333
  }
@@ -5997,9 +6339,9 @@ async function processAgent(agent, agentStates) {
5997
6339
  let rotationHandled = true;
5998
6340
  if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
5999
6341
  try {
6000
- const projectMcpPath = join7(projectDir, ".mcp.json");
6001
- const postWriteEnv = readFileSync8(envIntPath, "utf-8");
6002
- const mcpContent = readFileSync8(projectMcpPath, "utf-8");
6342
+ const projectMcpPath = join8(projectDir, ".mcp.json");
6343
+ const postWriteEnv = readFileSync9(envIntPath, "utf-8");
6344
+ const mcpContent = readFileSync9(projectMcpPath, "utf-8");
6003
6345
  const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
6004
6346
  const mcpJsonForReap = JSON.parse(mcpContent);
6005
6347
  const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
@@ -6067,8 +6409,8 @@ async function processAgent(agent, agentStates) {
6067
6409
  const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
6068
6410
  if (mcpPath) {
6069
6411
  try {
6070
- const { readFileSync: readFileSync9 } = await import("fs");
6071
- const mcpConfig = JSON.parse(readFileSync9(mcpPath, "utf-8"));
6412
+ const { readFileSync: readFileSync10 } = await import("fs");
6413
+ const mcpConfig = JSON.parse(readFileSync10(mcpPath, "utf-8"));
6072
6414
  if (mcpConfig.mcpServers) {
6073
6415
  const managedPrefixes = [
6074
6416
  "composio_",
@@ -6164,7 +6506,7 @@ async function processAgent(agent, agentStates) {
6164
6506
  if (agent.status === "active") {
6165
6507
  if (frameworkAdapter.installPlugin) {
6166
6508
  try {
6167
- const pluginPath = join7(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
6509
+ const pluginPath = join8(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
6168
6510
  if (existsSync5(pluginPath)) {
6169
6511
  frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
6170
6512
  agtHost: requireHost(),
@@ -6230,25 +6572,25 @@ async function processAgent(agent, agentStates) {
6230
6572
  }
6231
6573
  }
6232
6574
  try {
6233
- const { readdirSync: readdirSync4, rmSync: rmSync3 } = await import("fs");
6575
+ const { readdirSync: readdirSync5, rmSync: rmSync3 } = await import("fs");
6234
6576
  const { homedir: homedir5 } = await import("os");
6235
6577
  const frameworkId2 = frameworkAdapter.id;
6236
6578
  const candidateSkillDirs = [
6237
6579
  // Claude Code — framework runtime tree
6238
- join7(homedir5(), ".augmented", agent.code_name, "skills"),
6580
+ join8(homedir5(), ".augmented", agent.code_name, "skills"),
6239
6581
  // Claude Code — project tree
6240
- join7(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
6582
+ join8(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
6241
6583
  // OpenClaw — framework runtime tree
6242
- join7(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
6584
+ join8(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
6243
6585
  // Defensive: legacy provision-side path, not currently an
6244
6586
  // install target but cheap to sweep.
6245
- join7(agentDir, ".claude", "skills")
6587
+ join8(agentDir, ".claude", "skills")
6246
6588
  ];
6247
6589
  const existingDirs = candidateSkillDirs.filter((d) => existsSync5(d));
6248
6590
  const discoveredEntries = /* @__PURE__ */ new Set();
6249
6591
  for (const dir of existingDirs) {
6250
6592
  try {
6251
- for (const entry of readdirSync4(dir)) {
6593
+ for (const entry of readdirSync5(dir)) {
6252
6594
  if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
6253
6595
  discoveredEntries.add(entry);
6254
6596
  }
@@ -6258,7 +6600,7 @@ async function processAgent(agent, agentStates) {
6258
6600
  }
6259
6601
  const removeSkillFolder = (entry, reason) => {
6260
6602
  for (const dir of existingDirs) {
6261
- const p = join7(dir, entry);
6603
+ const p = join8(dir, entry);
6262
6604
  if (existsSync5(p)) {
6263
6605
  rmSync3(p, { recursive: true, force: true });
6264
6606
  }
@@ -6420,8 +6762,8 @@ async function processAgent(agent, agentStates) {
6420
6762
  const sess = getSessionState(agent.code_name);
6421
6763
  let mcpJsonParsed = null;
6422
6764
  try {
6423
- const mcpPath = join7(getProjectDir(agent.code_name), ".mcp.json");
6424
- mcpJsonParsed = JSON.parse(readFileSync8(mcpPath, "utf-8"));
6765
+ const mcpPath = join8(getProjectDir(agent.code_name), ".mcp.json");
6766
+ mcpJsonParsed = JSON.parse(readFileSync9(mcpPath, "utf-8"));
6425
6767
  } catch {
6426
6768
  }
6427
6769
  reapMissingMcpSessions({
@@ -6606,10 +6948,10 @@ async function processAgent(agent, agentStates) {
6606
6948
  lastWorkTriggerAt.set(agent.code_name, triggerTs);
6607
6949
  if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
6608
6950
  const homeDir = process.env["HOME"] ?? "/tmp";
6609
- const jobsPath = join7(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
6951
+ const jobsPath = join8(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
6610
6952
  if (existsSync5(jobsPath)) {
6611
6953
  try {
6612
- const jobsData = JSON.parse(readFileSync8(jobsPath, "utf-8"));
6954
+ const jobsData = JSON.parse(readFileSync9(jobsPath, "utf-8"));
6613
6955
  const kanbanJob = (jobsData.jobs ?? []).find(
6614
6956
  (j) => typeof j.name === "string" && j.name.includes("kanban-work")
6615
6957
  );
@@ -6746,7 +7088,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
6746
7088
  if (trackedFiles.length > 0 && existsSync5(agentDir)) {
6747
7089
  const hashes = /* @__PURE__ */ new Map();
6748
7090
  for (const file of trackedFiles) {
6749
- const h = hashFile(join7(agentDir, file));
7091
+ const h = hashFile(join8(agentDir, file));
6750
7092
  if (h) hashes.set(file, h);
6751
7093
  }
6752
7094
  agentState.writtenHashes.set(agent.agent_id, hashes);
@@ -6780,19 +7122,19 @@ function cleanupStaleSessions(codeName) {
6780
7122
  lastCleanupAt.set(codeName, Date.now());
6781
7123
  const homeDir = process.env["HOME"] ?? "/tmp";
6782
7124
  for (const agentDir of ["main", codeName]) {
6783
- const sessionsDir = join7(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
7125
+ const sessionsDir = join8(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
6784
7126
  cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
6785
7127
  }
6786
- const cronRunsDir = join7(homeDir, `.openclaw-${codeName}`, "cron", "runs");
7128
+ const cronRunsDir = join8(homeDir, `.openclaw-${codeName}`, "cron", "runs");
6787
7129
  cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
6788
- const cronJobsPath = join7(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
7130
+ const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
6789
7131
  clearStaleCronRunState(cronJobsPath);
6790
7132
  }
6791
7133
  function cleanupCronSessions(sessionsDir, keepCount) {
6792
- const indexPath = join7(sessionsDir, "sessions.json");
7134
+ const indexPath = join8(sessionsDir, "sessions.json");
6793
7135
  if (!existsSync5(indexPath)) return;
6794
7136
  try {
6795
- const raw = readFileSync8(indexPath, "utf-8");
7137
+ const raw = readFileSync9(indexPath, "utf-8");
6796
7138
  const index = JSON.parse(raw);
6797
7139
  const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
6798
7140
  key: k,
@@ -6805,7 +7147,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
6805
7147
  for (const entry of toDelete) {
6806
7148
  delete index[entry.key];
6807
7149
  if (entry.sessionId) {
6808
- const sessionFile = join7(sessionsDir, `${entry.sessionId}.jsonl`);
7150
+ const sessionFile = join8(sessionsDir, `${entry.sessionId}.jsonl`);
6809
7151
  try {
6810
7152
  if (existsSync5(sessionFile)) {
6811
7153
  unlinkSync(sessionFile);
@@ -6827,7 +7169,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
6827
7169
  delete index[parentKey];
6828
7170
  if (parentSessionId) {
6829
7171
  try {
6830
- const f = join7(sessionsDir, `${parentSessionId}.jsonl`);
7172
+ const f = join8(sessionsDir, `${parentSessionId}.jsonl`);
6831
7173
  if (existsSync5(f)) {
6832
7174
  unlinkSync(f);
6833
7175
  deletedFiles++;
@@ -6848,26 +7190,26 @@ var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
6848
7190
  function clearStaleCronRunState(jobsPath) {
6849
7191
  if (!existsSync5(jobsPath)) return;
6850
7192
  try {
6851
- const raw = readFileSync8(jobsPath, "utf-8");
7193
+ const raw = readFileSync9(jobsPath, "utf-8");
6852
7194
  const data = JSON.parse(raw);
6853
7195
  const jobs = data.jobs ?? data;
6854
7196
  if (!Array.isArray(jobs)) return;
6855
7197
  let changed = false;
6856
7198
  const now = Date.now();
6857
7199
  for (const job of jobs) {
6858
- const state5 = job.state;
6859
- if (!state5) continue;
6860
- const runStartedAt = state5.runningAtMs ?? state5.runStartedAtMs;
6861
- const isRunning = state5.running === true || state5.status === "running";
7200
+ const state6 = job.state;
7201
+ if (!state6) continue;
7202
+ const runStartedAt = state6.runningAtMs ?? state6.runStartedAtMs;
7203
+ const isRunning = state6.running === true || state6.status === "running";
6862
7204
  if (isRunning && runStartedAt && now - runStartedAt > STALE_RUN_TIMEOUT_MS) {
6863
- state5.running = false;
6864
- delete state5.status;
6865
- delete state5.runStartedAtMs;
6866
- delete state5.currentRunId;
7205
+ state6.running = false;
7206
+ delete state6.status;
7207
+ delete state6.runStartedAtMs;
7208
+ delete state6.currentRunId;
6867
7209
  changed = true;
6868
7210
  log(`Cleared stale running state for cron job '${job.name}' (stuck for ${Math.round((now - runStartedAt) / 6e4)}min)`);
6869
7211
  } else if (isRunning && !runStartedAt) {
6870
- state5.running = false;
7212
+ state6.running = false;
6871
7213
  changed = true;
6872
7214
  log(`Cleared stale running state for cron job '${job.name}' (no start timestamp)`);
6873
7215
  }
@@ -6883,11 +7225,11 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
6883
7225
  const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
6884
7226
  let removed = 0;
6885
7227
  try {
6886
- for (const f of readdirSync3(dir)) {
7228
+ for (const f of readdirSync4(dir)) {
6887
7229
  if (!f.endsWith(ext)) continue;
6888
- const fullPath = join7(dir, f);
7230
+ const fullPath = join8(dir, f);
6889
7231
  try {
6890
- const st = statSync2(fullPath);
7232
+ const st = statSync3(fullPath);
6891
7233
  if (st.mtimeMs < cutoff) {
6892
7234
  unlinkSync(fullPath);
6893
7235
  removed++;
@@ -6905,7 +7247,7 @@ var inFlightClaudeTasks = /* @__PURE__ */ new Set();
6905
7247
  var claudeTaskConcurrency = /* @__PURE__ */ new Map();
6906
7248
  var MAX_CLAUDE_CONCURRENCY = 2;
6907
7249
  function claudePidFilePath() {
6908
- return join7(homedir4(), ".augmented", "manager-claude-pids.json");
7250
+ return join8(homedir4(), ".augmented", "manager-claude-pids.json");
6909
7251
  }
6910
7252
  var inFlightClaudePids = /* @__PURE__ */ new Map();
6911
7253
  function registerClaudeSpawn(record) {
@@ -6950,16 +7292,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
6950
7292
  enabled: t.enabled ?? true,
6951
7293
  triggered_at: t.triggered_at ?? null
6952
7294
  }));
6953
- const state6 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
6954
- claudeSchedulerStates.set(codeName, state6);
7295
+ const state7 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
7296
+ claudeSchedulerStates.set(codeName, state7);
6955
7297
  agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
6956
7298
  log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
6957
7299
  }
6958
7300
  if (!claudeSchedulerStates.has(codeName)) {
6959
7301
  claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
6960
7302
  }
6961
- const state5 = claudeSchedulerStates.get(codeName);
6962
- const ready = getReadyTasks(state5, inFlightClaudeTasks);
7303
+ const state6 = claudeSchedulerStates.get(codeName);
7304
+ const ready = getReadyTasks(state6, inFlightClaudeTasks);
6963
7305
  if (ready.length === 0) return;
6964
7306
  for (const task of ready) {
6965
7307
  if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
@@ -7106,8 +7448,8 @@ async function deliverScheduledCardResult(codeName, agentId, cardId) {
7106
7448
  markScheduledCardDeliveryComplete(cardId);
7107
7449
  return "terminal";
7108
7450
  }
7109
- const state5 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
7110
- const task = state5.tasks[card.source_ref];
7451
+ const state6 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
7452
+ const task = state6.tasks[card.source_ref];
7111
7453
  if (!task) {
7112
7454
  log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
7113
7455
  markScheduledCardDeliveryComplete(cardId);
@@ -7213,7 +7555,7 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
7213
7555
  }
7214
7556
  async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
7215
7557
  const projectDir = getProjectDir2(codeName);
7216
- const mcpConfigPath = join7(projectDir, ".mcp.json");
7558
+ const mcpConfigPath = join8(projectDir, ".mcp.json");
7217
7559
  let runId = null;
7218
7560
  let kanbanItemId = null;
7219
7561
  let taskResult;
@@ -7221,11 +7563,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
7221
7563
  const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
7222
7564
  prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
7223
7565
  try {
7224
- const claudeMdPath = join7(projectDir, "CLAUDE.md");
7566
+ const claudeMdPath = join8(projectDir, "CLAUDE.md");
7225
7567
  const serverNames = [];
7226
7568
  if (existsSync5(mcpConfigPath)) {
7227
7569
  try {
7228
- const d = JSON.parse(readFileSync8(mcpConfigPath, "utf-8"));
7570
+ const d = JSON.parse(readFileSync9(mcpConfigPath, "utf-8"));
7229
7571
  if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
7230
7572
  } catch {
7231
7573
  }
@@ -7248,10 +7590,10 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
7248
7590
  claudeArgs.push("--system-prompt-file", claudeMdPath);
7249
7591
  }
7250
7592
  const childEnv = { ...process.env };
7251
- const envIntPath = join7(projectDir, ".env.integrations");
7593
+ const envIntPath = join8(projectDir, ".env.integrations");
7252
7594
  if (existsSync5(envIntPath)) {
7253
7595
  try {
7254
- Object.assign(childEnv, parseEnvIntegrations(readFileSync8(envIntPath, "utf-8")));
7596
+ Object.assign(childEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
7255
7597
  } catch {
7256
7598
  }
7257
7599
  }
@@ -7423,8 +7765,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
7423
7765
  async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
7424
7766
  const codeName = agent.code_name;
7425
7767
  const projectDir = getProjectDir(codeName);
7426
- const mcpConfigPath = join7(projectDir, ".mcp.json");
7427
- const claudeMdPath = join7(projectDir, "CLAUDE.md");
7768
+ const mcpConfigPath = join8(projectDir, ".mcp.json");
7769
+ const claudeMdPath = join8(projectDir, "CLAUDE.md");
7428
7770
  if (restartBreaker.isTripped(codeName)) {
7429
7771
  const trip = restartBreaker.getTrip(codeName);
7430
7772
  return {
@@ -7651,9 +7993,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash3("sha256").
7651
7993
  } else if (!claudeSchedulerStates.has(codeName)) {
7652
7994
  claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
7653
7995
  }
7654
- const state5 = claudeSchedulerStates.get(codeName);
7655
- if (state5) {
7656
- const ready = getReadyTasks(state5, inFlightClaudeTasks);
7996
+ const state6 = claudeSchedulerStates.get(codeName);
7997
+ if (state6) {
7998
+ const ready = getReadyTasks(state6, inFlightClaudeTasks);
7657
7999
  if (ready.length > 0) {
7658
8000
  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(", ")}`);
7659
8001
  }
@@ -8064,11 +8406,11 @@ ${escapeXml(msg.content)}
8064
8406
  log(`[direct-chat] One-shot spawn for '${agent.codeName}' (msg=${msg.id}; host in-flight=${directChatSpawnGate.hostInFlight}, queued=${directChatSpawnGate.queuedCount})`);
8065
8407
  const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-EM24LTGV.js");
8066
8408
  const projDir = ccProjectDir(agent.codeName);
8067
- const mcpConfigPath = join7(projDir, ".mcp.json");
8409
+ const mcpConfigPath = join8(projDir, ".mcp.json");
8068
8410
  const serverNames = [];
8069
8411
  if (existsSync5(mcpConfigPath)) {
8070
8412
  try {
8071
- const d = JSON.parse(readFileSync8(mcpConfigPath, "utf-8"));
8413
+ const d = JSON.parse(readFileSync9(mcpConfigPath, "utf-8"));
8072
8414
  if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
8073
8415
  } catch {
8074
8416
  }
@@ -8087,15 +8429,15 @@ ${escapeXml(msg.content)}
8087
8429
  "--allowedTools",
8088
8430
  allowedTools
8089
8431
  ];
8090
- const chatClaudeMd = join7(projDir, "CLAUDE.md");
8432
+ const chatClaudeMd = join8(projDir, "CLAUDE.md");
8091
8433
  if (existsSync5(chatClaudeMd)) {
8092
8434
  chatArgs.push("--system-prompt-file", chatClaudeMd);
8093
8435
  }
8094
- const envIntPath = join7(projDir, ".env.integrations");
8436
+ const envIntPath = join8(projDir, ".env.integrations");
8095
8437
  const childEnv = { ...process.env };
8096
8438
  if (existsSync5(envIntPath)) {
8097
8439
  try {
8098
- Object.assign(childEnv, parseEnvIntegrations(readFileSync8(envIntPath, "utf-8")));
8440
+ Object.assign(childEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
8099
8441
  } catch {
8100
8442
  }
8101
8443
  }
@@ -8473,12 +8815,12 @@ function getBuiltInSkillContent(skillId) {
8473
8815
  if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
8474
8816
  try {
8475
8817
  const candidates = [
8476
- join7(process.cwd(), "skills", skillId, "SKILL.md"),
8477
- join7(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
8818
+ join8(process.cwd(), "skills", skillId, "SKILL.md"),
8819
+ join8(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
8478
8820
  ];
8479
8821
  for (const candidate of candidates) {
8480
8822
  if (existsSync5(candidate)) {
8481
- const content = readFileSync8(candidate, "utf-8");
8823
+ const content = readFileSync9(candidate, "utf-8");
8482
8824
  const files = [{ relativePath: "SKILL.md", content }];
8483
8825
  builtInSkillCache.set(skillId, files);
8484
8826
  return files;
@@ -9121,7 +9463,7 @@ async function processClaudePairSessions(agents) {
9121
9463
  killPairSession,
9122
9464
  pairTmuxSession,
9123
9465
  finalizeClaudePairOnboarding
9124
- } = await import("../claude-pair-runtime-MKK7LSQG.js");
9466
+ } = await import("../claude-pair-runtime-DY2LN3ED.js");
9125
9467
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
9126
9468
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
9127
9469
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -9431,8 +9773,8 @@ function parseMemoryFile(raw, fallbackName) {
9431
9773
  };
9432
9774
  }
9433
9775
  async function syncMemories(agent, configDir, log2) {
9434
- const projectDir = join7(configDir, agent.code_name, "project");
9435
- const memoryDir = join7(projectDir, "memory");
9776
+ const projectDir = join8(configDir, agent.code_name, "project");
9777
+ const memoryDir = join8(projectDir, "memory");
9436
9778
  const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
9437
9779
  if (isFreshSync) {
9438
9780
  log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
@@ -9447,10 +9789,10 @@ async function syncMemories(agent, configDir, log2) {
9447
9789
  const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
9448
9790
  const currentHashes = /* @__PURE__ */ new Map();
9449
9791
  const changedMemories = [];
9450
- for (const file of readdirSync3(memoryDir)) {
9792
+ for (const file of readdirSync4(memoryDir)) {
9451
9793
  if (!file.endsWith(".md")) continue;
9452
9794
  try {
9453
- const raw = readFileSync8(join7(memoryDir, file), "utf-8");
9795
+ const raw = readFileSync9(join8(memoryDir, file), "utf-8");
9454
9796
  const fileHash = createHash3("sha256").update(raw).digest("hex").slice(0, 16);
9455
9797
  currentHashes.set(file, fileHash);
9456
9798
  if (prevHashes.get(file) === fileHash) continue;
@@ -9475,7 +9817,7 @@ async function syncMemories(agent, configDir, log2) {
9475
9817
  } catch (err) {
9476
9818
  for (const mem of changedMemories) {
9477
9819
  for (const [file] of currentHashes) {
9478
- const parsed = parseMemoryFile(readFileSync8(join7(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
9820
+ const parsed = parseMemoryFile(readFileSync9(join8(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
9479
9821
  if (parsed?.name === mem.name) currentHashes.delete(file);
9480
9822
  }
9481
9823
  }
@@ -9488,7 +9830,7 @@ async function syncMemories(agent, configDir, log2) {
9488
9830
  }
9489
9831
  }
9490
9832
  async function downloadMemories(agent, memoryDir, log2, { force }) {
9491
- const localFiles = existsSync5(memoryDir) ? readdirSync3(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
9833
+ const localFiles = existsSync5(memoryDir) ? readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
9492
9834
  const localListHash = createHash3("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
9493
9835
  const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
9494
9836
  const prevDownload = lastDownloadHash.get(agent.agent_id);
@@ -9510,7 +9852,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
9510
9852
  const mem = dbMemories.memories[i];
9511
9853
  const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
9512
9854
  const slug = rawSlug || `memory-${i}`;
9513
- const filePath = join7(memoryDir, `${slug}.md`);
9855
+ const filePath = join8(memoryDir, `${slug}.md`);
9514
9856
  const desired = `---
9515
9857
  name: ${JSON.stringify(mem.name)}
9516
9858
  type: ${mem.type}
@@ -9522,7 +9864,7 @@ ${mem.content}
9522
9864
  if (existsSync5(filePath)) {
9523
9865
  let existing = "";
9524
9866
  try {
9525
- existing = readFileSync8(filePath, "utf-8");
9867
+ existing = readFileSync9(filePath, "utf-8");
9526
9868
  } catch {
9527
9869
  }
9528
9870
  if (existing === desired) continue;
@@ -9534,7 +9876,7 @@ ${mem.content}
9534
9876
  }
9535
9877
  }
9536
9878
  if (written > 0 || overwritten > 0) {
9537
- const updatedFiles = readdirSync3(memoryDir).filter((f) => f.endsWith(".md")).sort();
9879
+ const updatedFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).sort();
9538
9880
  lastLocalFileHash.set(agent.agent_id, createHash3("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
9539
9881
  log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
9540
9882
  }
@@ -9736,11 +10078,11 @@ function startManager(opts) {
9736
10078
  try {
9737
10079
  const stateFile = getStateFile();
9738
10080
  if (existsSync5(stateFile)) {
9739
- const raw = readFileSync8(stateFile, "utf-8");
10081
+ const raw = readFileSync9(stateFile, "utf-8");
9740
10082
  const parsed = JSON.parse(raw);
9741
10083
  if (Array.isArray(parsed.agents)) {
9742
- state4.agents = parsed.agents;
9743
- log(`[startup] rehydrated ${state4.agents.length} agent state(s) from ${stateFile}`);
10084
+ state5.agents = parsed.agents;
10085
+ log(`[startup] rehydrated ${state5.agents.length} agent state(s) from ${stateFile}`);
9744
10086
  }
9745
10087
  if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
9746
10088
  restartBreaker.hydrate(parsed.circuitBreakerTrips);
@@ -9752,7 +10094,7 @@ function startManager(opts) {
9752
10094
  log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
9753
10095
  }
9754
10096
  log(
9755
- `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join7(homedir4(), ".augmented", "manager.log")}`
10097
+ `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join8(homedir4(), ".augmented", "manager.log")}`
9756
10098
  );
9757
10099
  deployMcpAssets();
9758
10100
  reapOrphanChannelMcps({ log });
@@ -9773,7 +10115,7 @@ async function reapOrphanedClaudePids() {
9773
10115
  const looksLikeClaude = (pid) => {
9774
10116
  if (process.platform !== "linux") return true;
9775
10117
  try {
9776
- const comm = readFileSync8(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
10118
+ const comm = readFileSync9(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
9777
10119
  return comm.includes("claude");
9778
10120
  } catch {
9779
10121
  return false;
@@ -9883,14 +10225,14 @@ function restartRunningChannelMcps(basenames) {
9883
10225
  }
9884
10226
  }
9885
10227
  function deployMcpAssets() {
9886
- const targetDir = join7(homedir4(), ".augmented", "_mcp");
10228
+ const targetDir = join8(homedir4(), ".augmented", "_mcp");
9887
10229
  mkdirSync4(targetDir, { recursive: true });
9888
10230
  const moduleDir = dirname3(fileURLToPath(import.meta.url));
9889
10231
  let mcpSourceDir = "";
9890
10232
  let dir = moduleDir;
9891
10233
  for (let i = 0; i < 6; i++) {
9892
- const candidate = join7(dir, "dist", "mcp");
9893
- if (existsSync5(join7(candidate, "index.js"))) {
10234
+ const candidate = join8(dir, "dist", "mcp");
10235
+ if (existsSync5(join8(candidate, "index.js"))) {
9894
10236
  mcpSourceDir = candidate;
9895
10237
  break;
9896
10238
  }
@@ -9906,7 +10248,7 @@ function deployMcpAssets() {
9906
10248
  const fileHash = (p) => {
9907
10249
  try {
9908
10250
  if (!existsSync5(p)) return null;
9909
- return createHash3("sha256").update(readFileSync8(p)).digest("hex");
10251
+ return createHash3("sha256").update(readFileSync9(p)).digest("hex");
9910
10252
  } catch {
9911
10253
  return null;
9912
10254
  }
@@ -9927,8 +10269,8 @@ function deployMcpAssets() {
9927
10269
  "teams-channel.js"
9928
10270
  // ENG-6019
9929
10271
  ]) {
9930
- const src = join7(mcpSourceDir, file);
9931
- const dst = join7(targetDir, file);
10272
+ const src = join8(mcpSourceDir, file);
10273
+ const dst = join8(targetDir, file);
9932
10274
  if (!existsSync5(src)) continue;
9933
10275
  const before = fileHash(dst);
9934
10276
  try {
@@ -9946,16 +10288,16 @@ function deployMcpAssets() {
9946
10288
  log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
9947
10289
  restartRunningChannelMcps(changedBasenames);
9948
10290
  }
9949
- const localMcpPath = join7(targetDir, "index.js");
10291
+ const localMcpPath = join8(targetDir, "index.js");
9950
10292
  try {
9951
- const agentsDir = join7(homedir4(), ".augmented", "agents");
10293
+ const agentsDir = join8(homedir4(), ".augmented", "agents");
9952
10294
  if (existsSync5(agentsDir)) {
9953
- for (const entry of readdirSync3(agentsDir, { withFileTypes: true })) {
10295
+ for (const entry of readdirSync4(agentsDir, { withFileTypes: true })) {
9954
10296
  if (!entry.isDirectory()) continue;
9955
10297
  for (const subdir of ["provision", "project"]) {
9956
- const mcpJsonPath = join7(agentsDir, entry.name, subdir, ".mcp.json");
10298
+ const mcpJsonPath = join8(agentsDir, entry.name, subdir, ".mcp.json");
9957
10299
  try {
9958
- const raw = readFileSync8(mcpJsonPath, "utf-8");
10300
+ const raw = readFileSync9(mcpJsonPath, "utf-8");
9959
10301
  if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
9960
10302
  const mcpConfig = JSON.parse(raw);
9961
10303
  const augServer = mcpConfig.mcpServers?.["augmented"];