@integrity-labs/agt-cli 0.27.7-test.7 → 0.27.8-test.10
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.
- package/dist/bin/agt.js +146 -37
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-2G35ES7G.js → chunk-2E5OABKX.js} +241 -61
- package/dist/chunk-2E5OABKX.js.map +1 -0
- package/dist/{chunk-YSBGIXJG.js → chunk-6HFXSNNY.js} +31 -18
- package/dist/chunk-6HFXSNNY.js.map +1 -0
- package/dist/{chunk-GN4XPQWJ.js → chunk-JIETOJQV.js} +37 -28
- package/dist/chunk-JIETOJQV.js.map +1 -0
- package/dist/{claude-pair-runtime-ZBQKBBMT.js → claude-pair-runtime-3UHTNKZ4.js} +2 -2
- package/dist/lib/manager-worker.js +536 -140
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +9 -462
- package/dist/mcp/slack-channel.js +691 -638
- package/dist/mcp/telegram-channel.js +257 -513
- package/dist/{persistent-session-ICYFLUAM.js → persistent-session-HSHIZC2Q.js} +3 -3
- package/dist/{responsiveness-probe-WZNQ2762.js → responsiveness-probe-RYKS5TRP.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-2G35ES7G.js.map +0 -1
- package/dist/chunk-GN4XPQWJ.js.map +0 -1
- package/dist/chunk-YSBGIXJG.js.map +0 -1
- /package/dist/{claude-pair-runtime-ZBQKBBMT.js.map → claude-pair-runtime-3UHTNKZ4.js.map} +0 -0
- /package/dist/{persistent-session-ICYFLUAM.js.map → persistent-session-HSHIZC2Q.js.map} +0 -0
- /package/dist/{responsiveness-probe-WZNQ2762.js.map → responsiveness-probe-RYKS5TRP.js.map} +0 -0
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
provisionOrientHook,
|
|
16
16
|
provisionStopHook,
|
|
17
17
|
requireHost
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-2E5OABKX.js";
|
|
19
19
|
import {
|
|
20
20
|
getProjectDir as getProjectDir2,
|
|
21
21
|
getReadyTasks,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
stopAllSessionsAndWait,
|
|
47
47
|
stopPersistentSession,
|
|
48
48
|
takeZombieDetection
|
|
49
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-JIETOJQV.js";
|
|
50
50
|
import {
|
|
51
51
|
KANBAN_CHECK_COMMAND,
|
|
52
52
|
appendDmFooter,
|
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
resolveConnectivityProbe,
|
|
70
70
|
resolveDmTarget,
|
|
71
71
|
wrapScheduledTaskPrompt
|
|
72
|
-
} from "../chunk-
|
|
72
|
+
} from "../chunk-6HFXSNNY.js";
|
|
73
73
|
import {
|
|
74
74
|
parsePsRows,
|
|
75
75
|
reapOrphanChannelMcps
|
|
@@ -77,10 +77,10 @@ import {
|
|
|
77
77
|
|
|
78
78
|
// src/lib/manager-worker.ts
|
|
79
79
|
import { createHash as createHash3 } from "crypto";
|
|
80
|
-
import { readFileSync as
|
|
80
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, appendFileSync, mkdirSync as mkdirSync4, chmodSync, existsSync as existsSync5, rmSync as rmSync2, readdirSync as readdirSync3, statSync as statSync2, unlinkSync, copyFileSync } from "fs";
|
|
81
81
|
import https from "https";
|
|
82
82
|
import { execFileSync as syncExecFile } from "child_process";
|
|
83
|
-
import { join as join6, dirname } from "path";
|
|
83
|
+
import { join as join6, dirname as dirname3 } from "path";
|
|
84
84
|
import { homedir as homedir4 } from "os";
|
|
85
85
|
import { fileURLToPath } from "url";
|
|
86
86
|
|
|
@@ -578,6 +578,37 @@ function decideChannelRestart(input) {
|
|
|
578
578
|
return { restart: true, added, removed };
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
+
// src/lib/sender-policy-restart-decision.ts
|
|
582
|
+
function decideSenderPolicyRestart(input) {
|
|
583
|
+
const {
|
|
584
|
+
previousHash,
|
|
585
|
+
currentHash,
|
|
586
|
+
sessionMode,
|
|
587
|
+
framework,
|
|
588
|
+
sessionHealthy,
|
|
589
|
+
channelSetRestartAlreadyScheduled
|
|
590
|
+
} = input;
|
|
591
|
+
if (previousHash === void 0) {
|
|
592
|
+
return { restart: false, firstPoll: true, changed: false };
|
|
593
|
+
}
|
|
594
|
+
if (previousHash === currentHash) {
|
|
595
|
+
return { restart: false, firstPoll: false, changed: false };
|
|
596
|
+
}
|
|
597
|
+
if (sessionMode !== "persistent") {
|
|
598
|
+
return { restart: false, firstPoll: false, changed: true };
|
|
599
|
+
}
|
|
600
|
+
if (framework !== "claude-code") {
|
|
601
|
+
return { restart: false, firstPoll: false, changed: true };
|
|
602
|
+
}
|
|
603
|
+
if (!sessionHealthy) {
|
|
604
|
+
return { restart: false, firstPoll: false, changed: true };
|
|
605
|
+
}
|
|
606
|
+
if (channelSetRestartAlreadyScheduled) {
|
|
607
|
+
return { restart: false, firstPoll: false, changed: true };
|
|
608
|
+
}
|
|
609
|
+
return { restart: true, firstPoll: false, changed: true };
|
|
610
|
+
}
|
|
611
|
+
|
|
581
612
|
// src/lib/restart-breaker.ts
|
|
582
613
|
function reaperRestartBreakerReason(activeKeys) {
|
|
583
614
|
return activeKeys.length >= 2 ? "mcp-presence-reaper" : void 0;
|
|
@@ -910,6 +941,142 @@ function runCliProbe(binary, args, opts = {}) {
|
|
|
910
941
|
});
|
|
911
942
|
}
|
|
912
943
|
|
|
944
|
+
// src/lib/self-update-coalesce.ts
|
|
945
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
946
|
+
import { dirname } from "path";
|
|
947
|
+
var DEFAULT_SELF_UPDATE_COALESCE_MS = 30 * 60 * 1e3;
|
|
948
|
+
function resolveCoalesceWindowMs(env = process.env) {
|
|
949
|
+
const raw = env.AGT_SELF_UPDATE_COALESCE_MS;
|
|
950
|
+
if (raw == null || raw === "") return DEFAULT_SELF_UPDATE_COALESCE_MS;
|
|
951
|
+
const parsed = Number(raw);
|
|
952
|
+
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_SELF_UPDATE_COALESCE_MS;
|
|
953
|
+
return Math.trunc(parsed);
|
|
954
|
+
}
|
|
955
|
+
function readLastSelfUpdateAppliedMs(markerPath, now = Date.now(), read = (p) => readFileSync(p, "utf-8")) {
|
|
956
|
+
let raw;
|
|
957
|
+
try {
|
|
958
|
+
raw = read(markerPath);
|
|
959
|
+
} catch {
|
|
960
|
+
return null;
|
|
961
|
+
}
|
|
962
|
+
const v = parseInt(raw.trim(), 10);
|
|
963
|
+
if (!Number.isFinite(v) || v <= 0) return null;
|
|
964
|
+
if (v > now) return null;
|
|
965
|
+
return v;
|
|
966
|
+
}
|
|
967
|
+
function stampLastSelfUpdateApplied(markerPath, now = Date.now(), write = (p, v) => {
|
|
968
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
969
|
+
writeFileSync(p, v);
|
|
970
|
+
}) {
|
|
971
|
+
try {
|
|
972
|
+
write(markerPath, String(now));
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
function decideSelfUpdateCoalesce(inputs) {
|
|
977
|
+
const { windowMs, lastAppliedMs, now } = inputs;
|
|
978
|
+
if (windowMs <= 0) return { proceed: true, remainingMs: 0, lastAppliedMs: null };
|
|
979
|
+
if (lastAppliedMs == null) return { proceed: true, remainingMs: 0, lastAppliedMs: null };
|
|
980
|
+
const elapsed = now - lastAppliedMs;
|
|
981
|
+
if (elapsed < 0) return { proceed: true, remainingMs: 0, lastAppliedMs: null };
|
|
982
|
+
if (elapsed >= windowMs) return { proceed: true, remainingMs: 0, lastAppliedMs: null };
|
|
983
|
+
return { proceed: false, remainingMs: windowMs - elapsed, lastAppliedMs };
|
|
984
|
+
}
|
|
985
|
+
function formatCoalesceDeferLogLine(opts) {
|
|
986
|
+
const remainingSec = Math.ceil(opts.remainingMs / 1e3);
|
|
987
|
+
const windowMin = Math.round(opts.windowMs / 6e4);
|
|
988
|
+
return `[self-update] coalescing \u2014 ${opts.installed} \u2192 ${opts.latest} (${opts.channelLabel}) available, deferring for ${remainingSec}s (last restart inside ${windowMin}min window). See ENG-5862. Override with AGT_SELF_UPDATE_COALESCE_MS=0 during incident response.`;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// src/lib/atomic-write.ts
|
|
992
|
+
import { closeSync, fsyncSync, openSync, writeSync, renameSync, mkdirSync as mkdirSync2 } from "fs";
|
|
993
|
+
import { dirname as dirname2 } from "path";
|
|
994
|
+
function atomicWriteFileSync(path, data) {
|
|
995
|
+
const dirPath = dirname2(path);
|
|
996
|
+
const tmpPath = `${path}.tmp.${process.pid}.${Math.random().toString(36).slice(2, 8)}`;
|
|
997
|
+
try {
|
|
998
|
+
mkdirSync2(dirPath, { recursive: true });
|
|
999
|
+
} catch {
|
|
1000
|
+
}
|
|
1001
|
+
const fd = openSync(tmpPath, "w", 420);
|
|
1002
|
+
try {
|
|
1003
|
+
writeSync(fd, data);
|
|
1004
|
+
try {
|
|
1005
|
+
fsyncSync(fd);
|
|
1006
|
+
} catch {
|
|
1007
|
+
}
|
|
1008
|
+
} finally {
|
|
1009
|
+
closeSync(fd);
|
|
1010
|
+
}
|
|
1011
|
+
renameSync(tmpPath, path);
|
|
1012
|
+
try {
|
|
1013
|
+
const dirFd = openSync(dirPath, "r");
|
|
1014
|
+
try {
|
|
1015
|
+
fsyncSync(dirFd);
|
|
1016
|
+
} finally {
|
|
1017
|
+
closeSync(dirFd);
|
|
1018
|
+
}
|
|
1019
|
+
} catch {
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// src/lib/claude-pid-tracker.ts
|
|
1024
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
1025
|
+
function readPidFile(path) {
|
|
1026
|
+
if (!existsSync(path)) return { version: 1, spawns: [] };
|
|
1027
|
+
try {
|
|
1028
|
+
const raw = JSON.parse(readFileSync2(path, "utf-8"));
|
|
1029
|
+
if (raw.version !== 1 || !Array.isArray(raw.spawns)) return { version: 1, spawns: [] };
|
|
1030
|
+
const spawns = raw.spawns.filter(
|
|
1031
|
+
(s) => !!s && typeof s.pid === "number" && Number.isFinite(s.pid) && s.pid > 0
|
|
1032
|
+
);
|
|
1033
|
+
return { version: 1, spawns };
|
|
1034
|
+
} catch {
|
|
1035
|
+
return { version: 1, spawns: [] };
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
function writePidFile(path, file) {
|
|
1039
|
+
atomicWriteFileSync(path, JSON.stringify(file, null, 2));
|
|
1040
|
+
}
|
|
1041
|
+
function addSpawn(path, record) {
|
|
1042
|
+
const file = readPidFile(path);
|
|
1043
|
+
const filtered = file.spawns.filter((s) => s.pid !== record.pid);
|
|
1044
|
+
writePidFile(path, { version: 1, spawns: [...filtered, record] });
|
|
1045
|
+
}
|
|
1046
|
+
function removeSpawn(path, pid) {
|
|
1047
|
+
const file = readPidFile(path);
|
|
1048
|
+
const next = file.spawns.filter((s) => s.pid !== pid);
|
|
1049
|
+
if (next.length !== file.spawns.length) writePidFile(path, { version: 1, spawns: next });
|
|
1050
|
+
}
|
|
1051
|
+
function decideReaperActions(file, isAlive, looksLikeClaude = () => true) {
|
|
1052
|
+
const toKill = [];
|
|
1053
|
+
const alreadyDead = [];
|
|
1054
|
+
const pidReusedSkipped = [];
|
|
1055
|
+
for (const s of file.spawns) {
|
|
1056
|
+
if (!isAlive(s.pid)) {
|
|
1057
|
+
alreadyDead.push(s);
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (!looksLikeClaude(s.pid)) {
|
|
1061
|
+
pidReusedSkipped.push(s);
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
toKill.push(s);
|
|
1065
|
+
}
|
|
1066
|
+
const toResetKanban = toKill.filter(
|
|
1067
|
+
(s) => s.kind === "kanban-work" && typeof s.kanban_card_id === "string" && s.kanban_card_id.length > 0
|
|
1068
|
+
);
|
|
1069
|
+
return { toKill, alreadyDead, pidReusedSkipped, toResetKanban };
|
|
1070
|
+
}
|
|
1071
|
+
function formatDrainShutdownLine(opts) {
|
|
1072
|
+
const killed = opts.killedPids && opts.killedPids.length > 0 ? ` killed_pids=${opts.killedPids.join(",")}` : "";
|
|
1073
|
+
const note = opts.note ? ` note="${opts.note.replace(/"/g, '\\"')}"` : "";
|
|
1074
|
+
return `[drain] step=${opts.step} elapsed_ms=${opts.elapsedMs}${killed}${note}`;
|
|
1075
|
+
}
|
|
1076
|
+
function formatReaperBootLine(opts) {
|
|
1077
|
+
return `[drain] step=boot-reaper total=${opts.totalRecorded} killed=${opts.killed} already_dead=${opts.alreadyDead} pid_reused_skipped=${opts.pidReusedSkipped} kanban_reset=${opts.kanbanReset}`;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
913
1080
|
// src/lib/usage-banner-monitor.ts
|
|
914
1081
|
var MIN_CHECK_INTERVAL_MS = 6e4;
|
|
915
1082
|
var PANE_TAIL_LINES_FOR_BANNER = 200;
|
|
@@ -966,7 +1133,7 @@ async function maybeReportUsageBanner(args) {
|
|
|
966
1133
|
}
|
|
967
1134
|
|
|
968
1135
|
// src/lib/token-usage-monitor.ts
|
|
969
|
-
import { readdirSync, readFileSync, statSync } from "fs";
|
|
1136
|
+
import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
970
1137
|
import { join } from "path";
|
|
971
1138
|
var MIN_CHECK_INTERVAL_MS2 = 6e4;
|
|
972
1139
|
var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
@@ -1012,7 +1179,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1012
1179
|
}
|
|
1013
1180
|
let content;
|
|
1014
1181
|
try {
|
|
1015
|
-
content =
|
|
1182
|
+
content = readFileSync3(path, "utf-8");
|
|
1016
1183
|
} catch (err) {
|
|
1017
1184
|
log2(`[token-usage] read failed for '${codeName}/${name}': ${err.message}`);
|
|
1018
1185
|
continue;
|
|
@@ -1093,7 +1260,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1093
1260
|
}
|
|
1094
1261
|
|
|
1095
1262
|
// src/lib/activity-cache-monitor.ts
|
|
1096
|
-
import { existsSync, readFileSync as
|
|
1263
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
|
|
1097
1264
|
import { homedir } from "os";
|
|
1098
1265
|
import { join as join2 } from "path";
|
|
1099
1266
|
var MIN_CHECK_INTERVAL_MS3 = 6e4;
|
|
@@ -1140,12 +1307,12 @@ async function maybeReportActivityCache(args) {
|
|
|
1140
1307
|
const nowMs = now.getTime();
|
|
1141
1308
|
if (nowMs - state3.lastCheckedAt < MIN_CHECK_INTERVAL_MS3) return;
|
|
1142
1309
|
state3.lastCheckedAt = nowMs;
|
|
1143
|
-
if (!
|
|
1310
|
+
if (!existsSync2(STATS_CACHE_PATH)) {
|
|
1144
1311
|
return;
|
|
1145
1312
|
}
|
|
1146
1313
|
let raw;
|
|
1147
1314
|
try {
|
|
1148
|
-
raw =
|
|
1315
|
+
raw = readFileSync4(STATS_CACHE_PATH, "utf-8");
|
|
1149
1316
|
} catch (err) {
|
|
1150
1317
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
1151
1318
|
return;
|
|
@@ -1712,7 +1879,7 @@ function normalize(value) {
|
|
|
1712
1879
|
}
|
|
1713
1880
|
|
|
1714
1881
|
// src/lib/channel-hash-cache.ts
|
|
1715
|
-
import { existsSync as
|
|
1882
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
1716
1883
|
import { join as join4 } from "path";
|
|
1717
1884
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
1718
1885
|
function getChannelHashCacheFile(configDir) {
|
|
@@ -1720,10 +1887,10 @@ function getChannelHashCacheFile(configDir) {
|
|
|
1720
1887
|
}
|
|
1721
1888
|
function loadChannelHashCache(target, configDir) {
|
|
1722
1889
|
const path = getChannelHashCacheFile(configDir);
|
|
1723
|
-
if (!
|
|
1890
|
+
if (!existsSync3(path)) return;
|
|
1724
1891
|
let parsed;
|
|
1725
1892
|
try {
|
|
1726
|
-
parsed = JSON.parse(
|
|
1893
|
+
parsed = JSON.parse(readFileSync5(path, "utf-8"));
|
|
1727
1894
|
} catch {
|
|
1728
1895
|
return;
|
|
1729
1896
|
}
|
|
@@ -1737,7 +1904,7 @@ function saveChannelHashCache(source, configDir) {
|
|
|
1737
1904
|
const obj = {};
|
|
1738
1905
|
for (const [key, value] of source) obj[key] = value;
|
|
1739
1906
|
try {
|
|
1740
|
-
|
|
1907
|
+
writeFileSync2(path, JSON.stringify(obj, null, 2));
|
|
1741
1908
|
} catch {
|
|
1742
1909
|
}
|
|
1743
1910
|
}
|
|
@@ -2210,6 +2377,16 @@ var agentState = {
|
|
|
2210
2377
|
knownIntegrationHashes: /* @__PURE__ */ new Map(),
|
|
2211
2378
|
knownManagedMcpHashes: /* @__PURE__ */ new Map(),
|
|
2212
2379
|
knownManagedMcpStructure: /* @__PURE__ */ new Map(),
|
|
2380
|
+
/**
|
|
2381
|
+
* ENG-5858: hash of the effective sender_policy returned by
|
|
2382
|
+
* /host/refresh for this agent. Tracked separately from
|
|
2383
|
+
* `knownChannelConfigHashes` because the channel-cred hash is
|
|
2384
|
+
* per-channel (cleared/rewritten per channel) whereas sender_policy is
|
|
2385
|
+
* agent-wide. A change here triggers a session restart so the
|
|
2386
|
+
* slack-channel / teams-channel MCP children pick up the new env vars
|
|
2387
|
+
* (they read SLACK_SENDER_POLICY / MSTEAMS_SENDER_POLICY only at boot).
|
|
2388
|
+
*/
|
|
2389
|
+
knownSenderPolicyHashes: /* @__PURE__ */ new Map(),
|
|
2213
2390
|
// ---------------------------------------------------------------------------
|
|
2214
2391
|
// codeName-keyed
|
|
2215
2392
|
// ---------------------------------------------------------------------------
|
|
@@ -2239,6 +2416,7 @@ function clearAgentState(agentId, codeName) {
|
|
|
2239
2416
|
agentState.knownIntegrationHashes.delete(agentId);
|
|
2240
2417
|
agentState.knownManagedMcpHashes.delete(agentId);
|
|
2241
2418
|
agentState.knownManagedMcpStructure.delete(agentId);
|
|
2419
|
+
agentState.knownSenderPolicyHashes.delete(agentId);
|
|
2242
2420
|
agentState.agentDisplayNames.delete(codeName);
|
|
2243
2421
|
agentState.codeNameToAgentId.delete(codeName);
|
|
2244
2422
|
let channelCacheMutated = false;
|
|
@@ -2257,7 +2435,7 @@ function clearAgentState(agentId, codeName) {
|
|
|
2257
2435
|
}
|
|
2258
2436
|
|
|
2259
2437
|
// src/lib/restart-flags.ts
|
|
2260
|
-
import { existsSync as
|
|
2438
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync6, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2261
2439
|
import { homedir as homedir3 } from "os";
|
|
2262
2440
|
import { join as join5 } from "path";
|
|
2263
2441
|
import { randomUUID } from "crypto";
|
|
@@ -2269,12 +2447,12 @@ function flagPath(codeName) {
|
|
|
2269
2447
|
}
|
|
2270
2448
|
function readRestartFlags() {
|
|
2271
2449
|
const dir = restartFlagsDir();
|
|
2272
|
-
if (!
|
|
2450
|
+
if (!existsSync4(dir)) return [];
|
|
2273
2451
|
const out = [];
|
|
2274
2452
|
for (const entry of readdirSync2(dir)) {
|
|
2275
2453
|
if (!entry.endsWith(".flag")) continue;
|
|
2276
2454
|
try {
|
|
2277
|
-
const raw =
|
|
2455
|
+
const raw = readFileSync6(join5(dir, entry), "utf8");
|
|
2278
2456
|
const parsed = JSON.parse(raw);
|
|
2279
2457
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
2280
2458
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -2292,7 +2470,7 @@ function readRestartFlags() {
|
|
|
2292
2470
|
}
|
|
2293
2471
|
function deleteRestartFlag(codeName) {
|
|
2294
2472
|
const path = flagPath(codeName);
|
|
2295
|
-
if (
|
|
2473
|
+
if (existsSync4(path)) {
|
|
2296
2474
|
rmSync(path, { force: true });
|
|
2297
2475
|
}
|
|
2298
2476
|
}
|
|
@@ -2973,7 +3151,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
|
2973
3151
|
var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
2974
3152
|
function projectMcpHash(_codeName, projectDir) {
|
|
2975
3153
|
try {
|
|
2976
|
-
const raw =
|
|
3154
|
+
const raw = readFileSync7(join6(projectDir, ".mcp.json"), "utf-8");
|
|
2977
3155
|
return createHash3("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
2978
3156
|
} catch {
|
|
2979
3157
|
return null;
|
|
@@ -2981,7 +3159,7 @@ function projectMcpHash(_codeName, projectDir) {
|
|
|
2981
3159
|
}
|
|
2982
3160
|
function projectMcpKeys(_codeName, projectDir) {
|
|
2983
3161
|
try {
|
|
2984
|
-
const raw =
|
|
3162
|
+
const raw = readFileSync7(join6(projectDir, ".mcp.json"), "utf-8");
|
|
2985
3163
|
const parsed = JSON.parse(raw);
|
|
2986
3164
|
const servers = parsed.mcpServers;
|
|
2987
3165
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -2992,7 +3170,7 @@ function projectMcpKeys(_codeName, projectDir) {
|
|
|
2992
3170
|
}
|
|
2993
3171
|
function readMcpHttpServerConfig(projectDir, serverKey) {
|
|
2994
3172
|
try {
|
|
2995
|
-
const raw =
|
|
3173
|
+
const raw = readFileSync7(join6(projectDir, ".mcp.json"), "utf-8");
|
|
2996
3174
|
const servers = JSON.parse(raw).mcpServers ?? {};
|
|
2997
3175
|
const entry = servers[serverKey];
|
|
2998
3176
|
if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
|
|
@@ -3166,7 +3344,7 @@ var cachedFrameworkVersion = null;
|
|
|
3166
3344
|
var lastVersionCheckAt = 0;
|
|
3167
3345
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3168
3346
|
var lastResponsivenessProbeAt = 0;
|
|
3169
|
-
var agtCliVersion = true ? "0.27.
|
|
3347
|
+
var agtCliVersion = true ? "0.27.8-test.10" : "dev";
|
|
3170
3348
|
function resolveBrewPath(execFileSync4) {
|
|
3171
3349
|
try {
|
|
3172
3350
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -3179,7 +3357,7 @@ function resolveBrewPath(execFileSync4) {
|
|
|
3179
3357
|
"/usr/local/bin/brew"
|
|
3180
3358
|
];
|
|
3181
3359
|
for (const path of fallbacks) {
|
|
3182
|
-
if (
|
|
3360
|
+
if (existsSync5(path)) return path;
|
|
3183
3361
|
}
|
|
3184
3362
|
return null;
|
|
3185
3363
|
}
|
|
@@ -3189,7 +3367,7 @@ function claudeBinaryInstalled(execFileSync4) {
|
|
|
3189
3367
|
"/opt/homebrew/bin/claude",
|
|
3190
3368
|
"/usr/local/bin/claude"
|
|
3191
3369
|
];
|
|
3192
|
-
if (canonical.some((path) =>
|
|
3370
|
+
if (canonical.some((path) => existsSync5(path))) return true;
|
|
3193
3371
|
try {
|
|
3194
3372
|
execFileSync4("which", ["claude"], { timeout: 5e3 });
|
|
3195
3373
|
return true;
|
|
@@ -3261,7 +3439,7 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
3261
3439
|
toolkitCliEnsured.add(toolkitSlug);
|
|
3262
3440
|
return;
|
|
3263
3441
|
}
|
|
3264
|
-
brewBinDir =
|
|
3442
|
+
brewBinDir = dirname3(brewPath);
|
|
3265
3443
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
3266
3444
|
log(`[toolkit-install] ${toolkitSlug}: installing via brew (${pkg})\u2026`);
|
|
3267
3445
|
if (isRoot) {
|
|
@@ -3342,8 +3520,8 @@ function claudeManagedSettingsPath() {
|
|
|
3342
3520
|
function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
3343
3521
|
try {
|
|
3344
3522
|
let settings = {};
|
|
3345
|
-
if (
|
|
3346
|
-
const raw =
|
|
3523
|
+
if (existsSync5(path)) {
|
|
3524
|
+
const raw = readFileSync7(path, "utf-8").trim();
|
|
3347
3525
|
if (raw) {
|
|
3348
3526
|
let parsed;
|
|
3349
3527
|
try {
|
|
@@ -3359,8 +3537,8 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
3359
3537
|
}
|
|
3360
3538
|
if (settings.channelsEnabled === true) return;
|
|
3361
3539
|
settings.channelsEnabled = true;
|
|
3362
|
-
|
|
3363
|
-
|
|
3540
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
3541
|
+
writeFileSync4(path, `${JSON.stringify(settings, null, 2)}
|
|
3364
3542
|
`);
|
|
3365
3543
|
log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
|
|
3366
3544
|
} catch (err) {
|
|
@@ -3399,11 +3577,11 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
3399
3577
|
log(`Claude Code install failed: ${err.message}`);
|
|
3400
3578
|
return;
|
|
3401
3579
|
}
|
|
3402
|
-
const brewBinDir =
|
|
3580
|
+
const brewBinDir = dirname3(brewPath);
|
|
3403
3581
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
3404
3582
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
3405
3583
|
}
|
|
3406
|
-
if (
|
|
3584
|
+
if (existsSync5("/home/linuxbrew/.linuxbrew/bin/claude")) {
|
|
3407
3585
|
log("Claude Code installed successfully");
|
|
3408
3586
|
} else {
|
|
3409
3587
|
log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
|
|
@@ -3419,13 +3597,13 @@ function claudeCodeUpgradeMarkerPath() {
|
|
|
3419
3597
|
}
|
|
3420
3598
|
function stampClaudeCodeUpgradeMarker() {
|
|
3421
3599
|
try {
|
|
3422
|
-
|
|
3600
|
+
writeFileSync4(claudeCodeUpgradeMarkerPath(), String(Date.now()));
|
|
3423
3601
|
} catch {
|
|
3424
3602
|
}
|
|
3425
3603
|
}
|
|
3426
3604
|
function claudeCodeUpgradeThrottled() {
|
|
3427
3605
|
try {
|
|
3428
|
-
const lastCheck = parseInt(
|
|
3606
|
+
const lastCheck = parseInt(readFileSync7(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
3429
3607
|
if (!Number.isFinite(lastCheck)) return false;
|
|
3430
3608
|
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
3431
3609
|
} catch {
|
|
@@ -3474,6 +3652,9 @@ ${r.stderr}`;
|
|
|
3474
3652
|
});
|
|
3475
3653
|
}
|
|
3476
3654
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3655
|
+
function selfUpdateAppliedMarkerPath() {
|
|
3656
|
+
return join6(homedir4(), ".augmented", ".last-self-update-applied");
|
|
3657
|
+
}
|
|
3477
3658
|
var selfUpdateUpToDateLogged = false;
|
|
3478
3659
|
var restartAfterUpgrade = false;
|
|
3479
3660
|
var pendingUpgradeVersion = null;
|
|
@@ -3537,6 +3718,22 @@ async function checkAndUpdateCliViaBrew() {
|
|
|
3537
3718
|
if (agtOutdated) {
|
|
3538
3719
|
const installed = agtOutdated.installed_versions?.[0] ?? "unknown";
|
|
3539
3720
|
const latest = agtOutdated.current_version ?? "unknown";
|
|
3721
|
+
const coalesceWindowMs = resolveCoalesceWindowMs();
|
|
3722
|
+
const coalesce = decideSelfUpdateCoalesce({
|
|
3723
|
+
windowMs: coalesceWindowMs,
|
|
3724
|
+
lastAppliedMs: readLastSelfUpdateAppliedMs(selfUpdateAppliedMarkerPath()),
|
|
3725
|
+
now: Date.now()
|
|
3726
|
+
});
|
|
3727
|
+
if (!coalesce.proceed) {
|
|
3728
|
+
log(formatCoalesceDeferLogLine({
|
|
3729
|
+
installed,
|
|
3730
|
+
latest,
|
|
3731
|
+
channelLabel: "brew",
|
|
3732
|
+
remainingMs: coalesce.remainingMs,
|
|
3733
|
+
windowMs: coalesceWindowMs
|
|
3734
|
+
}));
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
3540
3737
|
log(`[self-update] agt CLI update available: ${installed} \u2192 ${latest}. Upgrading via brew...`);
|
|
3541
3738
|
try {
|
|
3542
3739
|
execFileSync4(brewPath, ["upgrade", "integrity-labs/tap/agt"], {
|
|
@@ -3544,6 +3741,7 @@ async function checkAndUpdateCliViaBrew() {
|
|
|
3544
3741
|
stdio: "pipe"
|
|
3545
3742
|
});
|
|
3546
3743
|
log(`[self-update] agt CLI upgraded to ${latest}. Scheduling manager restart so the new binary takes effect.`);
|
|
3744
|
+
stampLastSelfUpdateApplied(selfUpdateAppliedMarkerPath());
|
|
3547
3745
|
restartAfterUpgrade = true;
|
|
3548
3746
|
pendingUpgradeVersion = latest;
|
|
3549
3747
|
} catch (err) {
|
|
@@ -3604,6 +3802,22 @@ async function checkAndUpdateCliViaNpm() {
|
|
|
3604
3802
|
}
|
|
3605
3803
|
return;
|
|
3606
3804
|
}
|
|
3805
|
+
const coalesceWindowMs = resolveCoalesceWindowMs();
|
|
3806
|
+
const coalesce = decideSelfUpdateCoalesce({
|
|
3807
|
+
windowMs: coalesceWindowMs,
|
|
3808
|
+
lastAppliedMs: readLastSelfUpdateAppliedMs(selfUpdateAppliedMarkerPath()),
|
|
3809
|
+
now: Date.now()
|
|
3810
|
+
});
|
|
3811
|
+
if (!coalesce.proceed) {
|
|
3812
|
+
log(formatCoalesceDeferLogLine({
|
|
3813
|
+
installed: agtCliVersion,
|
|
3814
|
+
latest,
|
|
3815
|
+
channelLabel: `npm, channel=${channel}`,
|
|
3816
|
+
remainingMs: coalesce.remainingMs,
|
|
3817
|
+
windowMs: coalesceWindowMs
|
|
3818
|
+
}));
|
|
3819
|
+
return;
|
|
3820
|
+
}
|
|
3607
3821
|
log(`[self-update] agt CLI update available: ${agtCliVersion} \u2192 ${latest} (channel=${channel}). Upgrading via npm...`);
|
|
3608
3822
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
3609
3823
|
const cmd = isRoot ? "npm" : "sudo";
|
|
@@ -3623,6 +3837,7 @@ async function checkAndUpdateCliViaNpm() {
|
|
|
3623
3837
|
try {
|
|
3624
3838
|
execFileSync4(cmd, args, { timeout: 18e4, stdio: "pipe" });
|
|
3625
3839
|
log(`[self-update] agt CLI upgraded to ${latest}. Scheduling manager restart so the new binary takes effect.`);
|
|
3840
|
+
stampLastSelfUpdateApplied(selfUpdateAppliedMarkerPath());
|
|
3626
3841
|
restartAfterUpgrade = true;
|
|
3627
3842
|
pendingUpgradeVersion = latest;
|
|
3628
3843
|
} catch (err) {
|
|
@@ -3685,7 +3900,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3685
3900
|
const claudeDir = join6(homedir4(), ".claude");
|
|
3686
3901
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
3687
3902
|
const p = join6(claudeDir, filename);
|
|
3688
|
-
if (
|
|
3903
|
+
if (existsSync5(p)) {
|
|
3689
3904
|
try {
|
|
3690
3905
|
rmSync2(p, { force: true });
|
|
3691
3906
|
log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
|
|
@@ -3699,14 +3914,14 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3699
3914
|
}
|
|
3700
3915
|
function loadGatewayPorts() {
|
|
3701
3916
|
try {
|
|
3702
|
-
return JSON.parse(
|
|
3917
|
+
return JSON.parse(readFileSync7(GATEWAY_PORTS_FILE, "utf-8"));
|
|
3703
3918
|
} catch {
|
|
3704
3919
|
return {};
|
|
3705
3920
|
}
|
|
3706
3921
|
}
|
|
3707
3922
|
function saveGatewayPorts(ports) {
|
|
3708
|
-
|
|
3709
|
-
|
|
3923
|
+
mkdirSync4(AUGMENTED_DIR, { recursive: true });
|
|
3924
|
+
writeFileSync4(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
3710
3925
|
}
|
|
3711
3926
|
function allocatePort(codeName) {
|
|
3712
3927
|
const ports = loadGatewayPorts();
|
|
@@ -3743,7 +3958,7 @@ function saveChannelHashCache2() {
|
|
|
3743
3958
|
function send(msg) {
|
|
3744
3959
|
if (msg.type === "state-update") {
|
|
3745
3960
|
try {
|
|
3746
|
-
|
|
3961
|
+
atomicWriteFileSync(getStateFile(), JSON.stringify(msg.state, null, 2));
|
|
3747
3962
|
} catch {
|
|
3748
3963
|
}
|
|
3749
3964
|
}
|
|
@@ -3775,8 +3990,8 @@ function log(msg) {
|
|
|
3775
3990
|
if (!managerLogPath) {
|
|
3776
3991
|
try {
|
|
3777
3992
|
managerLogPath = join6(homedir4(), ".augmented", "manager.log");
|
|
3778
|
-
|
|
3779
|
-
if (
|
|
3993
|
+
mkdirSync4(dirname3(managerLogPath), { recursive: true });
|
|
3994
|
+
if (existsSync5(managerLogPath)) {
|
|
3780
3995
|
chmodSync(managerLogPath, 384);
|
|
3781
3996
|
}
|
|
3782
3997
|
} catch {
|
|
@@ -3804,7 +4019,7 @@ function sha256(content) {
|
|
|
3804
4019
|
}
|
|
3805
4020
|
function hashFile(filePath) {
|
|
3806
4021
|
try {
|
|
3807
|
-
const content =
|
|
4022
|
+
const content = readFileSync7(filePath, "utf-8");
|
|
3808
4023
|
return sha256(content);
|
|
3809
4024
|
} catch {
|
|
3810
4025
|
return null;
|
|
@@ -3828,7 +4043,7 @@ function parseSkillFrontmatter(content) {
|
|
|
3828
4043
|
return out;
|
|
3829
4044
|
}
|
|
3830
4045
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
3831
|
-
const { readdirSync: readdirSync4, readFileSync: rfs, existsSync: ex, writeFileSync:
|
|
4046
|
+
const { readdirSync: readdirSync4, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync5 } = await import("fs");
|
|
3832
4047
|
const skillsDir = join6(configDir, codeName, "project", ".claude", "skills");
|
|
3833
4048
|
const claudeMdPath = join6(configDir, codeName, "project", "CLAUDE.md");
|
|
3834
4049
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
@@ -3876,7 +4091,7 @@ ${SKILLS_INDEX_END}`;
|
|
|
3876
4091
|
next = current.trimEnd() + "\n\n" + section + "\n";
|
|
3877
4092
|
}
|
|
3878
4093
|
if (next !== current) {
|
|
3879
|
-
|
|
4094
|
+
writeFileSync5(claudeMdPath, next, "utf-8");
|
|
3880
4095
|
log2(`Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)`);
|
|
3881
4096
|
}
|
|
3882
4097
|
}
|
|
@@ -3885,7 +4100,7 @@ async function migrateToProfiles() {
|
|
|
3885
4100
|
const sharedConfigPath = join6(homeDir, ".openclaw", "openclaw.json");
|
|
3886
4101
|
let sharedConfig;
|
|
3887
4102
|
try {
|
|
3888
|
-
sharedConfig = JSON.parse(
|
|
4103
|
+
sharedConfig = JSON.parse(readFileSync7(sharedConfigPath, "utf-8"));
|
|
3889
4104
|
} catch {
|
|
3890
4105
|
return;
|
|
3891
4106
|
}
|
|
@@ -3899,7 +4114,7 @@ async function migrateToProfiles() {
|
|
|
3899
4114
|
if (!codeName) continue;
|
|
3900
4115
|
if (codeName === "main") continue;
|
|
3901
4116
|
const profileDir = join6(homeDir, `.openclaw-${codeName}`);
|
|
3902
|
-
if (
|
|
4117
|
+
if (existsSync5(join6(profileDir, "openclaw.json"))) continue;
|
|
3903
4118
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
3904
4119
|
if (adapter.seedProfileConfig) {
|
|
3905
4120
|
adapter.seedProfileConfig(codeName);
|
|
@@ -3907,10 +4122,10 @@ async function migrateToProfiles() {
|
|
|
3907
4122
|
const sharedAuthDir = join6(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
3908
4123
|
const profileAuthDir = join6(profileDir, "agents", codeName, "agent");
|
|
3909
4124
|
const authFile = join6(sharedAuthDir, "auth-profiles.json");
|
|
3910
|
-
if (
|
|
3911
|
-
|
|
3912
|
-
const authContent =
|
|
3913
|
-
|
|
4125
|
+
if (existsSync5(authFile)) {
|
|
4126
|
+
mkdirSync4(profileAuthDir, { recursive: true });
|
|
4127
|
+
const authContent = readFileSync7(authFile, "utf-8");
|
|
4128
|
+
writeFileSync4(join6(profileAuthDir, "auth-profiles.json"), authContent);
|
|
3914
4129
|
}
|
|
3915
4130
|
allocatePort(codeName);
|
|
3916
4131
|
migrated++;
|
|
@@ -3948,7 +4163,7 @@ function readGatewayToken(codeName) {
|
|
|
3948
4163
|
}
|
|
3949
4164
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3950
4165
|
try {
|
|
3951
|
-
const cfg = JSON.parse(
|
|
4166
|
+
const cfg = JSON.parse(readFileSync7(join6(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
3952
4167
|
return cfg?.gateway?.auth?.token;
|
|
3953
4168
|
} catch {
|
|
3954
4169
|
return void 0;
|
|
@@ -3958,9 +4173,9 @@ var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
|
3958
4173
|
function isGatewayHung(codeName) {
|
|
3959
4174
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3960
4175
|
const jobsPath = join6(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
3961
|
-
if (!
|
|
4176
|
+
if (!existsSync5(jobsPath)) return false;
|
|
3962
4177
|
try {
|
|
3963
|
-
const data = JSON.parse(
|
|
4178
|
+
const data = JSON.parse(readFileSync7(jobsPath, "utf-8"));
|
|
3964
4179
|
const jobs = data.jobs ?? data;
|
|
3965
4180
|
if (!Array.isArray(jobs)) return false;
|
|
3966
4181
|
const now = Date.now();
|
|
@@ -4000,12 +4215,12 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4000
4215
|
try {
|
|
4001
4216
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4002
4217
|
const configPath = join6(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4003
|
-
if (
|
|
4004
|
-
const cfg = JSON.parse(
|
|
4218
|
+
if (existsSync5(configPath)) {
|
|
4219
|
+
const cfg = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
4005
4220
|
if (cfg.gateway?.port !== status.port) {
|
|
4006
4221
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4007
4222
|
cfg.gateway.port = status.port;
|
|
4008
|
-
|
|
4223
|
+
writeFileSync4(configPath, JSON.stringify(cfg, null, 2));
|
|
4009
4224
|
}
|
|
4010
4225
|
}
|
|
4011
4226
|
} catch {
|
|
@@ -4026,11 +4241,11 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4026
4241
|
try {
|
|
4027
4242
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4028
4243
|
const configPath = join6(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4029
|
-
if (
|
|
4030
|
-
const cfg = JSON.parse(
|
|
4244
|
+
if (existsSync5(configPath)) {
|
|
4245
|
+
const cfg = JSON.parse(readFileSync7(configPath, "utf-8"));
|
|
4031
4246
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4032
4247
|
cfg.gateway.port = port;
|
|
4033
|
-
|
|
4248
|
+
writeFileSync4(configPath, JSON.stringify(cfg, null, 2));
|
|
4034
4249
|
}
|
|
4035
4250
|
} catch {
|
|
4036
4251
|
}
|
|
@@ -4180,7 +4395,7 @@ async function pollCycle() {
|
|
|
4180
4395
|
}
|
|
4181
4396
|
try {
|
|
4182
4397
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
4183
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
4398
|
+
const { collectDiagnostics } = await import("../persistent-session-HSHIZC2Q.js");
|
|
4184
4399
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
4185
4400
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
4186
4401
|
let tailscaleHostname;
|
|
@@ -4248,7 +4463,7 @@ async function pollCycle() {
|
|
|
4248
4463
|
const {
|
|
4249
4464
|
collectResponsivenessProbes,
|
|
4250
4465
|
getResponsivenessIntervalMs
|
|
4251
|
-
} = await import("../responsiveness-probe-
|
|
4466
|
+
} = await import("../responsiveness-probe-RYKS5TRP.js");
|
|
4252
4467
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
4253
4468
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
4254
4469
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -4363,7 +4578,7 @@ async function pollCycle() {
|
|
|
4363
4578
|
if (restartAckStateChanged) {
|
|
4364
4579
|
try {
|
|
4365
4580
|
const ackedState = { ...state4, agents: agentStates };
|
|
4366
|
-
|
|
4581
|
+
atomicWriteFileSync(getStateFile(), JSON.stringify(ackedState, null, 2));
|
|
4367
4582
|
} catch (err) {
|
|
4368
4583
|
log(`[restart] failed to persist ack immediately: ${err.message}`);
|
|
4369
4584
|
}
|
|
@@ -4583,7 +4798,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4583
4798
|
const residuals = {
|
|
4584
4799
|
gatewayRunning: gatewayLiveness.running,
|
|
4585
4800
|
portAllocated: Object.prototype.hasOwnProperty.call(ports, agent.code_name),
|
|
4586
|
-
provisionDirExists:
|
|
4801
|
+
provisionDirExists: existsSync5(agentDir)
|
|
4587
4802
|
};
|
|
4588
4803
|
if (!hasRevokedResiduals(residuals)) {
|
|
4589
4804
|
agentStates.push({
|
|
@@ -4742,7 +4957,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4742
4957
|
try {
|
|
4743
4958
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
4744
4959
|
const changedFiles = [];
|
|
4745
|
-
|
|
4960
|
+
mkdirSync4(agentDir, { recursive: true });
|
|
4746
4961
|
for (const artifact of artifacts) {
|
|
4747
4962
|
const filePath = join6(agentDir, artifact.relativePath);
|
|
4748
4963
|
let existingHash;
|
|
@@ -4764,7 +4979,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4764
4979
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
4765
4980
|
try {
|
|
4766
4981
|
const projectClaudeMd = join6(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
4767
|
-
const existing =
|
|
4982
|
+
const existing = readFileSync7(projectClaudeMd, "utf-8");
|
|
4768
4983
|
existingHash = sha256(stripDynamicSections(existing));
|
|
4769
4984
|
} catch {
|
|
4770
4985
|
existingHash = null;
|
|
@@ -4782,7 +4997,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4782
4997
|
const generatorKeys = Object.keys(generatorServers);
|
|
4783
4998
|
let existingRaw = "";
|
|
4784
4999
|
try {
|
|
4785
|
-
existingRaw =
|
|
5000
|
+
existingRaw = readFileSync7(filePath, "utf-8");
|
|
4786
5001
|
} catch {
|
|
4787
5002
|
}
|
|
4788
5003
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -4804,18 +5019,18 @@ async function processAgent(agent, agentStates) {
|
|
|
4804
5019
|
}
|
|
4805
5020
|
}
|
|
4806
5021
|
if (changedFiles.length > 0) {
|
|
4807
|
-
const isFirst = !
|
|
5022
|
+
const isFirst = !existsSync5(join6(agentDir, "CHARTER.md"));
|
|
4808
5023
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
4809
5024
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
4810
5025
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
4811
5026
|
for (const file of changedFiles) {
|
|
4812
5027
|
const filePath = join6(agentDir, file.relativePath);
|
|
4813
|
-
|
|
4814
|
-
|
|
5028
|
+
mkdirSync4(dirname3(filePath), { recursive: true });
|
|
5029
|
+
writeFileSync4(filePath, file.content);
|
|
4815
5030
|
}
|
|
4816
5031
|
try {
|
|
4817
5032
|
const provSkillsDir = join6(agentDir, ".claude", "skills");
|
|
4818
|
-
if (
|
|
5033
|
+
if (existsSync5(provSkillsDir)) {
|
|
4819
5034
|
for (const folder of readdirSync3(provSkillsDir)) {
|
|
4820
5035
|
if (folder.startsWith("knowledge-")) {
|
|
4821
5036
|
try {
|
|
@@ -4897,7 +5112,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4897
5112
|
}
|
|
4898
5113
|
let lastDriftCheckAt = now;
|
|
4899
5114
|
const written = agentState.writtenHashes.get(agent.agent_id);
|
|
4900
|
-
if (written &&
|
|
5115
|
+
if (written && existsSync5(agentDir)) {
|
|
4901
5116
|
const driftedFiles = [];
|
|
4902
5117
|
for (const [file, expectedHash] of written) {
|
|
4903
5118
|
const localHash = hashFile(join6(agentDir, file));
|
|
@@ -4975,14 +5190,16 @@ async function processAgent(agent, agentStates) {
|
|
|
4975
5190
|
} : void 0;
|
|
4976
5191
|
const peersForHash = channelId === "telegram" ? extractCharterTelegramPeers(refreshData.charter?.raw_content ?? "", gateContext) : channelId === "slack" ? extractCharterSlackPeers(refreshData.charter?.raw_content ?? "", gateContext) : null;
|
|
4977
5192
|
const sessionModeForHash = refreshData.agent.session_mode;
|
|
4978
|
-
const
|
|
5193
|
+
const senderPolicyForHash = refreshData.sender_policy ?? null;
|
|
5194
|
+
const CHANNEL_WRITE_VERSION = 8;
|
|
4979
5195
|
const configHash = createHash3("sha256").update(
|
|
4980
5196
|
canonicalJson({
|
|
4981
5197
|
writeVersion: CHANNEL_WRITE_VERSION,
|
|
4982
5198
|
config: entry.config,
|
|
4983
5199
|
team: teamSettingsForHash,
|
|
4984
5200
|
peers: peersForHash,
|
|
4985
|
-
sessionMode: sessionModeForHash ?? null
|
|
5201
|
+
sessionMode: sessionModeForHash ?? null,
|
|
5202
|
+
senderPolicy: senderPolicyForHash
|
|
4986
5203
|
})
|
|
4987
5204
|
).digest("hex");
|
|
4988
5205
|
const cacheKey = `${agent.agent_id}:${channelId}`;
|
|
@@ -5008,6 +5225,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5008
5225
|
const telegramPeerDisabled = channelId === "telegram" ? peerDisabledMode === "all" : void 0;
|
|
5009
5226
|
const telegramPeers = channelId === "telegram" ? extractCharterTelegramPeers(refreshData.charter?.raw_content ?? "", gateContext) : void 0;
|
|
5010
5227
|
const slackPeers = channelId === "slack" ? extractCharterSlackPeers(refreshData.charter?.raw_content ?? "", gateContext) : void 0;
|
|
5228
|
+
const senderPolicyForCall = channelId === "slack" || channelId === "msteams" ? refreshData.sender_policy ?? void 0 : void 0;
|
|
5011
5229
|
frameworkAdapter.writeChannelCredentials(
|
|
5012
5230
|
agent.code_name,
|
|
5013
5231
|
channelId,
|
|
@@ -5019,7 +5237,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5019
5237
|
peerDisabled,
|
|
5020
5238
|
telegramPeers,
|
|
5021
5239
|
slackPeers,
|
|
5022
|
-
agentTimezone
|
|
5240
|
+
agentTimezone,
|
|
5241
|
+
senderPolicy: senderPolicyForCall
|
|
5023
5242
|
}
|
|
5024
5243
|
);
|
|
5025
5244
|
agentState.knownChannelConfigHashes.set(cacheKey, configHash);
|
|
@@ -5065,18 +5284,58 @@ async function processAgent(agent, agentStates) {
|
|
|
5065
5284
|
}
|
|
5066
5285
|
scheduleSessionRestart(agent.code_name, delay, "new channel set");
|
|
5067
5286
|
}
|
|
5287
|
+
if (channelConfigConverged) {
|
|
5288
|
+
const hasSenderPolicyChannel = currentChannelIds.has("slack") || currentChannelIds.has("msteams");
|
|
5289
|
+
const senderPolicyForRestartHash = refreshData.sender_policy ?? null;
|
|
5290
|
+
const senderPolicyHash = createHash3("sha256").update(canonicalJson({ senderPolicy: senderPolicyForRestartHash })).digest("hex");
|
|
5291
|
+
const prevSenderPolicyHash = agentState.knownSenderPolicyHashes.get(agent.agent_id);
|
|
5292
|
+
const senderPolicyDecision = hasSenderPolicyChannel ? decideSenderPolicyRestart({
|
|
5293
|
+
previousHash: prevSenderPolicyHash,
|
|
5294
|
+
currentHash: senderPolicyHash,
|
|
5295
|
+
sessionMode: refreshData.agent.session_mode,
|
|
5296
|
+
framework: agentFrameworkCache.get(agent.code_name) ?? "openclaw",
|
|
5297
|
+
sessionHealthy: isSessionHealthy(agent.code_name),
|
|
5298
|
+
channelSetRestartAlreadyScheduled: restartDecision.restart
|
|
5299
|
+
}) : { restart: false, firstPoll: prevSenderPolicyHash === void 0, changed: false };
|
|
5300
|
+
if (senderPolicyDecision.restart) {
|
|
5301
|
+
log(
|
|
5302
|
+
`[hot-reload] sender_policy changed for '${agent.code_name}' (${prevSenderPolicyHash?.slice(0, 8) ?? "first"} \u2192 ${senderPolicyHash.slice(0, 8)}) \u2014 restarting session`
|
|
5303
|
+
);
|
|
5304
|
+
const notice = "Your sender_policy has been updated. Restarting session shortly so the channel MCP servers pick up the new gate.";
|
|
5305
|
+
const delivered = await injectMessage(
|
|
5306
|
+
agent.code_name,
|
|
5307
|
+
"system",
|
|
5308
|
+
notice,
|
|
5309
|
+
{ task_name: "sender-policy-update" },
|
|
5310
|
+
log
|
|
5311
|
+
).catch(() => false);
|
|
5312
|
+
const delay = delivered ? 8e3 : 3e3;
|
|
5313
|
+
if (!delivered) {
|
|
5314
|
+
log(
|
|
5315
|
+
`[hot-reload] Inject notification unconfirmed for '${agent.code_name}' \u2014 proceeding with shorter delay`
|
|
5316
|
+
);
|
|
5317
|
+
}
|
|
5318
|
+
scheduleSessionRestart(
|
|
5319
|
+
agent.code_name,
|
|
5320
|
+
delay,
|
|
5321
|
+
"sender_policy change",
|
|
5322
|
+
"sender-policy-change"
|
|
5323
|
+
);
|
|
5324
|
+
}
|
|
5325
|
+
agentState.knownSenderPolicyHashes.set(agent.agent_id, senderPolicyHash);
|
|
5326
|
+
}
|
|
5068
5327
|
const agentSessionMode = refreshData.agent.session_mode;
|
|
5069
5328
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
5070
5329
|
try {
|
|
5071
5330
|
const agentProvisionDir = agentDir;
|
|
5072
5331
|
const projectDir = join6(homedir4(), ".augmented", agent.code_name, "project");
|
|
5073
|
-
|
|
5074
|
-
|
|
5332
|
+
mkdirSync4(agentProvisionDir, { recursive: true });
|
|
5333
|
+
mkdirSync4(projectDir, { recursive: true });
|
|
5075
5334
|
const provisionMcpPath = join6(agentProvisionDir, ".mcp.json");
|
|
5076
5335
|
const projectMcpPath = join6(projectDir, ".mcp.json");
|
|
5077
5336
|
let mcpConfig = { mcpServers: {} };
|
|
5078
5337
|
try {
|
|
5079
|
-
mcpConfig = JSON.parse(
|
|
5338
|
+
mcpConfig = JSON.parse(readFileSync7(provisionMcpPath, "utf-8"));
|
|
5080
5339
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
5081
5340
|
} catch {
|
|
5082
5341
|
}
|
|
@@ -5086,7 +5345,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5086
5345
|
const tz = directChatTeamSettings?.["timezone"];
|
|
5087
5346
|
return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
|
|
5088
5347
|
})();
|
|
5089
|
-
if (
|
|
5348
|
+
if (existsSync5(localDirectChatChannel) && !mcpConfig.mcpServers["direct-chat"]) {
|
|
5090
5349
|
mcpConfig.mcpServers["direct-chat"] = {
|
|
5091
5350
|
command: "node",
|
|
5092
5351
|
args: [localDirectChatChannel],
|
|
@@ -5098,12 +5357,12 @@ async function processAgent(agent, agentStates) {
|
|
|
5098
5357
|
}
|
|
5099
5358
|
};
|
|
5100
5359
|
const serialized = JSON.stringify(mcpConfig, null, 2);
|
|
5101
|
-
|
|
5102
|
-
|
|
5360
|
+
writeFileSync4(provisionMcpPath, serialized);
|
|
5361
|
+
writeFileSync4(projectMcpPath, serialized);
|
|
5103
5362
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
5104
5363
|
}
|
|
5105
5364
|
const staleChannelsPath = join6(projectDir, ".mcp-channels.json");
|
|
5106
|
-
if (
|
|
5365
|
+
if (existsSync5(staleChannelsPath)) {
|
|
5107
5366
|
try {
|
|
5108
5367
|
rmSync2(staleChannelsPath, { force: true });
|
|
5109
5368
|
} catch {
|
|
@@ -5181,7 +5440,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5181
5440
|
const envIntPath = join6(projectDir, ".env.integrations");
|
|
5182
5441
|
let preWriteEnv;
|
|
5183
5442
|
try {
|
|
5184
|
-
preWriteEnv =
|
|
5443
|
+
preWriteEnv = readFileSync7(envIntPath, "utf-8");
|
|
5185
5444
|
} catch {
|
|
5186
5445
|
preWriteEnv = void 0;
|
|
5187
5446
|
}
|
|
@@ -5194,8 +5453,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5194
5453
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
5195
5454
|
try {
|
|
5196
5455
|
const projectMcpPath = join6(projectDir, ".mcp.json");
|
|
5197
|
-
const postWriteEnv =
|
|
5198
|
-
const mcpContent =
|
|
5456
|
+
const postWriteEnv = readFileSync7(envIntPath, "utf-8");
|
|
5457
|
+
const mcpContent = readFileSync7(projectMcpPath, "utf-8");
|
|
5199
5458
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
5200
5459
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
5201
5460
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -5263,8 +5522,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5263
5522
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
5264
5523
|
if (mcpPath) {
|
|
5265
5524
|
try {
|
|
5266
|
-
const { readFileSync:
|
|
5267
|
-
const mcpConfig = JSON.parse(
|
|
5525
|
+
const { readFileSync: readFileSync8 } = await import("fs");
|
|
5526
|
+
const mcpConfig = JSON.parse(readFileSync8(mcpPath, "utf-8"));
|
|
5268
5527
|
if (mcpConfig.mcpServers) {
|
|
5269
5528
|
const managedPrefixes = [
|
|
5270
5529
|
"composio_",
|
|
@@ -5361,7 +5620,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5361
5620
|
if (frameworkAdapter.installPlugin) {
|
|
5362
5621
|
try {
|
|
5363
5622
|
const pluginPath = join6(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
5364
|
-
if (
|
|
5623
|
+
if (existsSync5(pluginPath)) {
|
|
5365
5624
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
5366
5625
|
agtHost: requireHost(),
|
|
5367
5626
|
agtApiKey: getApiKey() ?? void 0,
|
|
@@ -5440,7 +5699,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5440
5699
|
// install target but cheap to sweep.
|
|
5441
5700
|
join6(agentDir, ".claude", "skills")
|
|
5442
5701
|
];
|
|
5443
|
-
const existingDirs = candidateSkillDirs.filter((d) =>
|
|
5702
|
+
const existingDirs = candidateSkillDirs.filter((d) => existsSync5(d));
|
|
5444
5703
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
5445
5704
|
for (const dir of existingDirs) {
|
|
5446
5705
|
try {
|
|
@@ -5455,7 +5714,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5455
5714
|
const removeSkillFolder = (entry, reason) => {
|
|
5456
5715
|
for (const dir of existingDirs) {
|
|
5457
5716
|
const p = join6(dir, entry);
|
|
5458
|
-
if (
|
|
5717
|
+
if (existsSync5(p)) {
|
|
5459
5718
|
rmSync3(p, { recursive: true, force: true });
|
|
5460
5719
|
}
|
|
5461
5720
|
}
|
|
@@ -5617,7 +5876,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5617
5876
|
let mcpJsonParsed = null;
|
|
5618
5877
|
try {
|
|
5619
5878
|
const mcpPath = join6(getProjectDir(agent.code_name), ".mcp.json");
|
|
5620
|
-
mcpJsonParsed = JSON.parse(
|
|
5879
|
+
mcpJsonParsed = JSON.parse(readFileSync7(mcpPath, "utf-8"));
|
|
5621
5880
|
} catch {
|
|
5622
5881
|
}
|
|
5623
5882
|
reapMissingMcpSessions({
|
|
@@ -5772,9 +6031,9 @@ async function processAgent(agent, agentStates) {
|
|
|
5772
6031
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
5773
6032
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
5774
6033
|
const jobsPath = join6(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
5775
|
-
if (
|
|
6034
|
+
if (existsSync5(jobsPath)) {
|
|
5776
6035
|
try {
|
|
5777
|
-
const jobsData = JSON.parse(
|
|
6036
|
+
const jobsData = JSON.parse(readFileSync7(jobsPath, "utf-8"));
|
|
5778
6037
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
5779
6038
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
5780
6039
|
);
|
|
@@ -5908,7 +6167,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
5908
6167
|
}
|
|
5909
6168
|
}
|
|
5910
6169
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
5911
|
-
if (trackedFiles.length > 0 &&
|
|
6170
|
+
if (trackedFiles.length > 0 && existsSync5(agentDir)) {
|
|
5912
6171
|
const hashes = /* @__PURE__ */ new Map();
|
|
5913
6172
|
for (const file of trackedFiles) {
|
|
5914
6173
|
const h = hashFile(join6(agentDir, file));
|
|
@@ -5955,9 +6214,9 @@ function cleanupStaleSessions(codeName) {
|
|
|
5955
6214
|
}
|
|
5956
6215
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
5957
6216
|
const indexPath = join6(sessionsDir, "sessions.json");
|
|
5958
|
-
if (!
|
|
6217
|
+
if (!existsSync5(indexPath)) return;
|
|
5959
6218
|
try {
|
|
5960
|
-
const raw =
|
|
6219
|
+
const raw = readFileSync7(indexPath, "utf-8");
|
|
5961
6220
|
const index = JSON.parse(raw);
|
|
5962
6221
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
5963
6222
|
key: k,
|
|
@@ -5972,7 +6231,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5972
6231
|
if (entry.sessionId) {
|
|
5973
6232
|
const sessionFile = join6(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
5974
6233
|
try {
|
|
5975
|
-
if (
|
|
6234
|
+
if (existsSync5(sessionFile)) {
|
|
5976
6235
|
unlinkSync(sessionFile);
|
|
5977
6236
|
deletedFiles++;
|
|
5978
6237
|
}
|
|
@@ -5993,7 +6252,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5993
6252
|
if (parentSessionId) {
|
|
5994
6253
|
try {
|
|
5995
6254
|
const f = join6(sessionsDir, `${parentSessionId}.jsonl`);
|
|
5996
|
-
if (
|
|
6255
|
+
if (existsSync5(f)) {
|
|
5997
6256
|
unlinkSync(f);
|
|
5998
6257
|
deletedFiles++;
|
|
5999
6258
|
}
|
|
@@ -6002,7 +6261,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6002
6261
|
}
|
|
6003
6262
|
}
|
|
6004
6263
|
}
|
|
6005
|
-
|
|
6264
|
+
writeFileSync4(indexPath, JSON.stringify(index));
|
|
6006
6265
|
if (toDelete.length > 0) {
|
|
6007
6266
|
log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
|
|
6008
6267
|
}
|
|
@@ -6011,9 +6270,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6011
6270
|
}
|
|
6012
6271
|
var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
6013
6272
|
function clearStaleCronRunState(jobsPath) {
|
|
6014
|
-
if (!
|
|
6273
|
+
if (!existsSync5(jobsPath)) return;
|
|
6015
6274
|
try {
|
|
6016
|
-
const raw =
|
|
6275
|
+
const raw = readFileSync7(jobsPath, "utf-8");
|
|
6017
6276
|
const data = JSON.parse(raw);
|
|
6018
6277
|
const jobs = data.jobs ?? data;
|
|
6019
6278
|
if (!Array.isArray(jobs)) return;
|
|
@@ -6038,13 +6297,13 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
6038
6297
|
}
|
|
6039
6298
|
}
|
|
6040
6299
|
if (changed) {
|
|
6041
|
-
|
|
6300
|
+
writeFileSync4(jobsPath, JSON.stringify(data, null, 2));
|
|
6042
6301
|
}
|
|
6043
6302
|
} catch {
|
|
6044
6303
|
}
|
|
6045
6304
|
}
|
|
6046
6305
|
function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
6047
|
-
if (!
|
|
6306
|
+
if (!existsSync5(dir)) return;
|
|
6048
6307
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
6049
6308
|
let removed = 0;
|
|
6050
6309
|
try {
|
|
@@ -6069,6 +6328,25 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
|
6069
6328
|
var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
6070
6329
|
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
6071
6330
|
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
6331
|
+
function claudePidFilePath() {
|
|
6332
|
+
return join6(homedir4(), ".augmented", "manager-claude-pids.json");
|
|
6333
|
+
}
|
|
6334
|
+
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
6335
|
+
function registerClaudeSpawn(record) {
|
|
6336
|
+
inFlightClaudePids.set(record.pid, record);
|
|
6337
|
+
try {
|
|
6338
|
+
addSpawn(claudePidFilePath(), record);
|
|
6339
|
+
} catch (err) {
|
|
6340
|
+
log(`[drain] pid-file write failed: ${err.message}`);
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
function unregisterClaudeSpawn(pid) {
|
|
6344
|
+
inFlightClaudePids.delete(pid);
|
|
6345
|
+
try {
|
|
6346
|
+
removeSpawn(claudePidFilePath(), pid);
|
|
6347
|
+
} catch {
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6072
6350
|
var claudeSchedulerStates = /* @__PURE__ */ new Map();
|
|
6073
6351
|
async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData) {
|
|
6074
6352
|
const codeName = agent.code_name;
|
|
@@ -6340,9 +6618,9 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
6340
6618
|
try {
|
|
6341
6619
|
const claudeMdPath = join6(projectDir, "CLAUDE.md");
|
|
6342
6620
|
const serverNames = [];
|
|
6343
|
-
if (
|
|
6621
|
+
if (existsSync5(mcpConfigPath)) {
|
|
6344
6622
|
try {
|
|
6345
|
-
const d = JSON.parse(
|
|
6623
|
+
const d = JSON.parse(readFileSync7(mcpConfigPath, "utf-8"));
|
|
6346
6624
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
6347
6625
|
} catch {
|
|
6348
6626
|
}
|
|
@@ -6361,14 +6639,14 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
6361
6639
|
"--allowedTools",
|
|
6362
6640
|
allowedTools
|
|
6363
6641
|
];
|
|
6364
|
-
if (
|
|
6642
|
+
if (existsSync5(claudeMdPath)) {
|
|
6365
6643
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
6366
6644
|
}
|
|
6367
6645
|
const childEnv = { ...process.env };
|
|
6368
6646
|
const envIntPath = join6(projectDir, ".env.integrations");
|
|
6369
|
-
if (
|
|
6647
|
+
if (existsSync5(envIntPath)) {
|
|
6370
6648
|
try {
|
|
6371
|
-
for (const line of
|
|
6649
|
+
for (const line of readFileSync7(envIntPath, "utf-8").split("\n")) {
|
|
6372
6650
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
6373
6651
|
const eqIdx = line.indexOf("=");
|
|
6374
6652
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -6392,11 +6670,21 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
6392
6670
|
runId = startResult.run_id;
|
|
6393
6671
|
kanbanItemId = startResult.kanban_item_id;
|
|
6394
6672
|
if (runId) childEnv["AGT_RUN_ID"] = runId;
|
|
6673
|
+
const claudeKind = task.templateId === "kanban-work" ? "kanban-work" : "scheduled-task";
|
|
6395
6674
|
const { stdout, stderr } = await execFilePromiseLong(resolveClaudeBinary(), claudeArgs, {
|
|
6396
6675
|
cwd: projectDir,
|
|
6397
6676
|
timeout: 3e5,
|
|
6398
6677
|
stdin: "ignore",
|
|
6399
|
-
env: childEnv
|
|
6678
|
+
env: childEnv,
|
|
6679
|
+
onSpawn: (pid) => registerClaudeSpawn({
|
|
6680
|
+
pid,
|
|
6681
|
+
started_at: Date.now(),
|
|
6682
|
+
kind: claudeKind,
|
|
6683
|
+
agent_id: agentId,
|
|
6684
|
+
agent_code_name: codeName,
|
|
6685
|
+
kanban_card_id: kanbanItemId ?? void 0
|
|
6686
|
+
}),
|
|
6687
|
+
onExit: (pid) => unregisterClaudeSpawn(pid)
|
|
6400
6688
|
});
|
|
6401
6689
|
if (stderr) {
|
|
6402
6690
|
log(`[claude-scheduler] Task '${task.name}' stderr for '${codeName}': ${stderr.slice(0, 500)}`);
|
|
@@ -7170,9 +7458,9 @@ ${escapeXml(msg.content)}
|
|
|
7170
7458
|
const projDir = ccProjectDir(agent.codeName);
|
|
7171
7459
|
const mcpConfigPath = join6(projDir, ".mcp.json");
|
|
7172
7460
|
const serverNames = [];
|
|
7173
|
-
if (
|
|
7461
|
+
if (existsSync5(mcpConfigPath)) {
|
|
7174
7462
|
try {
|
|
7175
|
-
const d = JSON.parse(
|
|
7463
|
+
const d = JSON.parse(readFileSync7(mcpConfigPath, "utf-8"));
|
|
7176
7464
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
7177
7465
|
} catch {
|
|
7178
7466
|
}
|
|
@@ -7192,14 +7480,14 @@ ${escapeXml(msg.content)}
|
|
|
7192
7480
|
allowedTools
|
|
7193
7481
|
];
|
|
7194
7482
|
const chatClaudeMd = join6(projDir, "CLAUDE.md");
|
|
7195
|
-
if (
|
|
7483
|
+
if (existsSync5(chatClaudeMd)) {
|
|
7196
7484
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
7197
7485
|
}
|
|
7198
7486
|
const envIntPath = join6(projDir, ".env.integrations");
|
|
7199
7487
|
const childEnv = { ...process.env };
|
|
7200
|
-
if (
|
|
7488
|
+
if (existsSync5(envIntPath)) {
|
|
7201
7489
|
try {
|
|
7202
|
-
for (const line of
|
|
7490
|
+
for (const line of readFileSync7(envIntPath, "utf-8").split("\n")) {
|
|
7203
7491
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
7204
7492
|
const eqIdx = line.indexOf("=");
|
|
7205
7493
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -7212,7 +7500,19 @@ ${escapeXml(msg.content)}
|
|
|
7212
7500
|
} catch (err) {
|
|
7213
7501
|
throw new Error(`Auth resolve failed for '${agent.codeName}': ${err.message}`);
|
|
7214
7502
|
}
|
|
7215
|
-
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), chatArgs, {
|
|
7503
|
+
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), chatArgs, {
|
|
7504
|
+
cwd: projDir,
|
|
7505
|
+
stdin: "ignore",
|
|
7506
|
+
env: childEnv,
|
|
7507
|
+
onSpawn: (pid) => registerClaudeSpawn({
|
|
7508
|
+
pid,
|
|
7509
|
+
started_at: Date.now(),
|
|
7510
|
+
kind: "direct-chat",
|
|
7511
|
+
agent_id: agent.agentId,
|
|
7512
|
+
agent_code_name: agent.codeName
|
|
7513
|
+
}),
|
|
7514
|
+
onExit: (pid) => unregisterClaudeSpawn(pid)
|
|
7515
|
+
});
|
|
7216
7516
|
reply = stdout.trim() || "[No response from agent]";
|
|
7217
7517
|
} else {
|
|
7218
7518
|
const { stdout } = await execFilePromiseLong("openclaw", [
|
|
@@ -7564,8 +7864,8 @@ function getBuiltInSkillContent(skillId) {
|
|
|
7564
7864
|
join6(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
7565
7865
|
];
|
|
7566
7866
|
for (const candidate of candidates) {
|
|
7567
|
-
if (
|
|
7568
|
-
const content =
|
|
7867
|
+
if (existsSync5(candidate)) {
|
|
7868
|
+
const content = readFileSync7(candidate, "utf-8");
|
|
7569
7869
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
7570
7870
|
builtInSkillCache.set(skillId, files);
|
|
7571
7871
|
return files;
|
|
@@ -7736,6 +8036,12 @@ async function execFilePromiseLong(cmd, args, opts) {
|
|
|
7736
8036
|
stdio: [opts?.stdin === "ignore" ? "ignore" : "pipe", "pipe", "pipe"],
|
|
7737
8037
|
...opts?.env ? { env: opts.env } : {}
|
|
7738
8038
|
});
|
|
8039
|
+
if (opts?.onSpawn && typeof child.pid === "number") {
|
|
8040
|
+
try {
|
|
8041
|
+
opts.onSpawn(child.pid);
|
|
8042
|
+
} catch {
|
|
8043
|
+
}
|
|
8044
|
+
}
|
|
7739
8045
|
let stdout = "";
|
|
7740
8046
|
let stderr = "";
|
|
7741
8047
|
child.stdout?.on("data", (d) => {
|
|
@@ -7750,6 +8056,12 @@ async function execFilePromiseLong(cmd, args, opts) {
|
|
|
7750
8056
|
}, opts?.timeout ?? 12e4);
|
|
7751
8057
|
child.on("close", (code) => {
|
|
7752
8058
|
clearTimeout(timer);
|
|
8059
|
+
if (opts?.onExit && typeof child.pid === "number") {
|
|
8060
|
+
try {
|
|
8061
|
+
opts.onExit(child.pid);
|
|
8062
|
+
} catch {
|
|
8063
|
+
}
|
|
8064
|
+
}
|
|
7753
8065
|
if (code !== 0) reject(new ChildProcessError(code, stdout, stderr));
|
|
7754
8066
|
else resolve({ stdout, stderr });
|
|
7755
8067
|
});
|
|
@@ -8196,7 +8508,7 @@ async function processClaudePairSessions(agents) {
|
|
|
8196
8508
|
killPairSession,
|
|
8197
8509
|
pairTmuxSession,
|
|
8198
8510
|
finalizeClaudePairOnboarding
|
|
8199
|
-
} = await import("../claude-pair-runtime-
|
|
8511
|
+
} = await import("../claude-pair-runtime-3UHTNKZ4.js");
|
|
8200
8512
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
8201
8513
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
8202
8514
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -8518,14 +8830,14 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
8518
8830
|
}
|
|
8519
8831
|
pendingFreshMemorySync.delete(agent.agent_id);
|
|
8520
8832
|
}
|
|
8521
|
-
if (
|
|
8833
|
+
if (existsSync5(memoryDir)) {
|
|
8522
8834
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
8523
8835
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
8524
8836
|
const changedMemories = [];
|
|
8525
8837
|
for (const file of readdirSync3(memoryDir)) {
|
|
8526
8838
|
if (!file.endsWith(".md")) continue;
|
|
8527
8839
|
try {
|
|
8528
|
-
const raw =
|
|
8840
|
+
const raw = readFileSync7(join6(memoryDir, file), "utf-8");
|
|
8529
8841
|
const fileHash = createHash3("sha256").update(raw).digest("hex").slice(0, 16);
|
|
8530
8842
|
currentHashes.set(file, fileHash);
|
|
8531
8843
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -8550,7 +8862,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
8550
8862
|
} catch (err) {
|
|
8551
8863
|
for (const mem of changedMemories) {
|
|
8552
8864
|
for (const [file] of currentHashes) {
|
|
8553
|
-
const parsed = parseMemoryFile(
|
|
8865
|
+
const parsed = parseMemoryFile(readFileSync7(join6(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
8554
8866
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
8555
8867
|
}
|
|
8556
8868
|
}
|
|
@@ -8563,7 +8875,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
8563
8875
|
}
|
|
8564
8876
|
}
|
|
8565
8877
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
8566
|
-
const localFiles =
|
|
8878
|
+
const localFiles = existsSync5(memoryDir) ? readdirSync3(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
8567
8879
|
const localListHash = createHash3("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
8568
8880
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
8569
8881
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
@@ -8578,7 +8890,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
|
8578
8890
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
8579
8891
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
8580
8892
|
if (dbMemories.memories?.length) {
|
|
8581
|
-
|
|
8893
|
+
mkdirSync4(memoryDir, { recursive: true });
|
|
8582
8894
|
let written = 0;
|
|
8583
8895
|
let overwritten = 0;
|
|
8584
8896
|
for (let i = 0; i < dbMemories.memories.length; i++) {
|
|
@@ -8594,17 +8906,17 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
|
|
|
8594
8906
|
|
|
8595
8907
|
${mem.content}
|
|
8596
8908
|
`;
|
|
8597
|
-
if (
|
|
8909
|
+
if (existsSync5(filePath)) {
|
|
8598
8910
|
let existing = "";
|
|
8599
8911
|
try {
|
|
8600
|
-
existing =
|
|
8912
|
+
existing = readFileSync7(filePath, "utf-8");
|
|
8601
8913
|
} catch {
|
|
8602
8914
|
}
|
|
8603
8915
|
if (existing === desired) continue;
|
|
8604
|
-
|
|
8916
|
+
writeFileSync4(filePath, desired);
|
|
8605
8917
|
overwritten++;
|
|
8606
8918
|
} else {
|
|
8607
|
-
|
|
8919
|
+
writeFileSync4(filePath, desired);
|
|
8608
8920
|
written++;
|
|
8609
8921
|
}
|
|
8610
8922
|
}
|
|
@@ -8621,7 +8933,7 @@ ${mem.content}
|
|
|
8621
8933
|
}
|
|
8622
8934
|
}
|
|
8623
8935
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
8624
|
-
if (
|
|
8936
|
+
if (existsSync5(agentDir)) {
|
|
8625
8937
|
try {
|
|
8626
8938
|
rmSync2(agentDir, { recursive: true, force: true });
|
|
8627
8939
|
log(`Removed provision directory for '${codeName}'`);
|
|
@@ -8777,9 +9089,24 @@ async function stopPolling(opts = {}) {
|
|
|
8777
9089
|
process.exit(opts.forcedExitCode ?? 1);
|
|
8778
9090
|
}, 15e3);
|
|
8779
9091
|
shutdownTimer.unref();
|
|
9092
|
+
const drainStartedAt = Date.now();
|
|
9093
|
+
const livePids = [...inFlightClaudePids.keys()];
|
|
9094
|
+
if (livePids.length > 0) {
|
|
9095
|
+
for (const pid of livePids) {
|
|
9096
|
+
try {
|
|
9097
|
+
process.kill(pid, "SIGTERM");
|
|
9098
|
+
} catch {
|
|
9099
|
+
}
|
|
9100
|
+
}
|
|
9101
|
+
log(formatDrainShutdownLine({ step: "sigterm-claude-pids", elapsedMs: Date.now() - drainStartedAt, killedPids: livePids }));
|
|
9102
|
+
} else {
|
|
9103
|
+
log(formatDrainShutdownLine({ step: "sigterm-claude-pids", elapsedMs: 0, note: "no in-flight claude -p children" }));
|
|
9104
|
+
}
|
|
9105
|
+
const subsysStartedAt = Date.now();
|
|
8780
9106
|
stopCaffeinate();
|
|
8781
9107
|
stopRealtimeChat();
|
|
8782
9108
|
stopGatewayPool();
|
|
9109
|
+
log(formatDrainShutdownLine({ step: "stop-subsystems", elapsedMs: Date.now() - subsysStartedAt }));
|
|
8783
9110
|
for (const codeName of [...scheduledRunsByCode.keys()]) {
|
|
8784
9111
|
closeScheduledRunsForCode(codeName, "cancelled", "manager shutdown");
|
|
8785
9112
|
}
|
|
@@ -8795,8 +9122,8 @@ function startManager(opts) {
|
|
|
8795
9122
|
config = opts;
|
|
8796
9123
|
try {
|
|
8797
9124
|
const stateFile = getStateFile();
|
|
8798
|
-
if (
|
|
8799
|
-
const raw =
|
|
9125
|
+
if (existsSync5(stateFile)) {
|
|
9126
|
+
const raw = readFileSync7(stateFile, "utf-8");
|
|
8800
9127
|
const parsed = JSON.parse(raw);
|
|
8801
9128
|
if (Array.isArray(parsed.agents)) {
|
|
8802
9129
|
state4.agents = parsed.agents;
|
|
@@ -8816,8 +9143,77 @@ function startManager(opts) {
|
|
|
8816
9143
|
);
|
|
8817
9144
|
deployMcpAssets();
|
|
8818
9145
|
reapOrphanChannelMcps({ log });
|
|
8819
|
-
void
|
|
8820
|
-
|
|
9146
|
+
void (async () => {
|
|
9147
|
+
try {
|
|
9148
|
+
await reapOrphanedClaudePids();
|
|
9149
|
+
} catch (err) {
|
|
9150
|
+
log(`[drain] boot reaper failed: ${err.message}`);
|
|
9151
|
+
}
|
|
9152
|
+
void ensureHostFrameworkBinaries();
|
|
9153
|
+
startPolling();
|
|
9154
|
+
})();
|
|
9155
|
+
}
|
|
9156
|
+
async function reapOrphanedClaudePids() {
|
|
9157
|
+
const path = claudePidFilePath();
|
|
9158
|
+
const file = readPidFile(path);
|
|
9159
|
+
if (file.spawns.length === 0) return;
|
|
9160
|
+
const looksLikeClaude = (pid) => {
|
|
9161
|
+
if (process.platform !== "linux") return true;
|
|
9162
|
+
try {
|
|
9163
|
+
const comm = readFileSync7(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
9164
|
+
return comm.includes("claude");
|
|
9165
|
+
} catch {
|
|
9166
|
+
return false;
|
|
9167
|
+
}
|
|
9168
|
+
};
|
|
9169
|
+
const decision = decideReaperActions(
|
|
9170
|
+
file,
|
|
9171
|
+
(pid) => {
|
|
9172
|
+
try {
|
|
9173
|
+
process.kill(pid, 0);
|
|
9174
|
+
return true;
|
|
9175
|
+
} catch {
|
|
9176
|
+
return false;
|
|
9177
|
+
}
|
|
9178
|
+
},
|
|
9179
|
+
looksLikeClaude
|
|
9180
|
+
);
|
|
9181
|
+
for (const spawn of decision.toKill) {
|
|
9182
|
+
try {
|
|
9183
|
+
process.kill(spawn.pid, "SIGKILL");
|
|
9184
|
+
} catch (err) {
|
|
9185
|
+
log(`[drain] reaper SIGKILL pid=${spawn.pid} failed: ${err.message}`);
|
|
9186
|
+
}
|
|
9187
|
+
}
|
|
9188
|
+
for (const spawn of decision.pidReusedSkipped) {
|
|
9189
|
+
log(
|
|
9190
|
+
`[drain] reaper skipped pid=${spawn.pid} agent=${spawn.agent_code_name} card=${spawn.kanban_card_id ?? "none"} \u2014 PID reuse suspected (identity probe rejected)`
|
|
9191
|
+
);
|
|
9192
|
+
}
|
|
9193
|
+
let kanbanReset = 0;
|
|
9194
|
+
for (const spawn of decision.toResetKanban) {
|
|
9195
|
+
try {
|
|
9196
|
+
await api.post("/host/kanban", {
|
|
9197
|
+
agent_id: spawn.agent_id,
|
|
9198
|
+
update: [{ id: spawn.kanban_card_id, status: "backlog" }]
|
|
9199
|
+
});
|
|
9200
|
+
kanbanReset += 1;
|
|
9201
|
+
} catch (err) {
|
|
9202
|
+
log(`[drain] reaper kanban-reset failed for card=${spawn.kanban_card_id} agent=${spawn.agent_code_name}: ${err.message}`);
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
9205
|
+
log(formatReaperBootLine({
|
|
9206
|
+
totalRecorded: file.spawns.length,
|
|
9207
|
+
killed: decision.toKill.length,
|
|
9208
|
+
alreadyDead: decision.alreadyDead.length,
|
|
9209
|
+
pidReusedSkipped: decision.pidReusedSkipped.length,
|
|
9210
|
+
kanbanReset
|
|
9211
|
+
}));
|
|
9212
|
+
try {
|
|
9213
|
+
writePidFile(path, { version: 1, spawns: [] });
|
|
9214
|
+
} catch (err) {
|
|
9215
|
+
log(`[drain] reaper file truncate failed: ${err.message}`);
|
|
9216
|
+
}
|
|
8821
9217
|
}
|
|
8822
9218
|
async function ensureHostFrameworkBinaries() {
|
|
8823
9219
|
const apiKey = getApiKey();
|
|
@@ -8875,17 +9271,17 @@ function restartRunningChannelMcps(basenames) {
|
|
|
8875
9271
|
}
|
|
8876
9272
|
function deployMcpAssets() {
|
|
8877
9273
|
const targetDir = join6(homedir4(), ".augmented", "_mcp");
|
|
8878
|
-
|
|
8879
|
-
const moduleDir =
|
|
9274
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
9275
|
+
const moduleDir = dirname3(fileURLToPath(import.meta.url));
|
|
8880
9276
|
let mcpSourceDir = "";
|
|
8881
9277
|
let dir = moduleDir;
|
|
8882
9278
|
for (let i = 0; i < 6; i++) {
|
|
8883
9279
|
const candidate = join6(dir, "dist", "mcp");
|
|
8884
|
-
if (
|
|
9280
|
+
if (existsSync5(join6(candidate, "index.js"))) {
|
|
8885
9281
|
mcpSourceDir = candidate;
|
|
8886
9282
|
break;
|
|
8887
9283
|
}
|
|
8888
|
-
const parent =
|
|
9284
|
+
const parent = dirname3(dir);
|
|
8889
9285
|
if (parent === dir) break;
|
|
8890
9286
|
dir = parent;
|
|
8891
9287
|
}
|
|
@@ -8896,8 +9292,8 @@ function deployMcpAssets() {
|
|
|
8896
9292
|
const changedBasenames = [];
|
|
8897
9293
|
const fileHash = (p) => {
|
|
8898
9294
|
try {
|
|
8899
|
-
if (!
|
|
8900
|
-
return createHash3("sha256").update(
|
|
9295
|
+
if (!existsSync5(p)) return null;
|
|
9296
|
+
return createHash3("sha256").update(readFileSync7(p)).digest("hex");
|
|
8901
9297
|
} catch {
|
|
8902
9298
|
return null;
|
|
8903
9299
|
}
|
|
@@ -8910,7 +9306,7 @@ function deployMcpAssets() {
|
|
|
8910
9306
|
for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
|
|
8911
9307
|
const src = join6(mcpSourceDir, file);
|
|
8912
9308
|
const dst = join6(targetDir, file);
|
|
8913
|
-
if (!
|
|
9309
|
+
if (!existsSync5(src)) continue;
|
|
8914
9310
|
const before = fileHash(dst);
|
|
8915
9311
|
try {
|
|
8916
9312
|
copyFileSync(src, dst);
|
|
@@ -8930,20 +9326,20 @@ function deployMcpAssets() {
|
|
|
8930
9326
|
const localMcpPath = join6(targetDir, "index.js");
|
|
8931
9327
|
try {
|
|
8932
9328
|
const agentsDir = join6(homedir4(), ".augmented", "agents");
|
|
8933
|
-
if (
|
|
9329
|
+
if (existsSync5(agentsDir)) {
|
|
8934
9330
|
for (const entry of readdirSync3(agentsDir, { withFileTypes: true })) {
|
|
8935
9331
|
if (!entry.isDirectory()) continue;
|
|
8936
9332
|
for (const subdir of ["provision", "project"]) {
|
|
8937
9333
|
const mcpJsonPath = join6(agentsDir, entry.name, subdir, ".mcp.json");
|
|
8938
9334
|
try {
|
|
8939
|
-
const raw =
|
|
9335
|
+
const raw = readFileSync7(mcpJsonPath, "utf-8");
|
|
8940
9336
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
8941
9337
|
const mcpConfig = JSON.parse(raw);
|
|
8942
9338
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|
|
8943
9339
|
if (!augServer) continue;
|
|
8944
9340
|
augServer.command = "node";
|
|
8945
9341
|
augServer.args = [localMcpPath];
|
|
8946
|
-
|
|
9342
|
+
writeFileSync4(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
|
|
8947
9343
|
log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
|
|
8948
9344
|
} catch {
|
|
8949
9345
|
}
|