@integrity-labs/agt-cli 0.19.2 → 0.19.4

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.
@@ -22,7 +22,7 @@ import {
22
22
  resolveChannels,
23
23
  resolveDmTarget,
24
24
  wrapScheduledTaskPrompt
25
- } from "../chunk-BEIZXSG4.js";
25
+ } from "../chunk-MC3SMG4A.js";
26
26
  import {
27
27
  findTaskByTemplate,
28
28
  getProjectDir,
@@ -51,10 +51,10 @@ import {
51
51
 
52
52
  // src/lib/manager-worker.ts
53
53
  import { createHash } from "crypto";
54
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, appendFileSync, mkdirSync as mkdirSync2, chmodSync, existsSync as existsSync2, rmSync as rmSync2, readdirSync as readdirSync2, statSync, unlinkSync, copyFileSync } from "fs";
54
+ 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";
55
55
  import https from "https";
56
56
  import { execFileSync as syncExecFile } from "child_process";
57
- import { join as join3, dirname } from "path";
57
+ import { join as join4, dirname } from "path";
58
58
  import { homedir as homedir3 } from "os";
59
59
  import { fileURLToPath } from "url";
60
60
 
@@ -588,6 +588,37 @@ function normalize(value) {
588
588
  return sorted;
589
589
  }
590
590
 
591
+ // src/lib/channel-hash-cache.ts
592
+ import { existsSync, readFileSync, writeFileSync } from "fs";
593
+ import { join as join2 } from "path";
594
+ var CACHE_FILENAME = "channel-hash-cache.json";
595
+ function getChannelHashCacheFile(configDir) {
596
+ return join2(configDir, CACHE_FILENAME);
597
+ }
598
+ function loadChannelHashCache(target, configDir) {
599
+ const path = getChannelHashCacheFile(configDir);
600
+ if (!existsSync(path)) return;
601
+ let parsed;
602
+ try {
603
+ parsed = JSON.parse(readFileSync(path, "utf-8"));
604
+ } catch {
605
+ return;
606
+ }
607
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
608
+ for (const [key, value] of Object.entries(parsed)) {
609
+ if (typeof value === "string" && value.length > 0) target.set(key, value);
610
+ }
611
+ }
612
+ function saveChannelHashCache(source, configDir) {
613
+ const path = getChannelHashCacheFile(configDir);
614
+ const obj = {};
615
+ for (const [key, value] of source) obj[key] = value;
616
+ try {
617
+ writeFileSync(path, JSON.stringify(obj, null, 2));
618
+ } catch {
619
+ }
620
+ }
621
+
591
622
  // src/lib/channel-sweep.ts
592
623
  import { execFileSync } from "child_process";
593
624
  var CHANNEL_BASENAMES = [
@@ -838,6 +869,108 @@ function killAgentChannelProcesses(codeName, opts) {
838
869
  return pids;
839
870
  }
840
871
 
872
+ // src/lib/channel-input-watchdog.ts
873
+ var STUCK_THRESHOLD_MS = 5e3;
874
+ var ATTACHED_STUCK_THRESHOLD_MS = 15e3;
875
+ var INPUT_BOX_DIVIDER = /^[─━]{10,}/;
876
+ var PROMPT_PREFIX = "\u276F ";
877
+ function decide(pane, prev, now, config2 = {}) {
878
+ const threshold = config2.stuckThresholdMs ?? STUCK_THRESHOLD_MS;
879
+ const inputText = extractInputBoxText(pane);
880
+ if (!inputText) {
881
+ return { fire: false, next: void 0 };
882
+ }
883
+ if (isActivelyProcessing(pane)) {
884
+ return { fire: false, next: prev };
885
+ }
886
+ const hash = simpleHash(inputText);
887
+ if (!prev || prev.lastInputHash !== hash) {
888
+ return {
889
+ fire: false,
890
+ next: { lastInputHash: hash, firstSeenAt: now, resolved: false }
891
+ };
892
+ }
893
+ if (prev.resolved) return { fire: false, next: prev };
894
+ if (now - prev.firstSeenAt < threshold) return { fire: false, next: prev };
895
+ return {
896
+ fire: true,
897
+ next: { ...prev, resolved: true }
898
+ };
899
+ }
900
+ function extractInputBoxText(pane) {
901
+ const lines = pane.split("\n");
902
+ for (let i = 1; i < lines.length; i++) {
903
+ const line = lines[i] ?? "";
904
+ if (!line.startsWith(PROMPT_PREFIX)) continue;
905
+ let j = i - 1;
906
+ while (j >= 0 && (lines[j] ?? "").trim() === "") j--;
907
+ if (j < 0) continue;
908
+ if (!INPUT_BOX_DIVIDER.test((lines[j] ?? "").trim())) continue;
909
+ const text = line.slice(PROMPT_PREFIX.length).trim();
910
+ return text.length > 0 ? text : null;
911
+ }
912
+ return null;
913
+ }
914
+ function isActivelyProcessing(pane) {
915
+ const lines = pane.split("\n");
916
+ for (let i = lines.length - 1; i >= 0; i--) {
917
+ const line = (lines[i] ?? "").trim();
918
+ if (!line.startsWith("\u273B")) continue;
919
+ if (/\bfor\s+\d+s\s*$/.test(line)) return false;
920
+ if (/\b\w+ing[…\.]{0,3}\s*$/i.test(line)) return true;
921
+ return false;
922
+ }
923
+ return false;
924
+ }
925
+ function simpleHash(s) {
926
+ let h = 0;
927
+ for (let i = 0; i < s.length; i++) {
928
+ h = (h << 5) - h + s.charCodeAt(i) | 0;
929
+ }
930
+ return h.toString(16);
931
+ }
932
+ function checkChannelInputs(codeNames, io, config2 = {}, states = sharedStates) {
933
+ const live = new Set(codeNames);
934
+ for (const codeName of codeNames) {
935
+ try {
936
+ checkOne(codeName, io, config2, states);
937
+ } catch (err) {
938
+ io.log(`[channel-input-watchdog] '${codeName}': ${err.message}`);
939
+ }
940
+ }
941
+ for (const key of [...states.keys()]) {
942
+ if (!live.has(key)) states.delete(key);
943
+ }
944
+ }
945
+ function checkOne(codeName, io, config2, states) {
946
+ const pane = io.capturePane(codeName);
947
+ if (!pane) {
948
+ states.delete(codeName);
949
+ return;
950
+ }
951
+ const attached = io.isClientAttached(codeName);
952
+ const effectiveConfig = attached ? {
953
+ ...config2,
954
+ stuckThresholdMs: config2.attachedStuckThresholdMs ?? ATTACHED_STUCK_THRESHOLD_MS
955
+ } : config2;
956
+ const prev = states.get(codeName);
957
+ const { fire, next } = decide(pane, prev, io.now(), effectiveConfig);
958
+ if (next === void 0) {
959
+ states.delete(codeName);
960
+ } else {
961
+ states.set(codeName, next);
962
+ }
963
+ if (fire) {
964
+ const text = extractInputBoxText(pane) ?? "";
965
+ const hash = next?.lastInputHash ?? simpleHash(text);
966
+ io.log(
967
+ `[channel-input-watchdog] '${codeName}': stuck channel input \u2014 firing Enter (input_hash=${hash}, len=${text.length})`
968
+ );
969
+ io.sendEnter(codeName);
970
+ }
971
+ }
972
+ var sharedStates = /* @__PURE__ */ new Map();
973
+
841
974
  // src/lib/delivery-hint.ts
842
975
  var DEFAULT_PROBABILITY = 0.1;
843
976
  function envSuffixFor(codeName) {
@@ -940,24 +1073,24 @@ function withScheduleLinkFooter(opts) {
940
1073
  }
941
1074
 
942
1075
  // src/lib/restart-flags.ts
943
- import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
1076
+ import { existsSync as existsSync2, mkdirSync, readdirSync, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
944
1077
  import { homedir as homedir2 } from "os";
945
- import { join as join2 } from "path";
1078
+ import { join as join3 } from "path";
946
1079
  import { randomUUID } from "crypto";
947
1080
  function restartFlagsDir() {
948
- return join2(homedir2(), ".augmented", "restart-flags");
1081
+ return join3(homedir2(), ".augmented", "restart-flags");
949
1082
  }
950
1083
  function flagPath(codeName) {
951
- return join2(restartFlagsDir(), `${codeName}.flag`);
1084
+ return join3(restartFlagsDir(), `${codeName}.flag`);
952
1085
  }
953
1086
  function readRestartFlags() {
954
1087
  const dir = restartFlagsDir();
955
- if (!existsSync(dir)) return [];
1088
+ if (!existsSync2(dir)) return [];
956
1089
  const out = [];
957
1090
  for (const entry of readdirSync(dir)) {
958
1091
  if (!entry.endsWith(".flag")) continue;
959
1092
  try {
960
- const raw = readFileSync(join2(dir, entry), "utf8");
1093
+ const raw = readFileSync2(join3(dir, entry), "utf8");
961
1094
  const parsed = JSON.parse(raw);
962
1095
  if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
963
1096
  parsed.codeName = entry.replace(/\.flag$/, "");
@@ -975,7 +1108,7 @@ function readRestartFlags() {
975
1108
  }
976
1109
  function deleteRestartFlag(codeName) {
977
1110
  const path = flagPath(codeName);
978
- if (existsSync(path)) {
1111
+ if (existsSync2(path)) {
979
1112
  rmSync(path, { force: true });
980
1113
  }
981
1114
  }
@@ -1392,8 +1525,8 @@ function stopRealtimeChat() {
1392
1525
  var GATEWAY_PORT_BASE = 18800;
1393
1526
  var GATEWAY_PORT_STEP = 10;
1394
1527
  var GATEWAY_PORT_MAX = 18899;
1395
- var AUGMENTED_DIR = join3(process.env["HOME"] ?? "/tmp", ".augmented");
1396
- var GATEWAY_PORTS_FILE = join3(AUGMENTED_DIR, "gateway-ports.json");
1528
+ var AUGMENTED_DIR = join4(process.env["HOME"] ?? "/tmp", ".augmented");
1529
+ var GATEWAY_PORTS_FILE = join4(AUGMENTED_DIR, "gateway-ports.json");
1397
1530
  var CHANNEL_SWEEP_INTERVAL_MS = (() => {
1398
1531
  const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
1399
1532
  if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
@@ -1471,9 +1604,14 @@ function clearAgentCaches(agentId, codeName) {
1471
1604
  memoryFileHashes.delete(agentId);
1472
1605
  lastDownloadHash.delete(agentId);
1473
1606
  lastLocalFileHash.delete(agentId);
1607
+ let channelCacheMutated = false;
1474
1608
  for (const key of knownChannelConfigHashes.keys()) {
1475
- if (key.startsWith(`${agentId}:`)) knownChannelConfigHashes.delete(key);
1609
+ if (key.startsWith(`${agentId}:`)) {
1610
+ knownChannelConfigHashes.delete(key);
1611
+ channelCacheMutated = true;
1612
+ }
1476
1613
  }
1614
+ if (channelCacheMutated) saveChannelHashCache2();
1477
1615
  for (const key of knownSkillHashes.keys()) {
1478
1616
  if (key.startsWith(`${agentId}:`)) knownSkillHashes.delete(key);
1479
1617
  }
@@ -1484,7 +1622,7 @@ function clearAgentCaches(agentId, codeName) {
1484
1622
  var cachedFrameworkVersion = null;
1485
1623
  var lastVersionCheckAt = 0;
1486
1624
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
1487
- var agtCliVersion = true ? "0.19.2" : "dev";
1625
+ var agtCliVersion = true ? "0.19.4" : "dev";
1488
1626
  function resolveBrewPath(execFileSync2) {
1489
1627
  try {
1490
1628
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -1497,7 +1635,7 @@ function resolveBrewPath(execFileSync2) {
1497
1635
  "/usr/local/bin/brew"
1498
1636
  ];
1499
1637
  for (const path of fallbacks) {
1500
- if (existsSync2(path)) return path;
1638
+ if (existsSync3(path)) return path;
1501
1639
  }
1502
1640
  return null;
1503
1641
  }
@@ -1658,7 +1796,7 @@ async function ensureFrameworkBinary(frameworkId) {
1658
1796
  }
1659
1797
  return runAsync(brewPath, args, opts);
1660
1798
  };
1661
- let claudeExists = existsSync2("/home/linuxbrew/.linuxbrew/bin/claude");
1799
+ let claudeExists = existsSync3("/home/linuxbrew/.linuxbrew/bin/claude");
1662
1800
  if (!claudeExists) {
1663
1801
  try {
1664
1802
  execFileSync2("which", ["claude"], { timeout: 5e3 });
@@ -1682,7 +1820,7 @@ async function ensureFrameworkBinary(frameworkId) {
1682
1820
  if (!process.env.PATH?.split(":").includes(brewBinDir)) {
1683
1821
  process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
1684
1822
  }
1685
- if (existsSync2("/home/linuxbrew/.linuxbrew/bin/claude")) {
1823
+ if (existsSync3("/home/linuxbrew/.linuxbrew/bin/claude")) {
1686
1824
  log("Claude Code installed successfully");
1687
1825
  } else {
1688
1826
  log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
@@ -1729,7 +1867,7 @@ async function checkAndUpdateCli() {
1729
1867
  const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
1730
1868
  if (!isBrewFormula && !isNpmGlobal) return;
1731
1869
  const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
1732
- const markerPath = join3(homedir3(), ".augmented", ".last-update-check");
1870
+ const markerPath = join4(homedir3(), ".augmented", ".last-update-check");
1733
1871
  try {
1734
1872
  const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
1735
1873
  if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
@@ -1891,10 +2029,10 @@ async function applyClaudeAuthToEnv(childEnv, label) {
1891
2029
  throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
1892
2030
  }
1893
2031
  childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
1894
- const claudeDir = join3(homedir3(), ".claude");
2032
+ const claudeDir = join4(homedir3(), ".claude");
1895
2033
  for (const filename of [".credentials.json", "credentials.json"]) {
1896
- const p = join3(claudeDir, filename);
1897
- if (existsSync2(p)) {
2034
+ const p = join4(claudeDir, filename);
2035
+ if (existsSync3(p)) {
1898
2036
  try {
1899
2037
  rmSync2(p, { force: true });
1900
2038
  log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
@@ -1908,14 +2046,14 @@ async function applyClaudeAuthToEnv(childEnv, label) {
1908
2046
  }
1909
2047
  function loadGatewayPorts() {
1910
2048
  try {
1911
- return JSON.parse(readFileSync2(GATEWAY_PORTS_FILE, "utf-8"));
2049
+ return JSON.parse(readFileSync3(GATEWAY_PORTS_FILE, "utf-8"));
1912
2050
  } catch {
1913
2051
  return {};
1914
2052
  }
1915
2053
  }
1916
2054
  function saveGatewayPorts(ports) {
1917
2055
  mkdirSync2(AUGMENTED_DIR, { recursive: true });
1918
- writeFileSync2(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
2056
+ writeFileSync3(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
1919
2057
  }
1920
2058
  function allocatePort(codeName) {
1921
2059
  const ports = loadGatewayPorts();
@@ -1938,12 +2076,21 @@ function freePort(codeName) {
1938
2076
  }
1939
2077
  }
1940
2078
  function getStateFile() {
1941
- return join3(config?.configDir ?? join3(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
2079
+ return join4(config?.configDir ?? join4(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
2080
+ }
2081
+ function channelHashCacheDir() {
2082
+ return config?.configDir ?? join4(process.env["HOME"] ?? "/tmp", ".augmented");
2083
+ }
2084
+ function loadChannelHashCache2() {
2085
+ loadChannelHashCache(knownChannelConfigHashes, channelHashCacheDir());
2086
+ }
2087
+ function saveChannelHashCache2() {
2088
+ saveChannelHashCache(knownChannelConfigHashes, channelHashCacheDir());
1942
2089
  }
1943
2090
  function send(msg) {
1944
2091
  if (msg.type === "state-update") {
1945
2092
  try {
1946
- writeFileSync2(getStateFile(), JSON.stringify(msg.state, null, 2));
2093
+ writeFileSync3(getStateFile(), JSON.stringify(msg.state, null, 2));
1947
2094
  } catch {
1948
2095
  }
1949
2096
  }
@@ -1974,9 +2121,9 @@ function log(msg) {
1974
2121
  `;
1975
2122
  if (!managerLogPath) {
1976
2123
  try {
1977
- managerLogPath = join3(homedir3(), ".augmented", "manager.log");
2124
+ managerLogPath = join4(homedir3(), ".augmented", "manager.log");
1978
2125
  mkdirSync2(dirname(managerLogPath), { recursive: true });
1979
- if (existsSync2(managerLogPath)) {
2126
+ if (existsSync3(managerLogPath)) {
1980
2127
  chmodSync(managerLogPath, 384);
1981
2128
  }
1982
2129
  } catch {
@@ -2004,7 +2151,7 @@ function sha256(content) {
2004
2151
  }
2005
2152
  function hashFile(filePath) {
2006
2153
  try {
2007
- const content = readFileSync2(filePath, "utf-8");
2154
+ const content = readFileSync3(filePath, "utf-8");
2008
2155
  return sha256(content);
2009
2156
  } catch {
2010
2157
  return null;
@@ -2028,13 +2175,13 @@ function parseSkillFrontmatter(content) {
2028
2175
  return out;
2029
2176
  }
2030
2177
  async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
2031
- const { readdirSync: readdirSync3, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync3 } = await import("fs");
2032
- const skillsDir = join3(configDir, codeName, "project", ".claude", "skills");
2033
- const claudeMdPath = join3(configDir, codeName, "project", "CLAUDE.md");
2178
+ const { readdirSync: readdirSync3, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync4 } = await import("fs");
2179
+ const skillsDir = join4(configDir, codeName, "project", ".claude", "skills");
2180
+ const claudeMdPath = join4(configDir, codeName, "project", "CLAUDE.md");
2034
2181
  if (!ex(skillsDir) || !ex(claudeMdPath)) return;
2035
2182
  const entries = [];
2036
2183
  for (const dir of readdirSync3(skillsDir).sort()) {
2037
- const skillFile = join3(skillsDir, dir, "SKILL.md");
2184
+ const skillFile = join4(skillsDir, dir, "SKILL.md");
2038
2185
  if (!ex(skillFile)) continue;
2039
2186
  try {
2040
2187
  const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
@@ -2076,16 +2223,16 @@ ${SKILLS_INDEX_END}`;
2076
2223
  next = current.trimEnd() + "\n\n" + section + "\n";
2077
2224
  }
2078
2225
  if (next !== current) {
2079
- writeFileSync3(claudeMdPath, next, "utf-8");
2226
+ writeFileSync4(claudeMdPath, next, "utf-8");
2080
2227
  log2(`Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)`);
2081
2228
  }
2082
2229
  }
2083
2230
  async function migrateToProfiles() {
2084
2231
  const homeDir = process.env["HOME"] ?? "/tmp";
2085
- const sharedConfigPath = join3(homeDir, ".openclaw", "openclaw.json");
2232
+ const sharedConfigPath = join4(homeDir, ".openclaw", "openclaw.json");
2086
2233
  let sharedConfig;
2087
2234
  try {
2088
- sharedConfig = JSON.parse(readFileSync2(sharedConfigPath, "utf-8"));
2235
+ sharedConfig = JSON.parse(readFileSync3(sharedConfigPath, "utf-8"));
2089
2236
  } catch {
2090
2237
  return;
2091
2238
  }
@@ -2098,19 +2245,19 @@ async function migrateToProfiles() {
2098
2245
  const codeName = agentEntry["id"];
2099
2246
  if (!codeName) continue;
2100
2247
  if (codeName === "main") continue;
2101
- const profileDir = join3(homeDir, `.openclaw-${codeName}`);
2102
- if (existsSync2(join3(profileDir, "openclaw.json"))) continue;
2248
+ const profileDir = join4(homeDir, `.openclaw-${codeName}`);
2249
+ if (existsSync3(join4(profileDir, "openclaw.json"))) continue;
2103
2250
  log(`Migrating agent '${codeName}' to per-agent profile`);
2104
2251
  if (adapter.seedProfileConfig) {
2105
2252
  adapter.seedProfileConfig(codeName);
2106
2253
  }
2107
- const sharedAuthDir = join3(homeDir, ".openclaw", "agents", codeName, "agent");
2108
- const profileAuthDir = join3(profileDir, "agents", codeName, "agent");
2109
- const authFile = join3(sharedAuthDir, "auth-profiles.json");
2110
- if (existsSync2(authFile)) {
2254
+ const sharedAuthDir = join4(homeDir, ".openclaw", "agents", codeName, "agent");
2255
+ const profileAuthDir = join4(profileDir, "agents", codeName, "agent");
2256
+ const authFile = join4(sharedAuthDir, "auth-profiles.json");
2257
+ if (existsSync3(authFile)) {
2111
2258
  mkdirSync2(profileAuthDir, { recursive: true });
2112
- const authContent = readFileSync2(authFile, "utf-8");
2113
- writeFileSync2(join3(profileAuthDir, "auth-profiles.json"), authContent);
2259
+ const authContent = readFileSync3(authFile, "utf-8");
2260
+ writeFileSync3(join4(profileAuthDir, "auth-profiles.json"), authContent);
2114
2261
  }
2115
2262
  allocatePort(codeName);
2116
2263
  migrated++;
@@ -2148,7 +2295,7 @@ function readGatewayToken(codeName) {
2148
2295
  }
2149
2296
  const homeDir = process.env["HOME"] ?? "/tmp";
2150
2297
  try {
2151
- const cfg = JSON.parse(readFileSync2(join3(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
2298
+ const cfg = JSON.parse(readFileSync3(join4(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
2152
2299
  return cfg?.gateway?.auth?.token;
2153
2300
  } catch {
2154
2301
  return void 0;
@@ -2157,10 +2304,10 @@ function readGatewayToken(codeName) {
2157
2304
  var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
2158
2305
  function isGatewayHung(codeName) {
2159
2306
  const homeDir = process.env["HOME"] ?? "/tmp";
2160
- const jobsPath = join3(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
2161
- if (!existsSync2(jobsPath)) return false;
2307
+ const jobsPath = join4(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
2308
+ if (!existsSync3(jobsPath)) return false;
2162
2309
  try {
2163
- const data = JSON.parse(readFileSync2(jobsPath, "utf-8"));
2310
+ const data = JSON.parse(readFileSync3(jobsPath, "utf-8"));
2164
2311
  const jobs = data.jobs ?? data;
2165
2312
  if (!Array.isArray(jobs)) return false;
2166
2313
  const now = Date.now();
@@ -2193,19 +2340,19 @@ async function ensureGatewayRunning(codeName, adapter) {
2193
2340
  }
2194
2341
  await new Promise((r) => setTimeout(r, 2e3));
2195
2342
  const homeDir = process.env["HOME"] ?? "/tmp";
2196
- const cronJobsPath = join3(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
2343
+ const cronJobsPath = join4(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
2197
2344
  clearStaleCronRunState(cronJobsPath);
2198
2345
  } else {
2199
2346
  if (status.port) {
2200
2347
  try {
2201
2348
  const homeDir = process.env["HOME"] ?? "/tmp";
2202
- const configPath = join3(homeDir, `.openclaw-${codeName}`, "openclaw.json");
2203
- if (existsSync2(configPath)) {
2204
- const cfg = JSON.parse(readFileSync2(configPath, "utf-8"));
2349
+ const configPath = join4(homeDir, `.openclaw-${codeName}`, "openclaw.json");
2350
+ if (existsSync3(configPath)) {
2351
+ const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
2205
2352
  if (cfg.gateway?.port !== status.port) {
2206
2353
  if (!cfg.gateway) cfg.gateway = {};
2207
2354
  cfg.gateway.port = status.port;
2208
- writeFileSync2(configPath, JSON.stringify(cfg, null, 2));
2355
+ writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
2209
2356
  }
2210
2357
  }
2211
2358
  } catch {
@@ -2225,12 +2372,12 @@ async function ensureGatewayRunning(codeName, adapter) {
2225
2372
  gatewaysStartedThisCycle.add(codeName);
2226
2373
  try {
2227
2374
  const homeDir = process.env["HOME"] ?? "/tmp";
2228
- const configPath = join3(homeDir, `.openclaw-${codeName}`, "openclaw.json");
2229
- if (existsSync2(configPath)) {
2230
- const cfg = JSON.parse(readFileSync2(configPath, "utf-8"));
2375
+ const configPath = join4(homeDir, `.openclaw-${codeName}`, "openclaw.json");
2376
+ if (existsSync3(configPath)) {
2377
+ const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
2231
2378
  if (!cfg.gateway) cfg.gateway = {};
2232
2379
  cfg.gateway.port = port;
2233
- writeFileSync2(configPath, JSON.stringify(cfg, null, 2));
2380
+ writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
2234
2381
  }
2235
2382
  } catch {
2236
2383
  }
@@ -2486,7 +2633,7 @@ async function pollCycle() {
2486
2633
  }
2487
2634
  killAgentChannelProcesses(prev.codeName, { log });
2488
2635
  freePort(prev.codeName);
2489
- const agentDir = join3(adapter.getAgentDir(prev.codeName), "provision");
2636
+ const agentDir = join4(adapter.getAgentDir(prev.codeName), "provision");
2490
2637
  await cleanupAgentFiles(prev.codeName, agentDir);
2491
2638
  clearAgentCaches(prev.agentId, prev.codeName);
2492
2639
  }
@@ -2511,6 +2658,43 @@ async function pollCycle() {
2511
2658
  log(`[channel-sweep] sweep error: ${err.message}`);
2512
2659
  });
2513
2660
  }
2661
+ {
2662
+ const claudeCodeAgents = agentStates.filter((a) => a.status === "active").filter((a) => (agentFrameworkCache.get(a.codeName) ?? "openclaw") === "claude-code").map((a) => a.codeName);
2663
+ checkChannelInputs(claudeCodeAgents, {
2664
+ capturePane: (codeName) => {
2665
+ try {
2666
+ return syncExecFile("tmux", ["capture-pane", "-t", `agt-${codeName}`, "-p"], {
2667
+ stdio: ["ignore", "pipe", "ignore"],
2668
+ timeout: 2e3
2669
+ }).toString();
2670
+ } catch {
2671
+ return null;
2672
+ }
2673
+ },
2674
+ isClientAttached: (codeName) => {
2675
+ try {
2676
+ const out = syncExecFile("tmux", ["list-clients", "-t", `agt-${codeName}`], {
2677
+ stdio: ["ignore", "pipe", "ignore"],
2678
+ timeout: 2e3
2679
+ }).toString();
2680
+ return out.trim().length > 0;
2681
+ } catch {
2682
+ return true;
2683
+ }
2684
+ },
2685
+ sendEnter: (codeName) => {
2686
+ try {
2687
+ syncExecFile("tmux", ["send-keys", "-t", `agt-${codeName}`, "Enter"], {
2688
+ stdio: "ignore",
2689
+ timeout: 2e3
2690
+ });
2691
+ } catch {
2692
+ }
2693
+ },
2694
+ log,
2695
+ now: () => Date.now()
2696
+ });
2697
+ }
2514
2698
  const lastHealthCheck = lastHarvestAt.get("__cron_health__") ?? 0;
2515
2699
  if (Date.now() - lastHealthCheck >= HARVEST_INTERVAL_MS) {
2516
2700
  lastHarvestAt.set("__cron_health__", Date.now());
@@ -2574,7 +2758,7 @@ async function processAgent(agent, agentStates) {
2574
2758
  }
2575
2759
  const now = (/* @__PURE__ */ new Date()).toISOString();
2576
2760
  const adapter = resolveAgentFramework(agent.code_name);
2577
- let agentDir = join3(adapter.getAgentDir(agent.code_name), "provision");
2761
+ let agentDir = join4(adapter.getAgentDir(agent.code_name), "provision");
2578
2762
  if (agent.status === "draft" || agent.status === "paused") {
2579
2763
  log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
2580
2764
  await stopGatewayIfRunning(agent.code_name, adapter);
@@ -2639,9 +2823,14 @@ async function processAgent(agent, agentStates) {
2639
2823
  if (previousStatus && previousStatus !== agent.status) {
2640
2824
  log(`Agent '${agent.code_name}' status changed: ${previousStatus} \u2192 ${agent.status}`);
2641
2825
  knownVersions.delete(agent.agent_id);
2826
+ let channelCacheMutated = false;
2642
2827
  for (const key of knownChannelConfigHashes.keys()) {
2643
- if (key.startsWith(`${agent.agent_id}:`)) knownChannelConfigHashes.delete(key);
2828
+ if (key.startsWith(`${agent.agent_id}:`)) {
2829
+ knownChannelConfigHashes.delete(key);
2830
+ channelCacheMutated = true;
2831
+ }
2644
2832
  }
2833
+ if (channelCacheMutated) saveChannelHashCache2();
2645
2834
  }
2646
2835
  knownStatuses.set(agent.agent_id, agent.status);
2647
2836
  let refreshData;
@@ -2699,7 +2888,7 @@ async function processAgent(agent, agentStates) {
2699
2888
  const frameworkId = refreshData.agent.framework ?? "openclaw";
2700
2889
  agentFrameworkCache.set(agent.code_name, frameworkId);
2701
2890
  const frameworkAdapter = getFramework(frameworkId);
2702
- agentDir = join3(frameworkAdapter.getAgentDir(agent.code_name), "provision");
2891
+ agentDir = join4(frameworkAdapter.getAgentDir(agent.code_name), "provision");
2703
2892
  cacheAgentDeliveryMetadata(agent.code_name, refreshData);
2704
2893
  if (frameworkAdapter.seedProfileConfig) {
2705
2894
  frameworkAdapter.seedProfileConfig(agent.code_name);
@@ -2729,7 +2918,7 @@ async function processAgent(agent, agentStates) {
2729
2918
  const changedFiles = [];
2730
2919
  mkdirSync2(agentDir, { recursive: true });
2731
2920
  for (const artifact of artifacts) {
2732
- const filePath = join3(agentDir, artifact.relativePath);
2921
+ const filePath = join4(agentDir, artifact.relativePath);
2733
2922
  let existingHash;
2734
2923
  let newHash;
2735
2924
  let writeContent = artifact.content;
@@ -2741,8 +2930,8 @@ async function processAgent(agent, agentStates) {
2741
2930
  };
2742
2931
  newHash = sha256(stripDynamicSections(artifact.content));
2743
2932
  try {
2744
- const projectClaudeMd = join3(config.configDir, agent.code_name, "project", "CLAUDE.md");
2745
- const existing = readFileSync2(projectClaudeMd, "utf-8");
2933
+ const projectClaudeMd = join4(config.configDir, agent.code_name, "project", "CLAUDE.md");
2934
+ const existing = readFileSync3(projectClaudeMd, "utf-8");
2746
2935
  existingHash = sha256(stripDynamicSections(existing));
2747
2936
  } catch {
2748
2937
  existingHash = null;
@@ -2760,7 +2949,7 @@ async function processAgent(agent, agentStates) {
2760
2949
  const generatorKeys = Object.keys(generatorServers);
2761
2950
  let existingRaw = "";
2762
2951
  try {
2763
- existingRaw = readFileSync2(filePath, "utf-8");
2952
+ existingRaw = readFileSync3(filePath, "utf-8");
2764
2953
  } catch {
2765
2954
  }
2766
2955
  const existingServers = parseMcp(existingRaw);
@@ -2782,22 +2971,22 @@ async function processAgent(agent, agentStates) {
2782
2971
  }
2783
2972
  }
2784
2973
  if (changedFiles.length > 0) {
2785
- const isFirst = !existsSync2(join3(agentDir, "CHARTER.md"));
2974
+ const isFirst = !existsSync3(join4(agentDir, "CHARTER.md"));
2786
2975
  const verb = isFirst ? "Provisioning" : "Updating";
2787
2976
  const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
2788
2977
  log(`${verb} '${agent.code_name}': ${fileNames}`);
2789
2978
  for (const file of changedFiles) {
2790
- const filePath = join3(agentDir, file.relativePath);
2979
+ const filePath = join4(agentDir, file.relativePath);
2791
2980
  mkdirSync2(dirname(filePath), { recursive: true });
2792
- writeFileSync2(filePath, file.content);
2981
+ writeFileSync3(filePath, file.content);
2793
2982
  }
2794
2983
  try {
2795
- const provSkillsDir = join3(agentDir, ".claude", "skills");
2796
- if (existsSync2(provSkillsDir)) {
2984
+ const provSkillsDir = join4(agentDir, ".claude", "skills");
2985
+ if (existsSync3(provSkillsDir)) {
2797
2986
  for (const folder of readdirSync2(provSkillsDir)) {
2798
2987
  if (folder.startsWith("knowledge-")) {
2799
2988
  try {
2800
- rmSync2(join3(provSkillsDir, folder), { recursive: true });
2989
+ rmSync2(join4(provSkillsDir, folder), { recursive: true });
2801
2990
  } catch {
2802
2991
  }
2803
2992
  }
@@ -2810,7 +2999,7 @@ async function processAgent(agent, agentStates) {
2810
2999
  const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
2811
3000
  const hashes = /* @__PURE__ */ new Map();
2812
3001
  for (const file of trackedFiles2) {
2813
- const h = hashFile(join3(agentDir, file));
3002
+ const h = hashFile(join4(agentDir, file));
2814
3003
  if (h) hashes.set(file, h);
2815
3004
  }
2816
3005
  writtenHashes.set(agent.agent_id, hashes);
@@ -2854,10 +3043,10 @@ async function processAgent(agent, agentStates) {
2854
3043
  }
2855
3044
  let lastDriftCheckAt = now;
2856
3045
  const written = writtenHashes.get(agent.agent_id);
2857
- if (written && existsSync2(agentDir)) {
3046
+ if (written && existsSync3(agentDir)) {
2858
3047
  const driftedFiles = [];
2859
3048
  for (const [file, expectedHash] of written) {
2860
- const localHash = hashFile(join3(agentDir, file));
3049
+ const localHash = hashFile(join4(agentDir, file));
2861
3050
  if (localHash && localHash !== expectedHash) {
2862
3051
  driftedFiles.push(file);
2863
3052
  }
@@ -2868,7 +3057,7 @@ async function processAgent(agent, agentStates) {
2868
3057
  try {
2869
3058
  const localHashes = {};
2870
3059
  for (const file of driftedFiles) {
2871
- localHashes[file] = hashFile(join3(agentDir, file));
3060
+ localHashes[file] = hashFile(join4(agentDir, file));
2872
3061
  }
2873
3062
  await api.post("/host/drift", {
2874
3063
  agent_id: agent.agent_id,
@@ -2933,6 +3122,7 @@ async function processAgent(agent, agentStates) {
2933
3122
  const sessionMode2 = refreshData.agent.session_mode;
2934
3123
  frameworkAdapter.writeChannelCredentials(agent.code_name, channelId, entry.config, { sessionMode: sessionMode2, agentId: agent.agent_id });
2935
3124
  knownChannelConfigHashes.set(cacheKey, configHash);
3125
+ saveChannelHashCache2();
2936
3126
  log(`Channel credentials written for '${agent.code_name}/${channelId}' (reason=${reason}, hash=${configHash.slice(0, 8)}${prevHash ? `, prev=${prevHash.slice(0, 8)}` : ""})`);
2937
3127
  } catch (err) {
2938
3128
  log(`Failed to write channel credentials for '${agent.code_name}/${channelId}': ${err.message}`);
@@ -2951,19 +3141,19 @@ async function processAgent(agent, agentStates) {
2951
3141
  if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
2952
3142
  try {
2953
3143
  const agentProvisionDir = agentDir;
2954
- const projectDir = join3(homedir3(), ".augmented", agent.code_name, "project");
3144
+ const projectDir = join4(homedir3(), ".augmented", agent.code_name, "project");
2955
3145
  mkdirSync2(agentProvisionDir, { recursive: true });
2956
3146
  mkdirSync2(projectDir, { recursive: true });
2957
- const provisionMcpPath = join3(agentProvisionDir, ".mcp.json");
2958
- const projectMcpPath = join3(projectDir, ".mcp.json");
3147
+ const provisionMcpPath = join4(agentProvisionDir, ".mcp.json");
3148
+ const projectMcpPath = join4(projectDir, ".mcp.json");
2959
3149
  let mcpConfig = { mcpServers: {} };
2960
3150
  try {
2961
- mcpConfig = JSON.parse(readFileSync2(provisionMcpPath, "utf-8"));
3151
+ mcpConfig = JSON.parse(readFileSync3(provisionMcpPath, "utf-8"));
2962
3152
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
2963
3153
  } catch {
2964
3154
  }
2965
- const localDirectChatChannel = join3(homedir3(), ".augmented", "_mcp", "direct-chat-channel.js");
2966
- if (existsSync2(localDirectChatChannel) && !mcpConfig.mcpServers["direct-chat"]) {
3155
+ const localDirectChatChannel = join4(homedir3(), ".augmented", "_mcp", "direct-chat-channel.js");
3156
+ if (existsSync3(localDirectChatChannel) && !mcpConfig.mcpServers["direct-chat"]) {
2967
3157
  mcpConfig.mcpServers["direct-chat"] = {
2968
3158
  command: "node",
2969
3159
  args: [localDirectChatChannel],
@@ -2974,12 +3164,12 @@ async function processAgent(agent, agentStates) {
2974
3164
  }
2975
3165
  };
2976
3166
  const serialized = JSON.stringify(mcpConfig, null, 2);
2977
- writeFileSync2(provisionMcpPath, serialized);
2978
- writeFileSync2(projectMcpPath, serialized);
3167
+ writeFileSync3(provisionMcpPath, serialized);
3168
+ writeFileSync3(projectMcpPath, serialized);
2979
3169
  log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
2980
3170
  }
2981
- const staleChannelsPath = join3(projectDir, ".mcp-channels.json");
2982
- if (existsSync2(staleChannelsPath)) {
3171
+ const staleChannelsPath = join4(projectDir, ".mcp-channels.json");
3172
+ if (existsSync3(staleChannelsPath)) {
2983
3173
  try {
2984
3174
  rmSync2(staleChannelsPath, { force: true });
2985
3175
  } catch {
@@ -3094,8 +3284,8 @@ async function processAgent(agent, agentStates) {
3094
3284
  const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
3095
3285
  if (mcpPath) {
3096
3286
  try {
3097
- const { readFileSync: readFileSync3 } = await import("fs");
3098
- const mcpConfig = JSON.parse(readFileSync3(mcpPath, "utf-8"));
3287
+ const { readFileSync: readFileSync4 } = await import("fs");
3288
+ const mcpConfig = JSON.parse(readFileSync4(mcpPath, "utf-8"));
3099
3289
  if (mcpConfig.mcpServers) {
3100
3290
  const managedPrefixes = [
3101
3291
  "composio_",
@@ -3187,8 +3377,8 @@ async function processAgent(agent, agentStates) {
3187
3377
  if (agent.status === "active") {
3188
3378
  if (frameworkAdapter.installPlugin) {
3189
3379
  try {
3190
- const pluginPath = join3(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
3191
- if (existsSync2(pluginPath)) {
3380
+ const pluginPath = join4(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
3381
+ if (existsSync3(pluginPath)) {
3192
3382
  frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
3193
3383
  agtHost: requireHost(),
3194
3384
  agtApiKey: getApiKey() ?? void 0,
@@ -3258,16 +3448,16 @@ async function processAgent(agent, agentStates) {
3258
3448
  const frameworkId2 = frameworkAdapter.id;
3259
3449
  const candidateSkillDirs = [
3260
3450
  // Claude Code — framework runtime tree
3261
- join3(homedir4(), ".augmented", agent.code_name, "skills"),
3451
+ join4(homedir4(), ".augmented", agent.code_name, "skills"),
3262
3452
  // Claude Code — project tree
3263
- join3(homedir4(), ".augmented", agent.code_name, "project", ".claude", "skills"),
3453
+ join4(homedir4(), ".augmented", agent.code_name, "project", ".claude", "skills"),
3264
3454
  // OpenClaw — framework runtime tree
3265
- join3(homedir4(), `.openclaw-${agent.code_name}`, "skills"),
3455
+ join4(homedir4(), `.openclaw-${agent.code_name}`, "skills"),
3266
3456
  // Defensive: legacy provision-side path, not currently an
3267
3457
  // install target but cheap to sweep.
3268
- join3(agentDir, ".claude", "skills")
3458
+ join4(agentDir, ".claude", "skills")
3269
3459
  ];
3270
- const existingDirs = candidateSkillDirs.filter((d) => existsSync2(d));
3460
+ const existingDirs = candidateSkillDirs.filter((d) => existsSync3(d));
3271
3461
  const discoveredEntries = /* @__PURE__ */ new Set();
3272
3462
  for (const dir of existingDirs) {
3273
3463
  try {
@@ -3281,8 +3471,8 @@ async function processAgent(agent, agentStates) {
3281
3471
  }
3282
3472
  const removeSkillFolder = (entry, reason) => {
3283
3473
  for (const dir of existingDirs) {
3284
- const p = join3(dir, entry);
3285
- if (existsSync2(p)) {
3474
+ const p = join4(dir, entry);
3475
+ if (existsSync3(p)) {
3286
3476
  rmSync3(p, { recursive: true, force: true });
3287
3477
  }
3288
3478
  }
@@ -3456,10 +3646,10 @@ async function processAgent(agent, agentStates) {
3456
3646
  lastWorkTriggerAt.set(agent.code_name, triggerTs);
3457
3647
  if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
3458
3648
  const homeDir = process.env["HOME"] ?? "/tmp";
3459
- const jobsPath = join3(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
3460
- if (existsSync2(jobsPath)) {
3649
+ const jobsPath = join4(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
3650
+ if (existsSync3(jobsPath)) {
3461
3651
  try {
3462
- const jobsData = JSON.parse(readFileSync2(jobsPath, "utf-8"));
3652
+ const jobsData = JSON.parse(readFileSync3(jobsPath, "utf-8"));
3463
3653
  const kanbanJob = (jobsData.jobs ?? []).find(
3464
3654
  (j) => typeof j.name === "string" && j.name.includes("kanban-work")
3465
3655
  );
@@ -3585,10 +3775,10 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
3585
3775
  }
3586
3776
  }
3587
3777
  const trackedFiles = frameworkAdapter.driftTrackedFiles();
3588
- if (trackedFiles.length > 0 && existsSync2(agentDir)) {
3778
+ if (trackedFiles.length > 0 && existsSync3(agentDir)) {
3589
3779
  const hashes = /* @__PURE__ */ new Map();
3590
3780
  for (const file of trackedFiles) {
3591
- const h = hashFile(join3(agentDir, file));
3781
+ const h = hashFile(join4(agentDir, file));
3592
3782
  if (h) hashes.set(file, h);
3593
3783
  }
3594
3784
  writtenHashes.set(agent.agent_id, hashes);
@@ -3622,19 +3812,19 @@ function cleanupStaleSessions(codeName) {
3622
3812
  lastCleanupAt.set(codeName, Date.now());
3623
3813
  const homeDir = process.env["HOME"] ?? "/tmp";
3624
3814
  for (const agentDir of ["main", codeName]) {
3625
- const sessionsDir = join3(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
3815
+ const sessionsDir = join4(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
3626
3816
  cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
3627
3817
  }
3628
- const cronRunsDir = join3(homeDir, `.openclaw-${codeName}`, "cron", "runs");
3818
+ const cronRunsDir = join4(homeDir, `.openclaw-${codeName}`, "cron", "runs");
3629
3819
  cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
3630
- const cronJobsPath = join3(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
3820
+ const cronJobsPath = join4(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
3631
3821
  clearStaleCronRunState(cronJobsPath);
3632
3822
  }
3633
3823
  function cleanupCronSessions(sessionsDir, keepCount) {
3634
- const indexPath = join3(sessionsDir, "sessions.json");
3635
- if (!existsSync2(indexPath)) return;
3824
+ const indexPath = join4(sessionsDir, "sessions.json");
3825
+ if (!existsSync3(indexPath)) return;
3636
3826
  try {
3637
- const raw = readFileSync2(indexPath, "utf-8");
3827
+ const raw = readFileSync3(indexPath, "utf-8");
3638
3828
  const index = JSON.parse(raw);
3639
3829
  const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
3640
3830
  key: k,
@@ -3647,9 +3837,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
3647
3837
  for (const entry of toDelete) {
3648
3838
  delete index[entry.key];
3649
3839
  if (entry.sessionId) {
3650
- const sessionFile = join3(sessionsDir, `${entry.sessionId}.jsonl`);
3840
+ const sessionFile = join4(sessionsDir, `${entry.sessionId}.jsonl`);
3651
3841
  try {
3652
- if (existsSync2(sessionFile)) {
3842
+ if (existsSync3(sessionFile)) {
3653
3843
  unlinkSync(sessionFile);
3654
3844
  deletedFiles++;
3655
3845
  }
@@ -3669,8 +3859,8 @@ function cleanupCronSessions(sessionsDir, keepCount) {
3669
3859
  delete index[parentKey];
3670
3860
  if (parentSessionId) {
3671
3861
  try {
3672
- const f = join3(sessionsDir, `${parentSessionId}.jsonl`);
3673
- if (existsSync2(f)) {
3862
+ const f = join4(sessionsDir, `${parentSessionId}.jsonl`);
3863
+ if (existsSync3(f)) {
3674
3864
  unlinkSync(f);
3675
3865
  deletedFiles++;
3676
3866
  }
@@ -3679,7 +3869,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
3679
3869
  }
3680
3870
  }
3681
3871
  }
3682
- writeFileSync2(indexPath, JSON.stringify(index));
3872
+ writeFileSync3(indexPath, JSON.stringify(index));
3683
3873
  if (toDelete.length > 0) {
3684
3874
  log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
3685
3875
  }
@@ -3688,9 +3878,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
3688
3878
  }
3689
3879
  var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
3690
3880
  function clearStaleCronRunState(jobsPath) {
3691
- if (!existsSync2(jobsPath)) return;
3881
+ if (!existsSync3(jobsPath)) return;
3692
3882
  try {
3693
- const raw = readFileSync2(jobsPath, "utf-8");
3883
+ const raw = readFileSync3(jobsPath, "utf-8");
3694
3884
  const data = JSON.parse(raw);
3695
3885
  const jobs = data.jobs ?? data;
3696
3886
  if (!Array.isArray(jobs)) return;
@@ -3715,19 +3905,19 @@ function clearStaleCronRunState(jobsPath) {
3715
3905
  }
3716
3906
  }
3717
3907
  if (changed) {
3718
- writeFileSync2(jobsPath, JSON.stringify(data, null, 2));
3908
+ writeFileSync3(jobsPath, JSON.stringify(data, null, 2));
3719
3909
  }
3720
3910
  } catch {
3721
3911
  }
3722
3912
  }
3723
3913
  function cleanupOldFiles(dir, maxAgeDays, ext) {
3724
- if (!existsSync2(dir)) return;
3914
+ if (!existsSync3(dir)) return;
3725
3915
  const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
3726
3916
  let removed = 0;
3727
3917
  try {
3728
3918
  for (const f of readdirSync2(dir)) {
3729
3919
  if (!f.endsWith(ext)) continue;
3730
- const fullPath = join3(dir, f);
3920
+ const fullPath = join4(dir, f);
3731
3921
  try {
3732
3922
  const st = statSync(fullPath);
3733
3923
  if (st.mtimeMs < cutoff) {
@@ -3871,7 +4061,7 @@ async function fetchPriorScheduledRuns(agentId, taskId) {
3871
4061
  }
3872
4062
  async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
3873
4063
  const projectDir = getProjectDir(codeName);
3874
- const mcpConfigPath = join3(projectDir, ".mcp.json");
4064
+ const mcpConfigPath = join4(projectDir, ".mcp.json");
3875
4065
  let runId = null;
3876
4066
  let kanbanItemId = null;
3877
4067
  let taskResult;
@@ -3879,11 +4069,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
3879
4069
  const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
3880
4070
  prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
3881
4071
  try {
3882
- const claudeMdPath = join3(projectDir, "CLAUDE.md");
4072
+ const claudeMdPath = join4(projectDir, "CLAUDE.md");
3883
4073
  const serverNames = [];
3884
- if (existsSync2(mcpConfigPath)) {
4074
+ if (existsSync3(mcpConfigPath)) {
3885
4075
  try {
3886
- const d = JSON.parse(readFileSync2(mcpConfigPath, "utf-8"));
4076
+ const d = JSON.parse(readFileSync3(mcpConfigPath, "utf-8"));
3887
4077
  if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
3888
4078
  } catch {
3889
4079
  }
@@ -3902,14 +4092,14 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
3902
4092
  "--allowedTools",
3903
4093
  allowedTools
3904
4094
  ];
3905
- if (existsSync2(claudeMdPath)) {
4095
+ if (existsSync3(claudeMdPath)) {
3906
4096
  claudeArgs.push("--system-prompt-file", claudeMdPath);
3907
4097
  }
3908
4098
  const childEnv = { ...process.env };
3909
- const envIntPath = join3(projectDir, ".env.integrations");
3910
- if (existsSync2(envIntPath)) {
4099
+ const envIntPath = join4(projectDir, ".env.integrations");
4100
+ if (existsSync3(envIntPath)) {
3911
4101
  try {
3912
- for (const line of readFileSync2(envIntPath, "utf-8").split("\n")) {
4102
+ for (const line of readFileSync3(envIntPath, "utf-8").split("\n")) {
3913
4103
  if (!line || line.startsWith("#") || !line.includes("=")) continue;
3914
4104
  const eqIdx = line.indexOf("=");
3915
4105
  childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
@@ -4091,8 +4281,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
4091
4281
  async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
4092
4282
  const codeName = agent.code_name;
4093
4283
  const projectDir = getProjectDir2(codeName);
4094
- const mcpConfigPath = join3(projectDir, ".mcp.json");
4095
- const claudeMdPath = join3(projectDir, "CLAUDE.md");
4284
+ const mcpConfigPath = join4(projectDir, ".mcp.json");
4285
+ const claudeMdPath = join4(projectDir, "CLAUDE.md");
4096
4286
  const channelConfigs = refreshData.channel_configs;
4097
4287
  const channels = [];
4098
4288
  const devChannels = [];
@@ -4567,11 +4757,11 @@ async function processDirectChatMessage(agent, msg) {
4567
4757
  if (fw === "claude-code") {
4568
4758
  const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-XHJS2MZV.js");
4569
4759
  const projDir = ccProjectDir(agent.codeName);
4570
- const mcpConfigPath = join3(projDir, ".mcp.json");
4760
+ const mcpConfigPath = join4(projDir, ".mcp.json");
4571
4761
  const serverNames = [];
4572
- if (existsSync2(mcpConfigPath)) {
4762
+ if (existsSync3(mcpConfigPath)) {
4573
4763
  try {
4574
- const d = JSON.parse(readFileSync2(mcpConfigPath, "utf-8"));
4764
+ const d = JSON.parse(readFileSync3(mcpConfigPath, "utf-8"));
4575
4765
  if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
4576
4766
  } catch {
4577
4767
  }
@@ -4590,15 +4780,15 @@ async function processDirectChatMessage(agent, msg) {
4590
4780
  "--allowedTools",
4591
4781
  allowedTools
4592
4782
  ];
4593
- const chatClaudeMd = join3(projDir, "CLAUDE.md");
4594
- if (existsSync2(chatClaudeMd)) {
4783
+ const chatClaudeMd = join4(projDir, "CLAUDE.md");
4784
+ if (existsSync3(chatClaudeMd)) {
4595
4785
  chatArgs.push("--system-prompt-file", chatClaudeMd);
4596
4786
  }
4597
- const envIntPath = join3(projDir, ".env.integrations");
4787
+ const envIntPath = join4(projDir, ".env.integrations");
4598
4788
  const childEnv = { ...process.env };
4599
- if (existsSync2(envIntPath)) {
4789
+ if (existsSync3(envIntPath)) {
4600
4790
  try {
4601
- for (const line of readFileSync2(envIntPath, "utf-8").split("\n")) {
4791
+ for (const line of readFileSync3(envIntPath, "utf-8").split("\n")) {
4602
4792
  if (!line || line.startsWith("#") || !line.includes("=")) continue;
4603
4793
  const eqIdx = line.indexOf("=");
4604
4794
  childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
@@ -4879,12 +5069,12 @@ function getBuiltInSkillContent(skillId) {
4879
5069
  if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
4880
5070
  try {
4881
5071
  const candidates = [
4882
- join3(process.cwd(), "skills", skillId, "SKILL.md"),
4883
- join3(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
5072
+ join4(process.cwd(), "skills", skillId, "SKILL.md"),
5073
+ join4(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
4884
5074
  ];
4885
5075
  for (const candidate of candidates) {
4886
- if (existsSync2(candidate)) {
4887
- const content = readFileSync2(candidate, "utf-8");
5076
+ if (existsSync3(candidate)) {
5077
+ const content = readFileSync3(candidate, "utf-8");
4888
5078
  const files = [{ relativePath: "SKILL.md", content }];
4889
5079
  builtInSkillCache.set(skillId, files);
4890
5080
  return files;
@@ -5773,8 +5963,8 @@ function parseMemoryFile(raw, fallbackName) {
5773
5963
  };
5774
5964
  }
5775
5965
  async function syncMemories(agent, configDir, log2) {
5776
- const projectDir = join3(configDir, agent.code_name, "project");
5777
- const memoryDir = join3(projectDir, "memory");
5966
+ const projectDir = join4(configDir, agent.code_name, "project");
5967
+ const memoryDir = join4(projectDir, "memory");
5778
5968
  const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
5779
5969
  if (isFreshSync) {
5780
5970
  log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
@@ -5785,14 +5975,14 @@ async function syncMemories(agent, configDir, log2) {
5785
5975
  }
5786
5976
  pendingFreshMemorySync.delete(agent.agent_id);
5787
5977
  }
5788
- if (existsSync2(memoryDir)) {
5978
+ if (existsSync3(memoryDir)) {
5789
5979
  const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
5790
5980
  const currentHashes = /* @__PURE__ */ new Map();
5791
5981
  const changedMemories = [];
5792
5982
  for (const file of readdirSync2(memoryDir)) {
5793
5983
  if (!file.endsWith(".md")) continue;
5794
5984
  try {
5795
- const raw = readFileSync2(join3(memoryDir, file), "utf-8");
5985
+ const raw = readFileSync3(join4(memoryDir, file), "utf-8");
5796
5986
  const fileHash = createHash("sha256").update(raw).digest("hex").slice(0, 16);
5797
5987
  currentHashes.set(file, fileHash);
5798
5988
  if (prevHashes.get(file) === fileHash) continue;
@@ -5817,7 +6007,7 @@ async function syncMemories(agent, configDir, log2) {
5817
6007
  } catch (err) {
5818
6008
  for (const mem of changedMemories) {
5819
6009
  for (const [file] of currentHashes) {
5820
- const parsed = parseMemoryFile(readFileSync2(join3(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
6010
+ const parsed = parseMemoryFile(readFileSync3(join4(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
5821
6011
  if (parsed?.name === mem.name) currentHashes.delete(file);
5822
6012
  }
5823
6013
  }
@@ -5830,7 +6020,7 @@ async function syncMemories(agent, configDir, log2) {
5830
6020
  }
5831
6021
  }
5832
6022
  async function downloadMemories(agent, memoryDir, log2, { force }) {
5833
- const localFiles = existsSync2(memoryDir) ? readdirSync2(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
6023
+ const localFiles = existsSync3(memoryDir) ? readdirSync2(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
5834
6024
  const localListHash = createHash("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
5835
6025
  const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
5836
6026
  const prevDownload = lastDownloadHash.get(agent.agent_id);
@@ -5852,7 +6042,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
5852
6042
  const mem = dbMemories.memories[i];
5853
6043
  const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
5854
6044
  const slug = rawSlug || `memory-${i}`;
5855
- const filePath = join3(memoryDir, `${slug}.md`);
6045
+ const filePath = join4(memoryDir, `${slug}.md`);
5856
6046
  const desired = `---
5857
6047
  name: ${JSON.stringify(mem.name)}
5858
6048
  type: ${mem.type}
@@ -5861,17 +6051,17 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
5861
6051
 
5862
6052
  ${mem.content}
5863
6053
  `;
5864
- if (existsSync2(filePath)) {
6054
+ if (existsSync3(filePath)) {
5865
6055
  let existing = "";
5866
6056
  try {
5867
- existing = readFileSync2(filePath, "utf-8");
6057
+ existing = readFileSync3(filePath, "utf-8");
5868
6058
  } catch {
5869
6059
  }
5870
6060
  if (existing === desired) continue;
5871
- writeFileSync2(filePath, desired);
6061
+ writeFileSync3(filePath, desired);
5872
6062
  overwritten++;
5873
6063
  } else {
5874
- writeFileSync2(filePath, desired);
6064
+ writeFileSync3(filePath, desired);
5875
6065
  written++;
5876
6066
  }
5877
6067
  }
@@ -5888,7 +6078,7 @@ ${mem.content}
5888
6078
  }
5889
6079
  }
5890
6080
  async function cleanupAgentFiles(codeName, agentDir) {
5891
- if (existsSync2(agentDir)) {
6081
+ if (existsSync3(agentDir)) {
5892
6082
  try {
5893
6083
  rmSync2(agentDir, { recursive: true, force: true });
5894
6084
  log(`Removed provision directory for '${codeName}'`);
@@ -5977,6 +6167,7 @@ function startPolling() {
5977
6167
  if (!config || running) return;
5978
6168
  running = true;
5979
6169
  void startCaffeinate();
6170
+ loadChannelHashCache2();
5980
6171
  log(`Starting poll loop (interval=${config.intervalMs}ms, configDir=${config.configDir})`);
5981
6172
  void killAllAgtTmuxSessions().catch(() => {
5982
6173
  }).then(() => migrateToProfiles()).then(() => {
@@ -6055,7 +6246,7 @@ async function stopPolling(opts = {}) {
6055
6246
  function startManager(opts) {
6056
6247
  config = opts;
6057
6248
  log(
6058
- `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join3(homedir3(), ".augmented", "manager.log")}`
6249
+ `[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join4(homedir3(), ".augmented", "manager.log")}`
6059
6250
  );
6060
6251
  deployMcpAssets();
6061
6252
  void ensureHostFrameworkBinaries();
@@ -6116,14 +6307,14 @@ function restartRunningChannelMcps(basenames) {
6116
6307
  }
6117
6308
  }
6118
6309
  function deployMcpAssets() {
6119
- const targetDir = join3(homedir3(), ".augmented", "_mcp");
6310
+ const targetDir = join4(homedir3(), ".augmented", "_mcp");
6120
6311
  mkdirSync2(targetDir, { recursive: true });
6121
6312
  const moduleDir = dirname(fileURLToPath(import.meta.url));
6122
6313
  let mcpSourceDir = "";
6123
6314
  let dir = moduleDir;
6124
6315
  for (let i = 0; i < 6; i++) {
6125
- const candidate = join3(dir, "mcp");
6126
- if (existsSync2(join3(candidate, "index.js"))) {
6316
+ const candidate = join4(dir, "mcp");
6317
+ if (existsSync3(join4(candidate, "index.js"))) {
6127
6318
  mcpSourceDir = candidate;
6128
6319
  break;
6129
6320
  }
@@ -6138,8 +6329,8 @@ function deployMcpAssets() {
6138
6329
  const changedBasenames = [];
6139
6330
  const fileHash = (p) => {
6140
6331
  try {
6141
- if (!existsSync2(p)) return null;
6142
- return createHash("sha256").update(readFileSync2(p)).digest("hex");
6332
+ if (!existsSync3(p)) return null;
6333
+ return createHash("sha256").update(readFileSync3(p)).digest("hex");
6143
6334
  } catch {
6144
6335
  return null;
6145
6336
  }
@@ -6150,9 +6341,9 @@ function deployMcpAssets() {
6150
6341
  "telegram-channel.js"
6151
6342
  ]);
6152
6343
  for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
6153
- const src = join3(mcpSourceDir, file);
6154
- const dst = join3(targetDir, file);
6155
- if (!existsSync2(src)) continue;
6344
+ const src = join4(mcpSourceDir, file);
6345
+ const dst = join4(targetDir, file);
6346
+ if (!existsSync3(src)) continue;
6156
6347
  const before = fileHash(dst);
6157
6348
  try {
6158
6349
  copyFileSync(src, dst);
@@ -6169,23 +6360,23 @@ function deployMcpAssets() {
6169
6360
  log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
6170
6361
  restartRunningChannelMcps(changedBasenames);
6171
6362
  }
6172
- const localMcpPath = join3(targetDir, "index.js");
6363
+ const localMcpPath = join4(targetDir, "index.js");
6173
6364
  try {
6174
- const agentsDir = join3(homedir3(), ".augmented", "agents");
6175
- if (existsSync2(agentsDir)) {
6365
+ const agentsDir = join4(homedir3(), ".augmented", "agents");
6366
+ if (existsSync3(agentsDir)) {
6176
6367
  for (const entry of readdirSync2(agentsDir, { withFileTypes: true })) {
6177
6368
  if (!entry.isDirectory()) continue;
6178
6369
  for (const subdir of ["provision", "project"]) {
6179
- const mcpJsonPath = join3(agentsDir, entry.name, subdir, ".mcp.json");
6370
+ const mcpJsonPath = join4(agentsDir, entry.name, subdir, ".mcp.json");
6180
6371
  try {
6181
- const raw = readFileSync2(mcpJsonPath, "utf-8");
6372
+ const raw = readFileSync3(mcpJsonPath, "utf-8");
6182
6373
  if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
6183
6374
  const mcpConfig = JSON.parse(raw);
6184
6375
  const augServer = mcpConfig.mcpServers?.["augmented"];
6185
6376
  if (!augServer) continue;
6186
6377
  augServer.command = "node";
6187
6378
  augServer.args = [localMcpPath];
6188
- writeFileSync2(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
6379
+ writeFileSync3(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
6189
6380
  log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
6190
6381
  } catch {
6191
6382
  }