@integrity-labs/agt-cli 0.19.2 → 0.19.3

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