@integrity-labs/agt-cli 0.24.0 → 0.24.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.
- package/dist/bin/agt.js +4 -4
- package/dist/{chunk-HSIESZMZ.js → chunk-ECDTRQLA.js} +89 -1
- package/dist/chunk-ECDTRQLA.js.map +1 -0
- package/dist/{chunk-WZTRMJM4.js → chunk-KVIKKWXB.js} +2 -2
- package/dist/{chunk-ZKQGDH3T.js → chunk-PNTLQKLO.js} +7 -9
- package/dist/{chunk-ZKQGDH3T.js.map → chunk-PNTLQKLO.js.map} +1 -1
- package/dist/{claude-pair-runtime-XCFWTH6T.js → claude-pair-runtime-ZQAJOQPN.js} +2 -2
- package/dist/lib/manager-worker.js +325 -215
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/{persistent-session-L4YIJJML.js → persistent-session-JUJY46HW.js} +3 -3
- package/dist/{responsiveness-probe-YRLESO54.js → responsiveness-probe-XOEC7J5D.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-HSIESZMZ.js.map +0 -1
- /package/dist/{chunk-WZTRMJM4.js.map → chunk-KVIKKWXB.js.map} +0 -0
- /package/dist/{claude-pair-runtime-XCFWTH6T.js.map → claude-pair-runtime-ZQAJOQPN.js.map} +0 -0
- /package/dist/{persistent-session-L4YIJJML.js.map → persistent-session-JUJY46HW.js.map} +0 -0
- /package/dist/{responsiveness-probe-YRLESO54.js.map → responsiveness-probe-XOEC7J5D.js.map} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
provisionOrientHook,
|
|
13
13
|
provisionStopHook,
|
|
14
14
|
requireHost
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-KVIKKWXB.js";
|
|
16
16
|
import {
|
|
17
17
|
findTaskByTemplate,
|
|
18
18
|
getProjectDir as getProjectDir2,
|
|
@@ -43,11 +43,12 @@ import {
|
|
|
43
43
|
resolveClaudeBinary,
|
|
44
44
|
sanitizeMcpJson,
|
|
45
45
|
sendToAgent,
|
|
46
|
+
sessionTranscriptDir,
|
|
46
47
|
startPersistentSession,
|
|
47
48
|
stopAllSessionsAndWait,
|
|
48
49
|
stopPersistentSession,
|
|
49
50
|
takeZombieDetection
|
|
50
|
-
} from "../chunk-
|
|
51
|
+
} from "../chunk-PNTLQKLO.js";
|
|
51
52
|
import {
|
|
52
53
|
KANBAN_CHECK_COMMAND,
|
|
53
54
|
appendDmFooter,
|
|
@@ -55,22 +56,24 @@ import {
|
|
|
55
56
|
extractFrontmatter,
|
|
56
57
|
formatActorId,
|
|
57
58
|
getFramework,
|
|
59
|
+
isEmptyTotals,
|
|
58
60
|
isParseError,
|
|
59
61
|
isResolveError,
|
|
60
62
|
isSelfCompletion,
|
|
61
63
|
parseDeliveryTarget,
|
|
64
|
+
parseTranscriptUsage,
|
|
62
65
|
parseUsageBanner,
|
|
63
66
|
resolveChannels,
|
|
64
67
|
resolveDmTarget,
|
|
65
68
|
wrapScheduledTaskPrompt
|
|
66
|
-
} from "../chunk-
|
|
69
|
+
} from "../chunk-ECDTRQLA.js";
|
|
67
70
|
|
|
68
71
|
// src/lib/manager-worker.ts
|
|
69
72
|
import { createHash as createHash2 } from "crypto";
|
|
70
|
-
import { readFileSync as
|
|
73
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, appendFileSync, mkdirSync as mkdirSync2, chmodSync, existsSync as existsSync4, rmSync as rmSync2, readdirSync as readdirSync3, statSync as statSync2, unlinkSync, copyFileSync } from "fs";
|
|
71
74
|
import https from "https";
|
|
72
75
|
import { execFileSync as syncExecFile } from "child_process";
|
|
73
|
-
import { join as
|
|
76
|
+
import { join as join6, dirname } from "path";
|
|
74
77
|
import { homedir as homedir4 } from "os";
|
|
75
78
|
import { fileURLToPath } from "url";
|
|
76
79
|
|
|
@@ -435,12 +438,12 @@ function reapMissingMcpSessions(args) {
|
|
|
435
438
|
if (!missingRaw.includes(key)) liveKeys.add(key);
|
|
436
439
|
}
|
|
437
440
|
for (const key of liveKeys) {
|
|
438
|
-
const
|
|
439
|
-
if (
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
441
|
+
const state5 = presenceReaperState.get(stateKey(codeName, key));
|
|
442
|
+
if (state5) {
|
|
443
|
+
state5.attempts = 0;
|
|
444
|
+
state5.lastSeenLiveAt = now();
|
|
445
|
+
state5.lastAttemptedSessionStartedAt = null;
|
|
446
|
+
state5.gaveUpLogged = false;
|
|
444
447
|
}
|
|
445
448
|
}
|
|
446
449
|
if (missing.length === 0) {
|
|
@@ -455,24 +458,24 @@ function reapMissingMcpSessions(args) {
|
|
|
455
458
|
const active = [];
|
|
456
459
|
for (const key of missing) {
|
|
457
460
|
const sk = stateKey(codeName, key);
|
|
458
|
-
const
|
|
461
|
+
const state5 = presenceReaperState.get(sk) ?? {
|
|
459
462
|
attempts: 0,
|
|
460
463
|
lastSeenLiveAt: null,
|
|
461
464
|
lastAttemptedSessionStartedAt: null,
|
|
462
465
|
gaveUpLogged: false
|
|
463
466
|
};
|
|
464
|
-
if (
|
|
465
|
-
|
|
466
|
-
|
|
467
|
+
if (state5.lastAttemptedSessionStartedAt !== sessionStartedAt) {
|
|
468
|
+
state5.attempts += 1;
|
|
469
|
+
state5.lastAttemptedSessionStartedAt = sessionStartedAt;
|
|
467
470
|
}
|
|
468
|
-
presenceReaperState.set(sk,
|
|
469
|
-
if (
|
|
471
|
+
presenceReaperState.set(sk, state5);
|
|
472
|
+
if (state5.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
|
|
470
473
|
givenUp.push(key);
|
|
471
|
-
if (!
|
|
474
|
+
if (!state5.gaveUpLogged) {
|
|
472
475
|
log2(
|
|
473
476
|
`[mcp-presence-reaper] giving up on '${codeName}:${key}' after ${MAX_PRESENCE_RESTART_ATTEMPTS} consecutive failed restarts \u2014 declared but never recovers (likely orphaned config; see ENG-5279)`
|
|
474
477
|
);
|
|
475
|
-
|
|
478
|
+
state5.gaveUpLogged = true;
|
|
476
479
|
if (onGiveUp) {
|
|
477
480
|
try {
|
|
478
481
|
onGiveUp(codeName, key);
|
|
@@ -710,14 +713,115 @@ async function maybeReportUsageBanner(args) {
|
|
|
710
713
|
}
|
|
711
714
|
}
|
|
712
715
|
|
|
713
|
-
// src/lib/
|
|
714
|
-
import {
|
|
715
|
-
import { homedir } from "os";
|
|
716
|
+
// src/lib/token-usage-monitor.ts
|
|
717
|
+
import { readdirSync, readFileSync, statSync } from "fs";
|
|
716
718
|
import { join } from "path";
|
|
717
719
|
var MIN_CHECK_INTERVAL_MS2 = 6e4;
|
|
718
|
-
var
|
|
720
|
+
var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
721
|
+
var MAX_ENTRIES_PER_POST = 200;
|
|
722
|
+
var state2 = /* @__PURE__ */ new Map();
|
|
723
|
+
async function maybeReportTokenUsage(args) {
|
|
724
|
+
const { api: api2, codeName, agentId, log: log2 } = args;
|
|
725
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
726
|
+
const nowMs = now.getTime();
|
|
727
|
+
const existing = state2.get(codeName);
|
|
728
|
+
if (existing && nowMs - existing.lastCheckedAt < MIN_CHECK_INTERVAL_MS2) {
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
const dir = args.transcriptDir ?? sessionTranscriptDir(getProjectDir(codeName));
|
|
732
|
+
const files = existing?.files ?? /* @__PURE__ */ new Map();
|
|
733
|
+
const next = { files, lastCheckedAt: nowMs };
|
|
734
|
+
let dirEntries;
|
|
735
|
+
try {
|
|
736
|
+
dirEntries = readdirSync(dir);
|
|
737
|
+
} catch {
|
|
738
|
+
state2.set(codeName, next);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const pending = [];
|
|
742
|
+
for (const name of dirEntries) {
|
|
743
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
744
|
+
const sessionId = name.slice(0, -".jsonl".length);
|
|
745
|
+
if (!sessionId) continue;
|
|
746
|
+
const path = join(dir, name);
|
|
747
|
+
let st;
|
|
748
|
+
try {
|
|
749
|
+
st = statSync(path);
|
|
750
|
+
} catch {
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
if (!st.isFile()) continue;
|
|
754
|
+
if (nowMs - st.mtimeMs > TRANSCRIPT_MTIME_WINDOW_MS) continue;
|
|
755
|
+
const fp = { mtimeMs: st.mtimeMs, size: st.size };
|
|
756
|
+
const seen = files.get(sessionId);
|
|
757
|
+
if (seen && seen.mtimeMs === fp.mtimeMs && seen.size === fp.size) {
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
let content;
|
|
761
|
+
try {
|
|
762
|
+
content = readFileSync(path, "utf-8");
|
|
763
|
+
} catch (err) {
|
|
764
|
+
log2(`[token-usage] read failed for '${codeName}/${name}': ${err.message}`);
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
const parsed = parseTranscriptUsage(content);
|
|
768
|
+
for (const [model, totals] of parsed.byModel) {
|
|
769
|
+
if (isEmptyTotals(totals)) continue;
|
|
770
|
+
pending.push({
|
|
771
|
+
sessionId,
|
|
772
|
+
fp,
|
|
773
|
+
entry: {
|
|
774
|
+
session_id: sessionId,
|
|
775
|
+
model,
|
|
776
|
+
input_tokens: totals.inputTokens,
|
|
777
|
+
output_tokens: totals.outputTokens,
|
|
778
|
+
cache_creation_tokens: totals.cacheCreationTokens,
|
|
779
|
+
cache_read_tokens: totals.cacheReadTokens,
|
|
780
|
+
session_started_at: parsed.sessionStartedAt,
|
|
781
|
+
last_observed_at: parsed.lastObservedAt
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
if (![...parsed.byModel.values()].some((t) => !isEmptyTotals(t))) {
|
|
786
|
+
files.set(sessionId, fp);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (pending.length === 0) {
|
|
790
|
+
state2.set(codeName, next);
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
let reported = 0;
|
|
794
|
+
for (let i = 0; i < pending.length; i += MAX_ENTRIES_PER_POST) {
|
|
795
|
+
const batch = pending.slice(i, i + MAX_ENTRIES_PER_POST);
|
|
796
|
+
try {
|
|
797
|
+
await api2.post("/host/token-usage", {
|
|
798
|
+
agent_id: agentId,
|
|
799
|
+
framework: "claude-code",
|
|
800
|
+
source: "transcript_jsonl",
|
|
801
|
+
entries: batch.map((b) => b.entry)
|
|
802
|
+
});
|
|
803
|
+
for (const b of batch) {
|
|
804
|
+
files.set(b.sessionId, b.fp);
|
|
805
|
+
}
|
|
806
|
+
reported += batch.length;
|
|
807
|
+
} catch (err) {
|
|
808
|
+
log2(`[token-usage] POST failed for '${codeName}': ${err.message}`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (reported > 0) {
|
|
812
|
+
log2(`[token-usage] reported ${reported} entr${reported === 1 ? "y" : "ies"} for '${codeName}'`);
|
|
813
|
+
}
|
|
814
|
+
state2.set(codeName, next);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/lib/activity-cache-monitor.ts
|
|
818
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
819
|
+
import { homedir } from "os";
|
|
820
|
+
import { join as join2 } from "path";
|
|
821
|
+
var MIN_CHECK_INTERVAL_MS3 = 6e4;
|
|
822
|
+
var STATS_CACHE_PATH = join2(homedir(), ".claude", "stats-cache.json");
|
|
719
823
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
720
|
-
var
|
|
824
|
+
var state3 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
721
825
|
function selectNewDailyRows(raw, lastObservedDate) {
|
|
722
826
|
let parsed;
|
|
723
827
|
try {
|
|
@@ -756,24 +860,24 @@ async function maybeReportActivityCache(args) {
|
|
|
756
860
|
const { api: api2, log: log2 } = args;
|
|
757
861
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
758
862
|
const nowMs = now.getTime();
|
|
759
|
-
if (nowMs -
|
|
760
|
-
|
|
863
|
+
if (nowMs - state3.lastCheckedAt < MIN_CHECK_INTERVAL_MS3) return;
|
|
864
|
+
state3.lastCheckedAt = nowMs;
|
|
761
865
|
if (!existsSync(STATS_CACHE_PATH)) {
|
|
762
866
|
return;
|
|
763
867
|
}
|
|
764
868
|
let raw;
|
|
765
869
|
try {
|
|
766
|
-
raw =
|
|
870
|
+
raw = readFileSync2(STATS_CACHE_PATH, "utf-8");
|
|
767
871
|
} catch (err) {
|
|
768
872
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
769
873
|
return;
|
|
770
874
|
}
|
|
771
|
-
const rows = selectNewDailyRows(raw,
|
|
875
|
+
const rows = selectNewDailyRows(raw, state3.lastObservedDate);
|
|
772
876
|
if (rows.length === 0) return;
|
|
773
877
|
for (const row of rows) {
|
|
774
878
|
try {
|
|
775
879
|
await api2.post("/host/activity-observations", row);
|
|
776
|
-
|
|
880
|
+
state3.lastObservedDate = row.date;
|
|
777
881
|
} catch (err) {
|
|
778
882
|
log2(
|
|
779
883
|
`[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
|
|
@@ -1213,7 +1317,7 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
1213
1317
|
// src/lib/claude-auth-detect.ts
|
|
1214
1318
|
import { readFile, readdir } from "fs/promises";
|
|
1215
1319
|
import { homedir as homedir2, platform } from "os";
|
|
1216
|
-
import { join as
|
|
1320
|
+
import { join as join3 } from "path";
|
|
1217
1321
|
import { execFile } from "child_process";
|
|
1218
1322
|
import { promisify } from "util";
|
|
1219
1323
|
var execFileAsync = promisify(execFile);
|
|
@@ -1228,8 +1332,8 @@ async function detectClaudeAuth() {
|
|
|
1228
1332
|
}
|
|
1229
1333
|
async function findClaudeCredentialsPaths() {
|
|
1230
1334
|
const candidates = [
|
|
1231
|
-
|
|
1232
|
-
|
|
1335
|
+
join3(homedir2(), ".claude", ".credentials.json"),
|
|
1336
|
+
join3(homedir2(), ".claude", "credentials.json")
|
|
1233
1337
|
];
|
|
1234
1338
|
const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
|
|
1235
1339
|
if (isLinuxRoot) {
|
|
@@ -1237,8 +1341,8 @@ async function findClaudeCredentialsPaths() {
|
|
|
1237
1341
|
const entries = await readdir("/home", { withFileTypes: true });
|
|
1238
1342
|
for (const entry of entries) {
|
|
1239
1343
|
if (!entry.isDirectory()) continue;
|
|
1240
|
-
candidates.push(
|
|
1241
|
-
candidates.push(
|
|
1344
|
+
candidates.push(join3("/home", entry.name, ".claude", ".credentials.json"));
|
|
1345
|
+
candidates.push(join3("/home", entry.name, ".claude", "credentials.json"));
|
|
1242
1346
|
}
|
|
1243
1347
|
} catch {
|
|
1244
1348
|
}
|
|
@@ -1330,18 +1434,18 @@ function normalize(value) {
|
|
|
1330
1434
|
}
|
|
1331
1435
|
|
|
1332
1436
|
// src/lib/channel-hash-cache.ts
|
|
1333
|
-
import { existsSync as existsSync2, readFileSync as
|
|
1334
|
-
import { join as
|
|
1437
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1438
|
+
import { join as join4 } from "path";
|
|
1335
1439
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
1336
1440
|
function getChannelHashCacheFile(configDir) {
|
|
1337
|
-
return
|
|
1441
|
+
return join4(configDir, CACHE_FILENAME);
|
|
1338
1442
|
}
|
|
1339
1443
|
function loadChannelHashCache(target, configDir) {
|
|
1340
1444
|
const path = getChannelHashCacheFile(configDir);
|
|
1341
1445
|
if (!existsSync2(path)) return;
|
|
1342
1446
|
let parsed;
|
|
1343
1447
|
try {
|
|
1344
|
-
parsed = JSON.parse(
|
|
1448
|
+
parsed = JSON.parse(readFileSync3(path, "utf-8"));
|
|
1345
1449
|
} catch {
|
|
1346
1450
|
return;
|
|
1347
1451
|
}
|
|
@@ -1814,24 +1918,24 @@ function withScheduleLinkFooter(opts) {
|
|
|
1814
1918
|
}
|
|
1815
1919
|
|
|
1816
1920
|
// src/lib/restart-flags.ts
|
|
1817
|
-
import { existsSync as existsSync3, mkdirSync, readdirSync, readFileSync as
|
|
1921
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync4, renameSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1818
1922
|
import { homedir as homedir3 } from "os";
|
|
1819
|
-
import { join as
|
|
1923
|
+
import { join as join5 } from "path";
|
|
1820
1924
|
import { randomUUID } from "crypto";
|
|
1821
1925
|
function restartFlagsDir() {
|
|
1822
|
-
return
|
|
1926
|
+
return join5(homedir3(), ".augmented", "restart-flags");
|
|
1823
1927
|
}
|
|
1824
1928
|
function flagPath(codeName) {
|
|
1825
|
-
return
|
|
1929
|
+
return join5(restartFlagsDir(), `${codeName}.flag`);
|
|
1826
1930
|
}
|
|
1827
1931
|
function readRestartFlags() {
|
|
1828
1932
|
const dir = restartFlagsDir();
|
|
1829
1933
|
if (!existsSync3(dir)) return [];
|
|
1830
1934
|
const out = [];
|
|
1831
|
-
for (const entry of
|
|
1935
|
+
for (const entry of readdirSync2(dir)) {
|
|
1832
1936
|
if (!entry.endsWith(".flag")) continue;
|
|
1833
1937
|
try {
|
|
1834
|
-
const raw =
|
|
1938
|
+
const raw = readFileSync4(join5(dir, entry), "utf8");
|
|
1835
1939
|
const parsed = JSON.parse(raw);
|
|
1836
1940
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
1837
1941
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -2347,8 +2451,8 @@ function applyRestartAcks(args) {
|
|
|
2347
2451
|
var GATEWAY_PORT_BASE = 18800;
|
|
2348
2452
|
var GATEWAY_PORT_STEP = 10;
|
|
2349
2453
|
var GATEWAY_PORT_MAX = 18899;
|
|
2350
|
-
var AUGMENTED_DIR =
|
|
2351
|
-
var GATEWAY_PORTS_FILE =
|
|
2454
|
+
var AUGMENTED_DIR = join6(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
2455
|
+
var GATEWAY_PORTS_FILE = join6(AUGMENTED_DIR, "gateway-ports.json");
|
|
2352
2456
|
var CHANNEL_SWEEP_INTERVAL_MS = (() => {
|
|
2353
2457
|
const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
|
|
2354
2458
|
if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
|
|
@@ -2455,8 +2559,8 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
|
|
|
2455
2559
|
function shouldSkipRevokedCleanup(previousKnownStatus) {
|
|
2456
2560
|
return previousKnownStatus === "revoked";
|
|
2457
2561
|
}
|
|
2458
|
-
function hasRevokedResiduals(
|
|
2459
|
-
return
|
|
2562
|
+
function hasRevokedResiduals(state5) {
|
|
2563
|
+
return state5.gatewayRunning || state5.portAllocated || state5.provisionDirExists;
|
|
2460
2564
|
}
|
|
2461
2565
|
var knownVersions = /* @__PURE__ */ new Map();
|
|
2462
2566
|
var knownStatuses = /* @__PURE__ */ new Map();
|
|
@@ -2522,7 +2626,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
|
2522
2626
|
var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
2523
2627
|
function projectMcpHash(_codeName, projectDir) {
|
|
2524
2628
|
try {
|
|
2525
|
-
const raw =
|
|
2629
|
+
const raw = readFileSync5(join6(projectDir, ".mcp.json"), "utf-8");
|
|
2526
2630
|
return createHash2("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
2527
2631
|
} catch {
|
|
2528
2632
|
return null;
|
|
@@ -2530,7 +2634,7 @@ function projectMcpHash(_codeName, projectDir) {
|
|
|
2530
2634
|
}
|
|
2531
2635
|
function projectMcpKeys(_codeName, projectDir) {
|
|
2532
2636
|
try {
|
|
2533
|
-
const raw =
|
|
2637
|
+
const raw = readFileSync5(join6(projectDir, ".mcp.json"), "utf-8");
|
|
2534
2638
|
const parsed = JSON.parse(raw);
|
|
2535
2639
|
const servers = parsed.mcpServers;
|
|
2536
2640
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -2627,7 +2731,7 @@ var codeNameToAgentId = /* @__PURE__ */ new Map();
|
|
|
2627
2731
|
var agentChannelTokens = /* @__PURE__ */ new Map();
|
|
2628
2732
|
var activeChannels = /* @__PURE__ */ new Map();
|
|
2629
2733
|
var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
|
|
2630
|
-
var
|
|
2734
|
+
var state4 = {
|
|
2631
2735
|
pid: process.pid,
|
|
2632
2736
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2633
2737
|
lastPollAt: null,
|
|
@@ -2684,7 +2788,7 @@ var cachedFrameworkVersion = null;
|
|
|
2684
2788
|
var lastVersionCheckAt = 0;
|
|
2685
2789
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2686
2790
|
var lastResponsivenessProbeAt = 0;
|
|
2687
|
-
var agtCliVersion = true ? "0.24.
|
|
2791
|
+
var agtCliVersion = true ? "0.24.3" : "dev";
|
|
2688
2792
|
function resolveBrewPath(execFileSync4) {
|
|
2689
2793
|
try {
|
|
2690
2794
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -2901,7 +3005,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
2901
3005
|
var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2902
3006
|
var claudeCodeUpgradeInFlight = false;
|
|
2903
3007
|
function claudeCodeUpgradeMarkerPath() {
|
|
2904
|
-
return
|
|
3008
|
+
return join6(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
|
|
2905
3009
|
}
|
|
2906
3010
|
function stampClaudeCodeUpgradeMarker() {
|
|
2907
3011
|
try {
|
|
@@ -2911,7 +3015,7 @@ function stampClaudeCodeUpgradeMarker() {
|
|
|
2911
3015
|
}
|
|
2912
3016
|
function claudeCodeUpgradeThrottled() {
|
|
2913
3017
|
try {
|
|
2914
|
-
const lastCheck = parseInt(
|
|
3018
|
+
const lastCheck = parseInt(readFileSync5(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
2915
3019
|
if (!Number.isFinite(lastCheck)) return false;
|
|
2916
3020
|
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
2917
3021
|
} catch {
|
|
@@ -2981,7 +3085,7 @@ async function checkAndUpdateCli() {
|
|
|
2981
3085
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
2982
3086
|
if (!isBrewFormula && !isNpmGlobal) return;
|
|
2983
3087
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
2984
|
-
const markerPath =
|
|
3088
|
+
const markerPath = join6(homedir4(), ".augmented", ".last-update-check");
|
|
2985
3089
|
try {
|
|
2986
3090
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
2987
3091
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
@@ -3145,9 +3249,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3145
3249
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
3146
3250
|
}
|
|
3147
3251
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
3148
|
-
const claudeDir =
|
|
3252
|
+
const claudeDir = join6(homedir4(), ".claude");
|
|
3149
3253
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
3150
|
-
const p =
|
|
3254
|
+
const p = join6(claudeDir, filename);
|
|
3151
3255
|
if (existsSync4(p)) {
|
|
3152
3256
|
try {
|
|
3153
3257
|
rmSync2(p, { force: true });
|
|
@@ -3162,7 +3266,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3162
3266
|
}
|
|
3163
3267
|
function loadGatewayPorts() {
|
|
3164
3268
|
try {
|
|
3165
|
-
return JSON.parse(
|
|
3269
|
+
return JSON.parse(readFileSync5(GATEWAY_PORTS_FILE, "utf-8"));
|
|
3166
3270
|
} catch {
|
|
3167
3271
|
return {};
|
|
3168
3272
|
}
|
|
@@ -3192,10 +3296,10 @@ function freePort(codeName) {
|
|
|
3192
3296
|
}
|
|
3193
3297
|
}
|
|
3194
3298
|
function getStateFile() {
|
|
3195
|
-
return
|
|
3299
|
+
return join6(config?.configDir ?? join6(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
3196
3300
|
}
|
|
3197
3301
|
function channelHashCacheDir() {
|
|
3198
|
-
return config?.configDir ??
|
|
3302
|
+
return config?.configDir ?? join6(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
3199
3303
|
}
|
|
3200
3304
|
function loadChannelHashCache2() {
|
|
3201
3305
|
loadChannelHashCache(knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -3237,7 +3341,7 @@ function log(msg) {
|
|
|
3237
3341
|
`;
|
|
3238
3342
|
if (!managerLogPath) {
|
|
3239
3343
|
try {
|
|
3240
|
-
managerLogPath =
|
|
3344
|
+
managerLogPath = join6(homedir4(), ".augmented", "manager.log");
|
|
3241
3345
|
mkdirSync2(dirname(managerLogPath), { recursive: true });
|
|
3242
3346
|
if (existsSync4(managerLogPath)) {
|
|
3243
3347
|
chmodSync(managerLogPath, 384);
|
|
@@ -3267,7 +3371,7 @@ function sha256(content) {
|
|
|
3267
3371
|
}
|
|
3268
3372
|
function hashFile(filePath) {
|
|
3269
3373
|
try {
|
|
3270
|
-
const content =
|
|
3374
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
3271
3375
|
return sha256(content);
|
|
3272
3376
|
} catch {
|
|
3273
3377
|
return null;
|
|
@@ -3291,13 +3395,13 @@ function parseSkillFrontmatter(content) {
|
|
|
3291
3395
|
return out;
|
|
3292
3396
|
}
|
|
3293
3397
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
3294
|
-
const { readdirSync:
|
|
3295
|
-
const skillsDir =
|
|
3296
|
-
const claudeMdPath =
|
|
3398
|
+
const { readdirSync: readdirSync4, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync4 } = await import("fs");
|
|
3399
|
+
const skillsDir = join6(configDir, codeName, "project", ".claude", "skills");
|
|
3400
|
+
const claudeMdPath = join6(configDir, codeName, "project", "CLAUDE.md");
|
|
3297
3401
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
3298
3402
|
const entries = [];
|
|
3299
|
-
for (const dir of
|
|
3300
|
-
const skillFile =
|
|
3403
|
+
for (const dir of readdirSync4(skillsDir).sort()) {
|
|
3404
|
+
const skillFile = join6(skillsDir, dir, "SKILL.md");
|
|
3301
3405
|
if (!ex(skillFile)) continue;
|
|
3302
3406
|
try {
|
|
3303
3407
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -3345,10 +3449,10 @@ ${SKILLS_INDEX_END}`;
|
|
|
3345
3449
|
}
|
|
3346
3450
|
async function migrateToProfiles() {
|
|
3347
3451
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3348
|
-
const sharedConfigPath =
|
|
3452
|
+
const sharedConfigPath = join6(homeDir, ".openclaw", "openclaw.json");
|
|
3349
3453
|
let sharedConfig;
|
|
3350
3454
|
try {
|
|
3351
|
-
sharedConfig = JSON.parse(
|
|
3455
|
+
sharedConfig = JSON.parse(readFileSync5(sharedConfigPath, "utf-8"));
|
|
3352
3456
|
} catch {
|
|
3353
3457
|
return;
|
|
3354
3458
|
}
|
|
@@ -3361,19 +3465,19 @@ async function migrateToProfiles() {
|
|
|
3361
3465
|
const codeName = agentEntry["id"];
|
|
3362
3466
|
if (!codeName) continue;
|
|
3363
3467
|
if (codeName === "main") continue;
|
|
3364
|
-
const profileDir =
|
|
3365
|
-
if (existsSync4(
|
|
3468
|
+
const profileDir = join6(homeDir, `.openclaw-${codeName}`);
|
|
3469
|
+
if (existsSync4(join6(profileDir, "openclaw.json"))) continue;
|
|
3366
3470
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
3367
3471
|
if (adapter.seedProfileConfig) {
|
|
3368
3472
|
adapter.seedProfileConfig(codeName);
|
|
3369
3473
|
}
|
|
3370
|
-
const sharedAuthDir =
|
|
3371
|
-
const profileAuthDir =
|
|
3372
|
-
const authFile =
|
|
3474
|
+
const sharedAuthDir = join6(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
3475
|
+
const profileAuthDir = join6(profileDir, "agents", codeName, "agent");
|
|
3476
|
+
const authFile = join6(sharedAuthDir, "auth-profiles.json");
|
|
3373
3477
|
if (existsSync4(authFile)) {
|
|
3374
3478
|
mkdirSync2(profileAuthDir, { recursive: true });
|
|
3375
|
-
const authContent =
|
|
3376
|
-
writeFileSync3(
|
|
3479
|
+
const authContent = readFileSync5(authFile, "utf-8");
|
|
3480
|
+
writeFileSync3(join6(profileAuthDir, "auth-profiles.json"), authContent);
|
|
3377
3481
|
}
|
|
3378
3482
|
allocatePort(codeName);
|
|
3379
3483
|
migrated++;
|
|
@@ -3411,7 +3515,7 @@ function readGatewayToken(codeName) {
|
|
|
3411
3515
|
}
|
|
3412
3516
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3413
3517
|
try {
|
|
3414
|
-
const cfg = JSON.parse(
|
|
3518
|
+
const cfg = JSON.parse(readFileSync5(join6(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
3415
3519
|
return cfg?.gateway?.auth?.token;
|
|
3416
3520
|
} catch {
|
|
3417
3521
|
return void 0;
|
|
@@ -3420,18 +3524,18 @@ function readGatewayToken(codeName) {
|
|
|
3420
3524
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
3421
3525
|
function isGatewayHung(codeName) {
|
|
3422
3526
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3423
|
-
const jobsPath =
|
|
3527
|
+
const jobsPath = join6(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
3424
3528
|
if (!existsSync4(jobsPath)) return false;
|
|
3425
3529
|
try {
|
|
3426
|
-
const data = JSON.parse(
|
|
3530
|
+
const data = JSON.parse(readFileSync5(jobsPath, "utf-8"));
|
|
3427
3531
|
const jobs = data.jobs ?? data;
|
|
3428
3532
|
if (!Array.isArray(jobs)) return false;
|
|
3429
3533
|
const now = Date.now();
|
|
3430
3534
|
for (const job of jobs) {
|
|
3431
|
-
const
|
|
3432
|
-
if (!
|
|
3433
|
-
const runStartedAt =
|
|
3434
|
-
const isRunning =
|
|
3535
|
+
const state5 = job.state;
|
|
3536
|
+
if (!state5) continue;
|
|
3537
|
+
const runStartedAt = state5.runStartedAtMs;
|
|
3538
|
+
const isRunning = state5.status === "running" || state5.running === true;
|
|
3435
3539
|
if (isRunning && runStartedAt && now - runStartedAt > GATEWAY_HUNG_TIMEOUT_MS) {
|
|
3436
3540
|
return true;
|
|
3437
3541
|
}
|
|
@@ -3456,15 +3560,15 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
3456
3560
|
}
|
|
3457
3561
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
3458
3562
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3459
|
-
const cronJobsPath =
|
|
3563
|
+
const cronJobsPath = join6(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
3460
3564
|
clearStaleCronRunState(cronJobsPath);
|
|
3461
3565
|
} else {
|
|
3462
3566
|
if (status.port) {
|
|
3463
3567
|
try {
|
|
3464
3568
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3465
|
-
const configPath =
|
|
3569
|
+
const configPath = join6(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
3466
3570
|
if (existsSync4(configPath)) {
|
|
3467
|
-
const cfg = JSON.parse(
|
|
3571
|
+
const cfg = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
3468
3572
|
if (cfg.gateway?.port !== status.port) {
|
|
3469
3573
|
if (!cfg.gateway) cfg.gateway = {};
|
|
3470
3574
|
cfg.gateway.port = status.port;
|
|
@@ -3488,9 +3592,9 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
3488
3592
|
gatewaysStartedThisCycle.add(codeName);
|
|
3489
3593
|
try {
|
|
3490
3594
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3491
|
-
const configPath =
|
|
3595
|
+
const configPath = join6(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
3492
3596
|
if (existsSync4(configPath)) {
|
|
3493
|
-
const cfg = JSON.parse(
|
|
3597
|
+
const cfg = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
3494
3598
|
if (!cfg.gateway) cfg.gateway = {};
|
|
3495
3599
|
cfg.gateway.port = port;
|
|
3496
3600
|
writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
|
|
@@ -3632,7 +3736,7 @@ async function pollCycle() {
|
|
|
3632
3736
|
const now = Date.now();
|
|
3633
3737
|
if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
|
|
3634
3738
|
try {
|
|
3635
|
-
const firstAgent =
|
|
3739
|
+
const firstAgent = state4.agents[0];
|
|
3636
3740
|
const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework("openclaw");
|
|
3637
3741
|
if (versionAdapter.getVersion) {
|
|
3638
3742
|
cachedFrameworkVersion = await versionAdapter.getVersion();
|
|
@@ -3643,7 +3747,7 @@ async function pollCycle() {
|
|
|
3643
3747
|
}
|
|
3644
3748
|
try {
|
|
3645
3749
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
3646
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
3750
|
+
const { collectDiagnostics } = await import("../persistent-session-JUJY46HW.js");
|
|
3647
3751
|
const diagCodeNames = [...persistentSessionAgents];
|
|
3648
3752
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
3649
3753
|
let tailscaleHostname;
|
|
@@ -3694,7 +3798,7 @@ async function pollCycle() {
|
|
|
3694
3798
|
const {
|
|
3695
3799
|
collectResponsivenessProbes,
|
|
3696
3800
|
getResponsivenessIntervalMs
|
|
3697
|
-
} = await import("../responsiveness-probe-
|
|
3801
|
+
} = await import("../responsiveness-probe-XOEC7J5D.js");
|
|
3698
3802
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
3699
3803
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
3700
3804
|
const probeCodeNames = [...persistentSessionAgents];
|
|
@@ -3754,7 +3858,7 @@ async function pollCycle() {
|
|
|
3754
3858
|
for (const agent of agents) {
|
|
3755
3859
|
const requested = agent.restart_requested_at ?? null;
|
|
3756
3860
|
if (!requested) continue;
|
|
3757
|
-
const prev =
|
|
3861
|
+
const prev = state4.agents.find((a) => a.agentId === agent.agent_id);
|
|
3758
3862
|
const lastProcessed = prev?.lastRestartProcessedAt ?? null;
|
|
3759
3863
|
if (lastProcessed && Date.parse(lastProcessed) >= Date.parse(requested)) continue;
|
|
3760
3864
|
log(`[restart] Dashboard requested restart for '${agent.code_name}' at ${requested}`);
|
|
@@ -3777,7 +3881,7 @@ async function pollCycle() {
|
|
|
3777
3881
|
await processAgent(agent, agentStates);
|
|
3778
3882
|
} catch (err) {
|
|
3779
3883
|
log(`Error processing agent '${agent.code_name}': ${err.message}`);
|
|
3780
|
-
const existing =
|
|
3884
|
+
const existing = state4.agents.find((a) => a.agentId === agent.agent_id);
|
|
3781
3885
|
if (existing) {
|
|
3782
3886
|
agentStates.push(existing);
|
|
3783
3887
|
} else {
|
|
@@ -3803,12 +3907,12 @@ async function pollCycle() {
|
|
|
3803
3907
|
void maybeReportActivityCache({ api, log });
|
|
3804
3908
|
const restartAckStateChanged = applyRestartAcks({
|
|
3805
3909
|
agentStates,
|
|
3806
|
-
priorAgents:
|
|
3910
|
+
priorAgents: state4.agents,
|
|
3807
3911
|
restartAcks
|
|
3808
3912
|
});
|
|
3809
3913
|
if (restartAckStateChanged) {
|
|
3810
3914
|
try {
|
|
3811
|
-
const ackedState = { ...
|
|
3915
|
+
const ackedState = { ...state4, agents: agentStates };
|
|
3812
3916
|
writeFileSync3(getStateFile(), JSON.stringify(ackedState, null, 2));
|
|
3813
3917
|
} catch (err) {
|
|
3814
3918
|
log(`[restart] failed to persist ack immediately: ${err.message}`);
|
|
@@ -3826,7 +3930,7 @@ async function pollCycle() {
|
|
|
3826
3930
|
} catch {
|
|
3827
3931
|
}
|
|
3828
3932
|
const currentIds = new Set(agents.map((a) => a.agent_id));
|
|
3829
|
-
for (const prev of
|
|
3933
|
+
for (const prev of state4.agents) {
|
|
3830
3934
|
if (!currentIds.has(prev.agentId)) {
|
|
3831
3935
|
log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
|
|
3832
3936
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
@@ -3839,7 +3943,7 @@ async function pollCycle() {
|
|
|
3839
3943
|
}
|
|
3840
3944
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
3841
3945
|
freePort(prev.codeName);
|
|
3842
|
-
const agentDir =
|
|
3946
|
+
const agentDir = join6(adapter.getAgentDir(prev.codeName), "provision");
|
|
3843
3947
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
3844
3948
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
3845
3949
|
}
|
|
@@ -3926,10 +4030,10 @@ async function pollCycle() {
|
|
|
3926
4030
|
}
|
|
3927
4031
|
} catch {
|
|
3928
4032
|
}
|
|
3929
|
-
|
|
3930
|
-
...
|
|
4033
|
+
state4 = {
|
|
4034
|
+
...state4,
|
|
3931
4035
|
lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3932
|
-
pollCount:
|
|
4036
|
+
pollCount: state4.pollCount + 1,
|
|
3933
4037
|
agents: agentStates,
|
|
3934
4038
|
// ENG-5441: serialise trip state on every poll so manager restarts
|
|
3935
4039
|
// never silently clear a tripped breaker. Cheap — only tripped
|
|
@@ -3940,9 +4044,9 @@ async function pollCycle() {
|
|
|
3940
4044
|
log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
|
|
3941
4045
|
consecutivePollFailures = 0;
|
|
3942
4046
|
}
|
|
3943
|
-
send({ type: "state-update", state:
|
|
4047
|
+
send({ type: "state-update", state: state4 });
|
|
3944
4048
|
} catch (err) {
|
|
3945
|
-
|
|
4049
|
+
state4.errorCount++;
|
|
3946
4050
|
const message = err.message;
|
|
3947
4051
|
log(`Poll error: ${message}`);
|
|
3948
4052
|
send({ type: "error", message });
|
|
@@ -3980,10 +4084,16 @@ async function processAgent(agent, agentStates) {
|
|
|
3980
4084
|
agentId: agent.agent_id,
|
|
3981
4085
|
log
|
|
3982
4086
|
});
|
|
4087
|
+
void maybeReportTokenUsage({
|
|
4088
|
+
api,
|
|
4089
|
+
codeName: agent.code_name,
|
|
4090
|
+
agentId: agent.agent_id,
|
|
4091
|
+
log
|
|
4092
|
+
});
|
|
3983
4093
|
}
|
|
3984
4094
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3985
4095
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
3986
|
-
let agentDir =
|
|
4096
|
+
let agentDir = join6(adapter.getAgentDir(agent.code_name), "provision");
|
|
3987
4097
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
3988
4098
|
if (previousKnownStatus !== agent.status) {
|
|
3989
4099
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -4104,7 +4214,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4104
4214
|
});
|
|
4105
4215
|
} catch (err) {
|
|
4106
4216
|
log(`Refresh failed for '${agent.code_name}': ${err.message}`);
|
|
4107
|
-
const existing =
|
|
4217
|
+
const existing = state4.agents.find((a) => a.agentId === agent.agent_id);
|
|
4108
4218
|
agentStates.push(existing ?? {
|
|
4109
4219
|
agentId: agent.agent_id,
|
|
4110
4220
|
codeName: agent.code_name,
|
|
@@ -4152,7 +4262,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4152
4262
|
const frameworkId = refreshData.agent.framework ?? "openclaw";
|
|
4153
4263
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
4154
4264
|
const frameworkAdapter = getFramework(frameworkId);
|
|
4155
|
-
agentDir =
|
|
4265
|
+
agentDir = join6(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
4156
4266
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
4157
4267
|
if (frameworkAdapter.seedProfileConfig) {
|
|
4158
4268
|
frameworkAdapter.seedProfileConfig(agent.code_name);
|
|
@@ -4160,7 +4270,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4160
4270
|
const charterVersion = refreshData.charter.version;
|
|
4161
4271
|
const toolsVersion = refreshData.tools.version;
|
|
4162
4272
|
const known = knownVersions.get(agent.agent_id);
|
|
4163
|
-
let lastProvisionAt =
|
|
4273
|
+
let lastProvisionAt = state4.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
|
|
4164
4274
|
const currentChannelIds = launchableChannelIds(refreshData.channel_configs);
|
|
4165
4275
|
const previousChannelIds = knownChannels.get(agent.agent_id);
|
|
4166
4276
|
const channelsChanged = !previousChannelIds || currentChannelIds.size !== previousChannelIds.size || [...currentChannelIds].some((ch) => !previousChannelIds.has(ch)) || [...previousChannelIds].some((ch) => !currentChannelIds.has(ch));
|
|
@@ -4183,7 +4293,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4183
4293
|
const changedFiles = [];
|
|
4184
4294
|
mkdirSync2(agentDir, { recursive: true });
|
|
4185
4295
|
for (const artifact of artifacts) {
|
|
4186
|
-
const filePath =
|
|
4296
|
+
const filePath = join6(agentDir, artifact.relativePath);
|
|
4187
4297
|
let existingHash;
|
|
4188
4298
|
let newHash;
|
|
4189
4299
|
let writeContent = artifact.content;
|
|
@@ -4195,8 +4305,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4195
4305
|
};
|
|
4196
4306
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
4197
4307
|
try {
|
|
4198
|
-
const projectClaudeMd =
|
|
4199
|
-
const existing =
|
|
4308
|
+
const projectClaudeMd = join6(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
4309
|
+
const existing = readFileSync5(projectClaudeMd, "utf-8");
|
|
4200
4310
|
existingHash = sha256(stripDynamicSections(existing));
|
|
4201
4311
|
} catch {
|
|
4202
4312
|
existingHash = null;
|
|
@@ -4214,7 +4324,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4214
4324
|
const generatorKeys = Object.keys(generatorServers);
|
|
4215
4325
|
let existingRaw = "";
|
|
4216
4326
|
try {
|
|
4217
|
-
existingRaw =
|
|
4327
|
+
existingRaw = readFileSync5(filePath, "utf-8");
|
|
4218
4328
|
} catch {
|
|
4219
4329
|
}
|
|
4220
4330
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -4236,22 +4346,22 @@ async function processAgent(agent, agentStates) {
|
|
|
4236
4346
|
}
|
|
4237
4347
|
}
|
|
4238
4348
|
if (changedFiles.length > 0) {
|
|
4239
|
-
const isFirst = !existsSync4(
|
|
4349
|
+
const isFirst = !existsSync4(join6(agentDir, "CHARTER.md"));
|
|
4240
4350
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
4241
4351
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
4242
4352
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
4243
4353
|
for (const file of changedFiles) {
|
|
4244
|
-
const filePath =
|
|
4354
|
+
const filePath = join6(agentDir, file.relativePath);
|
|
4245
4355
|
mkdirSync2(dirname(filePath), { recursive: true });
|
|
4246
4356
|
writeFileSync3(filePath, file.content);
|
|
4247
4357
|
}
|
|
4248
4358
|
try {
|
|
4249
|
-
const provSkillsDir =
|
|
4359
|
+
const provSkillsDir = join6(agentDir, ".claude", "skills");
|
|
4250
4360
|
if (existsSync4(provSkillsDir)) {
|
|
4251
|
-
for (const folder of
|
|
4361
|
+
for (const folder of readdirSync3(provSkillsDir)) {
|
|
4252
4362
|
if (folder.startsWith("knowledge-")) {
|
|
4253
4363
|
try {
|
|
4254
|
-
rmSync2(
|
|
4364
|
+
rmSync2(join6(provSkillsDir, folder), { recursive: true });
|
|
4255
4365
|
} catch {
|
|
4256
4366
|
}
|
|
4257
4367
|
}
|
|
@@ -4264,7 +4374,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4264
4374
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
4265
4375
|
const hashes = /* @__PURE__ */ new Map();
|
|
4266
4376
|
for (const file of trackedFiles2) {
|
|
4267
|
-
const h = hashFile(
|
|
4377
|
+
const h = hashFile(join6(agentDir, file));
|
|
4268
4378
|
if (h) hashes.set(file, h);
|
|
4269
4379
|
}
|
|
4270
4380
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -4311,7 +4421,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4311
4421
|
if (written && existsSync4(agentDir)) {
|
|
4312
4422
|
const driftedFiles = [];
|
|
4313
4423
|
for (const [file, expectedHash] of written) {
|
|
4314
|
-
const localHash = hashFile(
|
|
4424
|
+
const localHash = hashFile(join6(agentDir, file));
|
|
4315
4425
|
if (localHash && localHash !== expectedHash) {
|
|
4316
4426
|
driftedFiles.push(file);
|
|
4317
4427
|
}
|
|
@@ -4322,7 +4432,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4322
4432
|
try {
|
|
4323
4433
|
const localHashes = {};
|
|
4324
4434
|
for (const file of driftedFiles) {
|
|
4325
|
-
localHashes[file] = hashFile(
|
|
4435
|
+
localHashes[file] = hashFile(join6(agentDir, file));
|
|
4326
4436
|
}
|
|
4327
4437
|
await api.post("/host/drift", {
|
|
4328
4438
|
agent_id: agent.agent_id,
|
|
@@ -4480,18 +4590,18 @@ async function processAgent(agent, agentStates) {
|
|
|
4480
4590
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
4481
4591
|
try {
|
|
4482
4592
|
const agentProvisionDir = agentDir;
|
|
4483
|
-
const projectDir =
|
|
4593
|
+
const projectDir = join6(homedir4(), ".augmented", agent.code_name, "project");
|
|
4484
4594
|
mkdirSync2(agentProvisionDir, { recursive: true });
|
|
4485
4595
|
mkdirSync2(projectDir, { recursive: true });
|
|
4486
|
-
const provisionMcpPath =
|
|
4487
|
-
const projectMcpPath =
|
|
4596
|
+
const provisionMcpPath = join6(agentProvisionDir, ".mcp.json");
|
|
4597
|
+
const projectMcpPath = join6(projectDir, ".mcp.json");
|
|
4488
4598
|
let mcpConfig = { mcpServers: {} };
|
|
4489
4599
|
try {
|
|
4490
|
-
mcpConfig = JSON.parse(
|
|
4600
|
+
mcpConfig = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
|
|
4491
4601
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
4492
4602
|
} catch {
|
|
4493
4603
|
}
|
|
4494
|
-
const localDirectChatChannel =
|
|
4604
|
+
const localDirectChatChannel = join6(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
4495
4605
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
4496
4606
|
const directChatTz = (() => {
|
|
4497
4607
|
const tz = directChatTeamSettings?.["timezone"];
|
|
@@ -4513,7 +4623,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4513
4623
|
writeFileSync3(projectMcpPath, serialized);
|
|
4514
4624
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
4515
4625
|
}
|
|
4516
|
-
const staleChannelsPath =
|
|
4626
|
+
const staleChannelsPath = join6(projectDir, ".mcp-channels.json");
|
|
4517
4627
|
if (existsSync4(staleChannelsPath)) {
|
|
4518
4628
|
try {
|
|
4519
4629
|
rmSync2(staleChannelsPath, { force: true });
|
|
@@ -4524,7 +4634,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4524
4634
|
log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
|
|
4525
4635
|
}
|
|
4526
4636
|
}
|
|
4527
|
-
let lastSecretsProvisionAt =
|
|
4637
|
+
let lastSecretsProvisionAt = state4.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
|
|
4528
4638
|
let secretsHash = knownSecretsHashes.get(agent.agent_id) ?? null;
|
|
4529
4639
|
try {
|
|
4530
4640
|
const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
|
|
@@ -4580,11 +4690,11 @@ async function processAgent(agent, agentStates) {
|
|
|
4580
4690
|
const intHash = computeIntegrationsHash(integrations);
|
|
4581
4691
|
const prevIntHash = knownIntegrationHashes.get(agent.agent_id);
|
|
4582
4692
|
if (intHash !== prevIntHash) {
|
|
4583
|
-
const projectDir =
|
|
4584
|
-
const envIntPath =
|
|
4693
|
+
const projectDir = join6(homedir4(), ".augmented", agent.code_name, "project");
|
|
4694
|
+
const envIntPath = join6(projectDir, ".env.integrations");
|
|
4585
4695
|
let preWriteEnv;
|
|
4586
4696
|
try {
|
|
4587
|
-
preWriteEnv =
|
|
4697
|
+
preWriteEnv = readFileSync5(envIntPath, "utf-8");
|
|
4588
4698
|
} catch {
|
|
4589
4699
|
preWriteEnv = void 0;
|
|
4590
4700
|
}
|
|
@@ -4596,9 +4706,9 @@ async function processAgent(agent, agentStates) {
|
|
|
4596
4706
|
let rotationHandled = true;
|
|
4597
4707
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
4598
4708
|
try {
|
|
4599
|
-
const projectMcpPath =
|
|
4600
|
-
const postWriteEnv =
|
|
4601
|
-
const mcpContent =
|
|
4709
|
+
const projectMcpPath = join6(projectDir, ".mcp.json");
|
|
4710
|
+
const postWriteEnv = readFileSync5(envIntPath, "utf-8");
|
|
4711
|
+
const mcpContent = readFileSync5(projectMcpPath, "utf-8");
|
|
4602
4712
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
4603
4713
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
4604
4714
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -4664,8 +4774,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4664
4774
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
4665
4775
|
if (mcpPath) {
|
|
4666
4776
|
try {
|
|
4667
|
-
const { readFileSync:
|
|
4668
|
-
const mcpConfig = JSON.parse(
|
|
4777
|
+
const { readFileSync: readFileSync6 } = await import("fs");
|
|
4778
|
+
const mcpConfig = JSON.parse(readFileSync6(mcpPath, "utf-8"));
|
|
4669
4779
|
if (mcpConfig.mcpServers) {
|
|
4670
4780
|
const managedPrefixes = [
|
|
4671
4781
|
"composio_",
|
|
@@ -4754,7 +4864,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4754
4864
|
if (agent.status === "active") {
|
|
4755
4865
|
if (frameworkAdapter.installPlugin) {
|
|
4756
4866
|
try {
|
|
4757
|
-
const pluginPath =
|
|
4867
|
+
const pluginPath = join6(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
4758
4868
|
if (existsSync4(pluginPath)) {
|
|
4759
4869
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
4760
4870
|
agtHost: requireHost(),
|
|
@@ -4820,25 +4930,25 @@ async function processAgent(agent, agentStates) {
|
|
|
4820
4930
|
}
|
|
4821
4931
|
}
|
|
4822
4932
|
try {
|
|
4823
|
-
const { readdirSync:
|
|
4933
|
+
const { readdirSync: readdirSync4, rmSync: rmSync3 } = await import("fs");
|
|
4824
4934
|
const { homedir: homedir5 } = await import("os");
|
|
4825
4935
|
const frameworkId2 = frameworkAdapter.id;
|
|
4826
4936
|
const candidateSkillDirs = [
|
|
4827
4937
|
// Claude Code — framework runtime tree
|
|
4828
|
-
|
|
4938
|
+
join6(homedir5(), ".augmented", agent.code_name, "skills"),
|
|
4829
4939
|
// Claude Code — project tree
|
|
4830
|
-
|
|
4940
|
+
join6(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
4831
4941
|
// OpenClaw — framework runtime tree
|
|
4832
|
-
|
|
4942
|
+
join6(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
|
|
4833
4943
|
// Defensive: legacy provision-side path, not currently an
|
|
4834
4944
|
// install target but cheap to sweep.
|
|
4835
|
-
|
|
4945
|
+
join6(agentDir, ".claude", "skills")
|
|
4836
4946
|
];
|
|
4837
4947
|
const existingDirs = candidateSkillDirs.filter((d) => existsSync4(d));
|
|
4838
4948
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
4839
4949
|
for (const dir of existingDirs) {
|
|
4840
4950
|
try {
|
|
4841
|
-
for (const entry of
|
|
4951
|
+
for (const entry of readdirSync4(dir)) {
|
|
4842
4952
|
if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
|
|
4843
4953
|
discoveredEntries.add(entry);
|
|
4844
4954
|
}
|
|
@@ -4848,7 +4958,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4848
4958
|
}
|
|
4849
4959
|
const removeSkillFolder = (entry, reason) => {
|
|
4850
4960
|
for (const dir of existingDirs) {
|
|
4851
|
-
const p =
|
|
4961
|
+
const p = join6(dir, entry);
|
|
4852
4962
|
if (existsSync4(p)) {
|
|
4853
4963
|
rmSync3(p, { recursive: true, force: true });
|
|
4854
4964
|
}
|
|
@@ -5009,8 +5119,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5009
5119
|
const sess = getSessionState(agent.code_name);
|
|
5010
5120
|
let mcpJsonParsed = null;
|
|
5011
5121
|
try {
|
|
5012
|
-
const mcpPath =
|
|
5013
|
-
mcpJsonParsed = JSON.parse(
|
|
5122
|
+
const mcpPath = join6(getProjectDir(agent.code_name), ".mcp.json");
|
|
5123
|
+
mcpJsonParsed = JSON.parse(readFileSync5(mcpPath, "utf-8"));
|
|
5014
5124
|
} catch {
|
|
5015
5125
|
}
|
|
5016
5126
|
reapMissingMcpSessions({
|
|
@@ -5159,10 +5269,10 @@ async function processAgent(agent, agentStates) {
|
|
|
5159
5269
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
5160
5270
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
5161
5271
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
5162
|
-
const jobsPath =
|
|
5272
|
+
const jobsPath = join6(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
5163
5273
|
if (existsSync4(jobsPath)) {
|
|
5164
5274
|
try {
|
|
5165
|
-
const jobsData = JSON.parse(
|
|
5275
|
+
const jobsData = JSON.parse(readFileSync5(jobsPath, "utf-8"));
|
|
5166
5276
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
5167
5277
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
5168
5278
|
);
|
|
@@ -5306,7 +5416,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
5306
5416
|
if (trackedFiles.length > 0 && existsSync4(agentDir)) {
|
|
5307
5417
|
const hashes = /* @__PURE__ */ new Map();
|
|
5308
5418
|
for (const file of trackedFiles) {
|
|
5309
|
-
const h = hashFile(
|
|
5419
|
+
const h = hashFile(join6(agentDir, file));
|
|
5310
5420
|
if (h) hashes.set(file, h);
|
|
5311
5421
|
}
|
|
5312
5422
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -5340,19 +5450,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
5340
5450
|
lastCleanupAt.set(codeName, Date.now());
|
|
5341
5451
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
5342
5452
|
for (const agentDir of ["main", codeName]) {
|
|
5343
|
-
const sessionsDir =
|
|
5453
|
+
const sessionsDir = join6(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
5344
5454
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
5345
5455
|
}
|
|
5346
|
-
const cronRunsDir =
|
|
5456
|
+
const cronRunsDir = join6(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
5347
5457
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
5348
|
-
const cronJobsPath =
|
|
5458
|
+
const cronJobsPath = join6(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
5349
5459
|
clearStaleCronRunState(cronJobsPath);
|
|
5350
5460
|
}
|
|
5351
5461
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
5352
|
-
const indexPath =
|
|
5462
|
+
const indexPath = join6(sessionsDir, "sessions.json");
|
|
5353
5463
|
if (!existsSync4(indexPath)) return;
|
|
5354
5464
|
try {
|
|
5355
|
-
const raw =
|
|
5465
|
+
const raw = readFileSync5(indexPath, "utf-8");
|
|
5356
5466
|
const index = JSON.parse(raw);
|
|
5357
5467
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
5358
5468
|
key: k,
|
|
@@ -5365,7 +5475,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5365
5475
|
for (const entry of toDelete) {
|
|
5366
5476
|
delete index[entry.key];
|
|
5367
5477
|
if (entry.sessionId) {
|
|
5368
|
-
const sessionFile =
|
|
5478
|
+
const sessionFile = join6(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
5369
5479
|
try {
|
|
5370
5480
|
if (existsSync4(sessionFile)) {
|
|
5371
5481
|
unlinkSync(sessionFile);
|
|
@@ -5387,7 +5497,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5387
5497
|
delete index[parentKey];
|
|
5388
5498
|
if (parentSessionId) {
|
|
5389
5499
|
try {
|
|
5390
|
-
const f =
|
|
5500
|
+
const f = join6(sessionsDir, `${parentSessionId}.jsonl`);
|
|
5391
5501
|
if (existsSync4(f)) {
|
|
5392
5502
|
unlinkSync(f);
|
|
5393
5503
|
deletedFiles++;
|
|
@@ -5408,26 +5518,26 @@ var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
|
5408
5518
|
function clearStaleCronRunState(jobsPath) {
|
|
5409
5519
|
if (!existsSync4(jobsPath)) return;
|
|
5410
5520
|
try {
|
|
5411
|
-
const raw =
|
|
5521
|
+
const raw = readFileSync5(jobsPath, "utf-8");
|
|
5412
5522
|
const data = JSON.parse(raw);
|
|
5413
5523
|
const jobs = data.jobs ?? data;
|
|
5414
5524
|
if (!Array.isArray(jobs)) return;
|
|
5415
5525
|
let changed = false;
|
|
5416
5526
|
const now = Date.now();
|
|
5417
5527
|
for (const job of jobs) {
|
|
5418
|
-
const
|
|
5419
|
-
if (!
|
|
5420
|
-
const runStartedAt =
|
|
5421
|
-
const isRunning =
|
|
5528
|
+
const state5 = job.state;
|
|
5529
|
+
if (!state5) continue;
|
|
5530
|
+
const runStartedAt = state5.runningAtMs ?? state5.runStartedAtMs;
|
|
5531
|
+
const isRunning = state5.running === true || state5.status === "running";
|
|
5422
5532
|
if (isRunning && runStartedAt && now - runStartedAt > STALE_RUN_TIMEOUT_MS) {
|
|
5423
|
-
|
|
5424
|
-
delete
|
|
5425
|
-
delete
|
|
5426
|
-
delete
|
|
5533
|
+
state5.running = false;
|
|
5534
|
+
delete state5.status;
|
|
5535
|
+
delete state5.runStartedAtMs;
|
|
5536
|
+
delete state5.currentRunId;
|
|
5427
5537
|
changed = true;
|
|
5428
5538
|
log(`Cleared stale running state for cron job '${job.name}' (stuck for ${Math.round((now - runStartedAt) / 6e4)}min)`);
|
|
5429
5539
|
} else if (isRunning && !runStartedAt) {
|
|
5430
|
-
|
|
5540
|
+
state5.running = false;
|
|
5431
5541
|
changed = true;
|
|
5432
5542
|
log(`Cleared stale running state for cron job '${job.name}' (no start timestamp)`);
|
|
5433
5543
|
}
|
|
@@ -5443,11 +5553,11 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
|
5443
5553
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
5444
5554
|
let removed = 0;
|
|
5445
5555
|
try {
|
|
5446
|
-
for (const f of
|
|
5556
|
+
for (const f of readdirSync3(dir)) {
|
|
5447
5557
|
if (!f.endsWith(ext)) continue;
|
|
5448
|
-
const fullPath =
|
|
5558
|
+
const fullPath = join6(dir, f);
|
|
5449
5559
|
try {
|
|
5450
|
-
const st =
|
|
5560
|
+
const st = statSync2(fullPath);
|
|
5451
5561
|
if (st.mtimeMs < cutoff) {
|
|
5452
5562
|
unlinkSync(fullPath);
|
|
5453
5563
|
removed++;
|
|
@@ -5491,16 +5601,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
5491
5601
|
enabled: t.enabled ?? true,
|
|
5492
5602
|
triggered_at: t.triggered_at ?? null
|
|
5493
5603
|
}));
|
|
5494
|
-
const
|
|
5495
|
-
claudeSchedulerStates.set(codeName,
|
|
5604
|
+
const state6 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
5605
|
+
claudeSchedulerStates.set(codeName, state6);
|
|
5496
5606
|
knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
5497
5607
|
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
5498
5608
|
}
|
|
5499
5609
|
if (!claudeSchedulerStates.has(codeName)) {
|
|
5500
5610
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
5501
5611
|
}
|
|
5502
|
-
const
|
|
5503
|
-
const ready = getReadyTasks(
|
|
5612
|
+
const state5 = claudeSchedulerStates.get(codeName);
|
|
5613
|
+
const ready = getReadyTasks(state5, inFlightClaudeTasks);
|
|
5504
5614
|
if (ready.length === 0) return;
|
|
5505
5615
|
for (const task of ready) {
|
|
5506
5616
|
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
@@ -5589,7 +5699,7 @@ async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
|
5589
5699
|
}
|
|
5590
5700
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
5591
5701
|
const projectDir = getProjectDir2(codeName);
|
|
5592
|
-
const mcpConfigPath =
|
|
5702
|
+
const mcpConfigPath = join6(projectDir, ".mcp.json");
|
|
5593
5703
|
let runId = null;
|
|
5594
5704
|
let kanbanItemId = null;
|
|
5595
5705
|
let taskResult;
|
|
@@ -5597,11 +5707,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5597
5707
|
const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
|
|
5598
5708
|
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
|
|
5599
5709
|
try {
|
|
5600
|
-
const claudeMdPath =
|
|
5710
|
+
const claudeMdPath = join6(projectDir, "CLAUDE.md");
|
|
5601
5711
|
const serverNames = [];
|
|
5602
5712
|
if (existsSync4(mcpConfigPath)) {
|
|
5603
5713
|
try {
|
|
5604
|
-
const d = JSON.parse(
|
|
5714
|
+
const d = JSON.parse(readFileSync5(mcpConfigPath, "utf-8"));
|
|
5605
5715
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
5606
5716
|
} catch {
|
|
5607
5717
|
}
|
|
@@ -5624,10 +5734,10 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5624
5734
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
5625
5735
|
}
|
|
5626
5736
|
const childEnv = { ...process.env };
|
|
5627
|
-
const envIntPath =
|
|
5737
|
+
const envIntPath = join6(projectDir, ".env.integrations");
|
|
5628
5738
|
if (existsSync4(envIntPath)) {
|
|
5629
5739
|
try {
|
|
5630
|
-
for (const line of
|
|
5740
|
+
for (const line of readFileSync5(envIntPath, "utf-8").split("\n")) {
|
|
5631
5741
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
5632
5742
|
const eqIdx = line.indexOf("=");
|
|
5633
5743
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -5781,8 +5891,8 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
5781
5891
|
}
|
|
5782
5892
|
}
|
|
5783
5893
|
function fireClaudeWorkTrigger(codeName, agentId, boardItems) {
|
|
5784
|
-
const
|
|
5785
|
-
const kanbanTask = findTaskByTemplate(
|
|
5894
|
+
const state5 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
5895
|
+
const kanbanTask = findTaskByTemplate(state5, "kanban-work");
|
|
5786
5896
|
if (!kanbanTask) {
|
|
5787
5897
|
log(`[claude-scheduler] Work trigger: no kanban-work task found for '${codeName}'`);
|
|
5788
5898
|
return;
|
|
@@ -5811,8 +5921,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
|
|
|
5811
5921
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
5812
5922
|
const codeName = agent.code_name;
|
|
5813
5923
|
const projectDir = getProjectDir(codeName);
|
|
5814
|
-
const mcpConfigPath =
|
|
5815
|
-
const claudeMdPath =
|
|
5924
|
+
const mcpConfigPath = join6(projectDir, ".mcp.json");
|
|
5925
|
+
const claudeMdPath = join6(projectDir, "CLAUDE.md");
|
|
5816
5926
|
if (restartBreaker.isTripped(codeName)) {
|
|
5817
5927
|
const trip = restartBreaker.getTrip(codeName);
|
|
5818
5928
|
return {
|
|
@@ -6049,9 +6159,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash2("sha256").
|
|
|
6049
6159
|
} else if (!claudeSchedulerStates.has(codeName)) {
|
|
6050
6160
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
6051
6161
|
}
|
|
6052
|
-
const
|
|
6053
|
-
if (
|
|
6054
|
-
const ready = getReadyTasks(
|
|
6162
|
+
const state5 = claudeSchedulerStates.get(codeName);
|
|
6163
|
+
if (state5) {
|
|
6164
|
+
const ready = getReadyTasks(state5, inFlightClaudeTasks);
|
|
6055
6165
|
if (ready.length > 0) {
|
|
6056
6166
|
log(`[persistent-session] ${ready.length} ready task(s) for '${codeName}': ${ready.map((t) => `${t.name}(next=${t.nextFireAt ? new Date(t.nextFireAt).toISOString() : "null"})`).join(", ")}`);
|
|
6057
6167
|
}
|
|
@@ -6430,11 +6540,11 @@ ${escapeXml(msg.content)}
|
|
|
6430
6540
|
if (fw === "claude-code") {
|
|
6431
6541
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-XHJS2MZV.js");
|
|
6432
6542
|
const projDir = ccProjectDir(agent.codeName);
|
|
6433
|
-
const mcpConfigPath =
|
|
6543
|
+
const mcpConfigPath = join6(projDir, ".mcp.json");
|
|
6434
6544
|
const serverNames = [];
|
|
6435
6545
|
if (existsSync4(mcpConfigPath)) {
|
|
6436
6546
|
try {
|
|
6437
|
-
const d = JSON.parse(
|
|
6547
|
+
const d = JSON.parse(readFileSync5(mcpConfigPath, "utf-8"));
|
|
6438
6548
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
6439
6549
|
} catch {
|
|
6440
6550
|
}
|
|
@@ -6453,15 +6563,15 @@ ${escapeXml(msg.content)}
|
|
|
6453
6563
|
"--allowedTools",
|
|
6454
6564
|
allowedTools
|
|
6455
6565
|
];
|
|
6456
|
-
const chatClaudeMd =
|
|
6566
|
+
const chatClaudeMd = join6(projDir, "CLAUDE.md");
|
|
6457
6567
|
if (existsSync4(chatClaudeMd)) {
|
|
6458
6568
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
6459
6569
|
}
|
|
6460
|
-
const envIntPath =
|
|
6570
|
+
const envIntPath = join6(projDir, ".env.integrations");
|
|
6461
6571
|
const childEnv = { ...process.env };
|
|
6462
6572
|
if (existsSync4(envIntPath)) {
|
|
6463
6573
|
try {
|
|
6464
|
-
for (const line of
|
|
6574
|
+
for (const line of readFileSync5(envIntPath, "utf-8").split("\n")) {
|
|
6465
6575
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
6466
6576
|
const eqIdx = line.indexOf("=");
|
|
6467
6577
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -6804,12 +6914,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
6804
6914
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
6805
6915
|
try {
|
|
6806
6916
|
const candidates = [
|
|
6807
|
-
|
|
6808
|
-
|
|
6917
|
+
join6(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
6918
|
+
join6(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
6809
6919
|
];
|
|
6810
6920
|
for (const candidate of candidates) {
|
|
6811
6921
|
if (existsSync4(candidate)) {
|
|
6812
|
-
const content =
|
|
6922
|
+
const content = readFileSync5(candidate, "utf-8");
|
|
6813
6923
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
6814
6924
|
builtInSkillCache.set(skillId, files);
|
|
6815
6925
|
return files;
|
|
@@ -7422,7 +7532,7 @@ async function processClaudePairSessions(agents) {
|
|
|
7422
7532
|
killPairSession,
|
|
7423
7533
|
pairTmuxSession,
|
|
7424
7534
|
finalizeClaudePairOnboarding
|
|
7425
|
-
} = await import("../claude-pair-runtime-
|
|
7535
|
+
} = await import("../claude-pair-runtime-ZQAJOQPN.js");
|
|
7426
7536
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
7427
7537
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
7428
7538
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -7717,8 +7827,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
7717
7827
|
};
|
|
7718
7828
|
}
|
|
7719
7829
|
async function syncMemories(agent, configDir, log2) {
|
|
7720
|
-
const projectDir =
|
|
7721
|
-
const memoryDir =
|
|
7830
|
+
const projectDir = join6(configDir, agent.code_name, "project");
|
|
7831
|
+
const memoryDir = join6(projectDir, "memory");
|
|
7722
7832
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
7723
7833
|
if (isFreshSync) {
|
|
7724
7834
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -7733,10 +7843,10 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7733
7843
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
7734
7844
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
7735
7845
|
const changedMemories = [];
|
|
7736
|
-
for (const file of
|
|
7846
|
+
for (const file of readdirSync3(memoryDir)) {
|
|
7737
7847
|
if (!file.endsWith(".md")) continue;
|
|
7738
7848
|
try {
|
|
7739
|
-
const raw =
|
|
7849
|
+
const raw = readFileSync5(join6(memoryDir, file), "utf-8");
|
|
7740
7850
|
const fileHash = createHash2("sha256").update(raw).digest("hex").slice(0, 16);
|
|
7741
7851
|
currentHashes.set(file, fileHash);
|
|
7742
7852
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -7761,7 +7871,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7761
7871
|
} catch (err) {
|
|
7762
7872
|
for (const mem of changedMemories) {
|
|
7763
7873
|
for (const [file] of currentHashes) {
|
|
7764
|
-
const parsed = parseMemoryFile(
|
|
7874
|
+
const parsed = parseMemoryFile(readFileSync5(join6(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
7765
7875
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
7766
7876
|
}
|
|
7767
7877
|
}
|
|
@@ -7774,7 +7884,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7774
7884
|
}
|
|
7775
7885
|
}
|
|
7776
7886
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
7777
|
-
const localFiles = existsSync4(memoryDir) ?
|
|
7887
|
+
const localFiles = existsSync4(memoryDir) ? readdirSync3(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
7778
7888
|
const localListHash = createHash2("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
7779
7889
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
7780
7890
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
@@ -7796,7 +7906,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
|
7796
7906
|
const mem = dbMemories.memories[i];
|
|
7797
7907
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
7798
7908
|
const slug = rawSlug || `memory-${i}`;
|
|
7799
|
-
const filePath =
|
|
7909
|
+
const filePath = join6(memoryDir, `${slug}.md`);
|
|
7800
7910
|
const desired = `---
|
|
7801
7911
|
name: ${JSON.stringify(mem.name)}
|
|
7802
7912
|
type: ${mem.type}
|
|
@@ -7808,7 +7918,7 @@ ${mem.content}
|
|
|
7808
7918
|
if (existsSync4(filePath)) {
|
|
7809
7919
|
let existing = "";
|
|
7810
7920
|
try {
|
|
7811
|
-
existing =
|
|
7921
|
+
existing = readFileSync5(filePath, "utf-8");
|
|
7812
7922
|
} catch {
|
|
7813
7923
|
}
|
|
7814
7924
|
if (existing === desired) continue;
|
|
@@ -7820,7 +7930,7 @@ ${mem.content}
|
|
|
7820
7930
|
}
|
|
7821
7931
|
}
|
|
7822
7932
|
if (written > 0 || overwritten > 0) {
|
|
7823
|
-
const updatedFiles =
|
|
7933
|
+
const updatedFiles = readdirSync3(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
7824
7934
|
lastLocalFileHash.set(agent.agent_id, createHash2("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
7825
7935
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
7826
7936
|
}
|
|
@@ -8004,11 +8114,11 @@ function startManager(opts) {
|
|
|
8004
8114
|
try {
|
|
8005
8115
|
const stateFile = getStateFile();
|
|
8006
8116
|
if (existsSync4(stateFile)) {
|
|
8007
|
-
const raw =
|
|
8117
|
+
const raw = readFileSync5(stateFile, "utf-8");
|
|
8008
8118
|
const parsed = JSON.parse(raw);
|
|
8009
8119
|
if (Array.isArray(parsed.agents)) {
|
|
8010
|
-
|
|
8011
|
-
log(`[startup] rehydrated ${
|
|
8120
|
+
state4.agents = parsed.agents;
|
|
8121
|
+
log(`[startup] rehydrated ${state4.agents.length} agent state(s) from ${stateFile}`);
|
|
8012
8122
|
}
|
|
8013
8123
|
if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
|
|
8014
8124
|
restartBreaker.hydrate(parsed.circuitBreakerTrips);
|
|
@@ -8020,7 +8130,7 @@ function startManager(opts) {
|
|
|
8020
8130
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
8021
8131
|
}
|
|
8022
8132
|
log(
|
|
8023
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
8133
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join6(homedir4(), ".augmented", "manager.log")}`
|
|
8024
8134
|
);
|
|
8025
8135
|
warnOnConflictingKanbanModes();
|
|
8026
8136
|
deployMcpAssets();
|
|
@@ -8083,14 +8193,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
8083
8193
|
}
|
|
8084
8194
|
}
|
|
8085
8195
|
function deployMcpAssets() {
|
|
8086
|
-
const targetDir =
|
|
8196
|
+
const targetDir = join6(homedir4(), ".augmented", "_mcp");
|
|
8087
8197
|
mkdirSync2(targetDir, { recursive: true });
|
|
8088
8198
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
8089
8199
|
let mcpSourceDir = "";
|
|
8090
8200
|
let dir = moduleDir;
|
|
8091
8201
|
for (let i = 0; i < 6; i++) {
|
|
8092
|
-
const candidate =
|
|
8093
|
-
if (existsSync4(
|
|
8202
|
+
const candidate = join6(dir, "dist", "mcp");
|
|
8203
|
+
if (existsSync4(join6(candidate, "index.js"))) {
|
|
8094
8204
|
mcpSourceDir = candidate;
|
|
8095
8205
|
break;
|
|
8096
8206
|
}
|
|
@@ -8106,7 +8216,7 @@ function deployMcpAssets() {
|
|
|
8106
8216
|
const fileHash = (p) => {
|
|
8107
8217
|
try {
|
|
8108
8218
|
if (!existsSync4(p)) return null;
|
|
8109
|
-
return createHash2("sha256").update(
|
|
8219
|
+
return createHash2("sha256").update(readFileSync5(p)).digest("hex");
|
|
8110
8220
|
} catch {
|
|
8111
8221
|
return null;
|
|
8112
8222
|
}
|
|
@@ -8117,8 +8227,8 @@ function deployMcpAssets() {
|
|
|
8117
8227
|
"telegram-channel.js"
|
|
8118
8228
|
]);
|
|
8119
8229
|
for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
|
|
8120
|
-
const src =
|
|
8121
|
-
const dst =
|
|
8230
|
+
const src = join6(mcpSourceDir, file);
|
|
8231
|
+
const dst = join6(targetDir, file);
|
|
8122
8232
|
if (!existsSync4(src)) continue;
|
|
8123
8233
|
const before = fileHash(dst);
|
|
8124
8234
|
try {
|
|
@@ -8136,16 +8246,16 @@ function deployMcpAssets() {
|
|
|
8136
8246
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
8137
8247
|
restartRunningChannelMcps(changedBasenames);
|
|
8138
8248
|
}
|
|
8139
|
-
const localMcpPath =
|
|
8249
|
+
const localMcpPath = join6(targetDir, "index.js");
|
|
8140
8250
|
try {
|
|
8141
|
-
const agentsDir =
|
|
8251
|
+
const agentsDir = join6(homedir4(), ".augmented", "agents");
|
|
8142
8252
|
if (existsSync4(agentsDir)) {
|
|
8143
|
-
for (const entry of
|
|
8253
|
+
for (const entry of readdirSync3(agentsDir, { withFileTypes: true })) {
|
|
8144
8254
|
if (!entry.isDirectory()) continue;
|
|
8145
8255
|
for (const subdir of ["provision", "project"]) {
|
|
8146
|
-
const mcpJsonPath =
|
|
8256
|
+
const mcpJsonPath = join6(agentsDir, entry.name, subdir, ".mcp.json");
|
|
8147
8257
|
try {
|
|
8148
|
-
const raw =
|
|
8258
|
+
const raw = readFileSync5(mcpJsonPath, "utf-8");
|
|
8149
8259
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
8150
8260
|
const mcpConfig = JSON.parse(raw);
|
|
8151
8261
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|