@integrity-labs/agt-cli 0.24.0 → 0.24.2
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-WZTRMJM4.js → chunk-BXLWLI2U.js} +2 -2
- package/dist/{chunk-HSIESZMZ.js → chunk-ECDTRQLA.js} +89 -1
- package/dist/chunk-ECDTRQLA.js.map +1 -0
- 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 +320 -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-BXLWLI2U.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-BXLWLI2U.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,110 @@ 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
|
+
for (let i = 0; i < pending.length; i += MAX_ENTRIES_PER_POST) {
|
|
794
|
+
const batch = pending.slice(i, i + MAX_ENTRIES_PER_POST);
|
|
795
|
+
try {
|
|
796
|
+
await api2.post("/host/token-usage", {
|
|
797
|
+
agent_id: agentId,
|
|
798
|
+
framework: "claude-code",
|
|
799
|
+
source: "transcript_jsonl",
|
|
800
|
+
entries: batch.map((b) => b.entry)
|
|
801
|
+
});
|
|
802
|
+
for (const b of batch) {
|
|
803
|
+
files.set(b.sessionId, b.fp);
|
|
804
|
+
}
|
|
805
|
+
} catch (err) {
|
|
806
|
+
log2(`[token-usage] POST failed for '${codeName}': ${err.message}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
state2.set(codeName, next);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// src/lib/activity-cache-monitor.ts
|
|
813
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
814
|
+
import { homedir } from "os";
|
|
815
|
+
import { join as join2 } from "path";
|
|
816
|
+
var MIN_CHECK_INTERVAL_MS3 = 6e4;
|
|
817
|
+
var STATS_CACHE_PATH = join2(homedir(), ".claude", "stats-cache.json");
|
|
719
818
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
720
|
-
var
|
|
819
|
+
var state3 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
721
820
|
function selectNewDailyRows(raw, lastObservedDate) {
|
|
722
821
|
let parsed;
|
|
723
822
|
try {
|
|
@@ -756,24 +855,24 @@ async function maybeReportActivityCache(args) {
|
|
|
756
855
|
const { api: api2, log: log2 } = args;
|
|
757
856
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
758
857
|
const nowMs = now.getTime();
|
|
759
|
-
if (nowMs -
|
|
760
|
-
|
|
858
|
+
if (nowMs - state3.lastCheckedAt < MIN_CHECK_INTERVAL_MS3) return;
|
|
859
|
+
state3.lastCheckedAt = nowMs;
|
|
761
860
|
if (!existsSync(STATS_CACHE_PATH)) {
|
|
762
861
|
return;
|
|
763
862
|
}
|
|
764
863
|
let raw;
|
|
765
864
|
try {
|
|
766
|
-
raw =
|
|
865
|
+
raw = readFileSync2(STATS_CACHE_PATH, "utf-8");
|
|
767
866
|
} catch (err) {
|
|
768
867
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
769
868
|
return;
|
|
770
869
|
}
|
|
771
|
-
const rows = selectNewDailyRows(raw,
|
|
870
|
+
const rows = selectNewDailyRows(raw, state3.lastObservedDate);
|
|
772
871
|
if (rows.length === 0) return;
|
|
773
872
|
for (const row of rows) {
|
|
774
873
|
try {
|
|
775
874
|
await api2.post("/host/activity-observations", row);
|
|
776
|
-
|
|
875
|
+
state3.lastObservedDate = row.date;
|
|
777
876
|
} catch (err) {
|
|
778
877
|
log2(
|
|
779
878
|
`[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
|
|
@@ -1213,7 +1312,7 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
1213
1312
|
// src/lib/claude-auth-detect.ts
|
|
1214
1313
|
import { readFile, readdir } from "fs/promises";
|
|
1215
1314
|
import { homedir as homedir2, platform } from "os";
|
|
1216
|
-
import { join as
|
|
1315
|
+
import { join as join3 } from "path";
|
|
1217
1316
|
import { execFile } from "child_process";
|
|
1218
1317
|
import { promisify } from "util";
|
|
1219
1318
|
var execFileAsync = promisify(execFile);
|
|
@@ -1228,8 +1327,8 @@ async function detectClaudeAuth() {
|
|
|
1228
1327
|
}
|
|
1229
1328
|
async function findClaudeCredentialsPaths() {
|
|
1230
1329
|
const candidates = [
|
|
1231
|
-
|
|
1232
|
-
|
|
1330
|
+
join3(homedir2(), ".claude", ".credentials.json"),
|
|
1331
|
+
join3(homedir2(), ".claude", "credentials.json")
|
|
1233
1332
|
];
|
|
1234
1333
|
const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
|
|
1235
1334
|
if (isLinuxRoot) {
|
|
@@ -1237,8 +1336,8 @@ async function findClaudeCredentialsPaths() {
|
|
|
1237
1336
|
const entries = await readdir("/home", { withFileTypes: true });
|
|
1238
1337
|
for (const entry of entries) {
|
|
1239
1338
|
if (!entry.isDirectory()) continue;
|
|
1240
|
-
candidates.push(
|
|
1241
|
-
candidates.push(
|
|
1339
|
+
candidates.push(join3("/home", entry.name, ".claude", ".credentials.json"));
|
|
1340
|
+
candidates.push(join3("/home", entry.name, ".claude", "credentials.json"));
|
|
1242
1341
|
}
|
|
1243
1342
|
} catch {
|
|
1244
1343
|
}
|
|
@@ -1330,18 +1429,18 @@ function normalize(value) {
|
|
|
1330
1429
|
}
|
|
1331
1430
|
|
|
1332
1431
|
// src/lib/channel-hash-cache.ts
|
|
1333
|
-
import { existsSync as existsSync2, readFileSync as
|
|
1334
|
-
import { join as
|
|
1432
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1433
|
+
import { join as join4 } from "path";
|
|
1335
1434
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
1336
1435
|
function getChannelHashCacheFile(configDir) {
|
|
1337
|
-
return
|
|
1436
|
+
return join4(configDir, CACHE_FILENAME);
|
|
1338
1437
|
}
|
|
1339
1438
|
function loadChannelHashCache(target, configDir) {
|
|
1340
1439
|
const path = getChannelHashCacheFile(configDir);
|
|
1341
1440
|
if (!existsSync2(path)) return;
|
|
1342
1441
|
let parsed;
|
|
1343
1442
|
try {
|
|
1344
|
-
parsed = JSON.parse(
|
|
1443
|
+
parsed = JSON.parse(readFileSync3(path, "utf-8"));
|
|
1345
1444
|
} catch {
|
|
1346
1445
|
return;
|
|
1347
1446
|
}
|
|
@@ -1814,24 +1913,24 @@ function withScheduleLinkFooter(opts) {
|
|
|
1814
1913
|
}
|
|
1815
1914
|
|
|
1816
1915
|
// src/lib/restart-flags.ts
|
|
1817
|
-
import { existsSync as existsSync3, mkdirSync, readdirSync, readFileSync as
|
|
1916
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync4, renameSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1818
1917
|
import { homedir as homedir3 } from "os";
|
|
1819
|
-
import { join as
|
|
1918
|
+
import { join as join5 } from "path";
|
|
1820
1919
|
import { randomUUID } from "crypto";
|
|
1821
1920
|
function restartFlagsDir() {
|
|
1822
|
-
return
|
|
1921
|
+
return join5(homedir3(), ".augmented", "restart-flags");
|
|
1823
1922
|
}
|
|
1824
1923
|
function flagPath(codeName) {
|
|
1825
|
-
return
|
|
1924
|
+
return join5(restartFlagsDir(), `${codeName}.flag`);
|
|
1826
1925
|
}
|
|
1827
1926
|
function readRestartFlags() {
|
|
1828
1927
|
const dir = restartFlagsDir();
|
|
1829
1928
|
if (!existsSync3(dir)) return [];
|
|
1830
1929
|
const out = [];
|
|
1831
|
-
for (const entry of
|
|
1930
|
+
for (const entry of readdirSync2(dir)) {
|
|
1832
1931
|
if (!entry.endsWith(".flag")) continue;
|
|
1833
1932
|
try {
|
|
1834
|
-
const raw =
|
|
1933
|
+
const raw = readFileSync4(join5(dir, entry), "utf8");
|
|
1835
1934
|
const parsed = JSON.parse(raw);
|
|
1836
1935
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
1837
1936
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -2347,8 +2446,8 @@ function applyRestartAcks(args) {
|
|
|
2347
2446
|
var GATEWAY_PORT_BASE = 18800;
|
|
2348
2447
|
var GATEWAY_PORT_STEP = 10;
|
|
2349
2448
|
var GATEWAY_PORT_MAX = 18899;
|
|
2350
|
-
var AUGMENTED_DIR =
|
|
2351
|
-
var GATEWAY_PORTS_FILE =
|
|
2449
|
+
var AUGMENTED_DIR = join6(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
2450
|
+
var GATEWAY_PORTS_FILE = join6(AUGMENTED_DIR, "gateway-ports.json");
|
|
2352
2451
|
var CHANNEL_SWEEP_INTERVAL_MS = (() => {
|
|
2353
2452
|
const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
|
|
2354
2453
|
if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
|
|
@@ -2455,8 +2554,8 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
|
|
|
2455
2554
|
function shouldSkipRevokedCleanup(previousKnownStatus) {
|
|
2456
2555
|
return previousKnownStatus === "revoked";
|
|
2457
2556
|
}
|
|
2458
|
-
function hasRevokedResiduals(
|
|
2459
|
-
return
|
|
2557
|
+
function hasRevokedResiduals(state5) {
|
|
2558
|
+
return state5.gatewayRunning || state5.portAllocated || state5.provisionDirExists;
|
|
2460
2559
|
}
|
|
2461
2560
|
var knownVersions = /* @__PURE__ */ new Map();
|
|
2462
2561
|
var knownStatuses = /* @__PURE__ */ new Map();
|
|
@@ -2522,7 +2621,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
|
2522
2621
|
var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
2523
2622
|
function projectMcpHash(_codeName, projectDir) {
|
|
2524
2623
|
try {
|
|
2525
|
-
const raw =
|
|
2624
|
+
const raw = readFileSync5(join6(projectDir, ".mcp.json"), "utf-8");
|
|
2526
2625
|
return createHash2("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
2527
2626
|
} catch {
|
|
2528
2627
|
return null;
|
|
@@ -2530,7 +2629,7 @@ function projectMcpHash(_codeName, projectDir) {
|
|
|
2530
2629
|
}
|
|
2531
2630
|
function projectMcpKeys(_codeName, projectDir) {
|
|
2532
2631
|
try {
|
|
2533
|
-
const raw =
|
|
2632
|
+
const raw = readFileSync5(join6(projectDir, ".mcp.json"), "utf-8");
|
|
2534
2633
|
const parsed = JSON.parse(raw);
|
|
2535
2634
|
const servers = parsed.mcpServers;
|
|
2536
2635
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -2627,7 +2726,7 @@ var codeNameToAgentId = /* @__PURE__ */ new Map();
|
|
|
2627
2726
|
var agentChannelTokens = /* @__PURE__ */ new Map();
|
|
2628
2727
|
var activeChannels = /* @__PURE__ */ new Map();
|
|
2629
2728
|
var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
|
|
2630
|
-
var
|
|
2729
|
+
var state4 = {
|
|
2631
2730
|
pid: process.pid,
|
|
2632
2731
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2633
2732
|
lastPollAt: null,
|
|
@@ -2684,7 +2783,7 @@ var cachedFrameworkVersion = null;
|
|
|
2684
2783
|
var lastVersionCheckAt = 0;
|
|
2685
2784
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2686
2785
|
var lastResponsivenessProbeAt = 0;
|
|
2687
|
-
var agtCliVersion = true ? "0.24.
|
|
2786
|
+
var agtCliVersion = true ? "0.24.2" : "dev";
|
|
2688
2787
|
function resolveBrewPath(execFileSync4) {
|
|
2689
2788
|
try {
|
|
2690
2789
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -2901,7 +3000,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
2901
3000
|
var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2902
3001
|
var claudeCodeUpgradeInFlight = false;
|
|
2903
3002
|
function claudeCodeUpgradeMarkerPath() {
|
|
2904
|
-
return
|
|
3003
|
+
return join6(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
|
|
2905
3004
|
}
|
|
2906
3005
|
function stampClaudeCodeUpgradeMarker() {
|
|
2907
3006
|
try {
|
|
@@ -2911,7 +3010,7 @@ function stampClaudeCodeUpgradeMarker() {
|
|
|
2911
3010
|
}
|
|
2912
3011
|
function claudeCodeUpgradeThrottled() {
|
|
2913
3012
|
try {
|
|
2914
|
-
const lastCheck = parseInt(
|
|
3013
|
+
const lastCheck = parseInt(readFileSync5(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
2915
3014
|
if (!Number.isFinite(lastCheck)) return false;
|
|
2916
3015
|
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
2917
3016
|
} catch {
|
|
@@ -2981,7 +3080,7 @@ async function checkAndUpdateCli() {
|
|
|
2981
3080
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
2982
3081
|
if (!isBrewFormula && !isNpmGlobal) return;
|
|
2983
3082
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
2984
|
-
const markerPath =
|
|
3083
|
+
const markerPath = join6(homedir4(), ".augmented", ".last-update-check");
|
|
2985
3084
|
try {
|
|
2986
3085
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
2987
3086
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
@@ -3145,9 +3244,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3145
3244
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
3146
3245
|
}
|
|
3147
3246
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
3148
|
-
const claudeDir =
|
|
3247
|
+
const claudeDir = join6(homedir4(), ".claude");
|
|
3149
3248
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
3150
|
-
const p =
|
|
3249
|
+
const p = join6(claudeDir, filename);
|
|
3151
3250
|
if (existsSync4(p)) {
|
|
3152
3251
|
try {
|
|
3153
3252
|
rmSync2(p, { force: true });
|
|
@@ -3162,7 +3261,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3162
3261
|
}
|
|
3163
3262
|
function loadGatewayPorts() {
|
|
3164
3263
|
try {
|
|
3165
|
-
return JSON.parse(
|
|
3264
|
+
return JSON.parse(readFileSync5(GATEWAY_PORTS_FILE, "utf-8"));
|
|
3166
3265
|
} catch {
|
|
3167
3266
|
return {};
|
|
3168
3267
|
}
|
|
@@ -3192,10 +3291,10 @@ function freePort(codeName) {
|
|
|
3192
3291
|
}
|
|
3193
3292
|
}
|
|
3194
3293
|
function getStateFile() {
|
|
3195
|
-
return
|
|
3294
|
+
return join6(config?.configDir ?? join6(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
3196
3295
|
}
|
|
3197
3296
|
function channelHashCacheDir() {
|
|
3198
|
-
return config?.configDir ??
|
|
3297
|
+
return config?.configDir ?? join6(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
3199
3298
|
}
|
|
3200
3299
|
function loadChannelHashCache2() {
|
|
3201
3300
|
loadChannelHashCache(knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -3237,7 +3336,7 @@ function log(msg) {
|
|
|
3237
3336
|
`;
|
|
3238
3337
|
if (!managerLogPath) {
|
|
3239
3338
|
try {
|
|
3240
|
-
managerLogPath =
|
|
3339
|
+
managerLogPath = join6(homedir4(), ".augmented", "manager.log");
|
|
3241
3340
|
mkdirSync2(dirname(managerLogPath), { recursive: true });
|
|
3242
3341
|
if (existsSync4(managerLogPath)) {
|
|
3243
3342
|
chmodSync(managerLogPath, 384);
|
|
@@ -3267,7 +3366,7 @@ function sha256(content) {
|
|
|
3267
3366
|
}
|
|
3268
3367
|
function hashFile(filePath) {
|
|
3269
3368
|
try {
|
|
3270
|
-
const content =
|
|
3369
|
+
const content = readFileSync5(filePath, "utf-8");
|
|
3271
3370
|
return sha256(content);
|
|
3272
3371
|
} catch {
|
|
3273
3372
|
return null;
|
|
@@ -3291,13 +3390,13 @@ function parseSkillFrontmatter(content) {
|
|
|
3291
3390
|
return out;
|
|
3292
3391
|
}
|
|
3293
3392
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
3294
|
-
const { readdirSync:
|
|
3295
|
-
const skillsDir =
|
|
3296
|
-
const claudeMdPath =
|
|
3393
|
+
const { readdirSync: readdirSync4, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync4 } = await import("fs");
|
|
3394
|
+
const skillsDir = join6(configDir, codeName, "project", ".claude", "skills");
|
|
3395
|
+
const claudeMdPath = join6(configDir, codeName, "project", "CLAUDE.md");
|
|
3297
3396
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
3298
3397
|
const entries = [];
|
|
3299
|
-
for (const dir of
|
|
3300
|
-
const skillFile =
|
|
3398
|
+
for (const dir of readdirSync4(skillsDir).sort()) {
|
|
3399
|
+
const skillFile = join6(skillsDir, dir, "SKILL.md");
|
|
3301
3400
|
if (!ex(skillFile)) continue;
|
|
3302
3401
|
try {
|
|
3303
3402
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -3345,10 +3444,10 @@ ${SKILLS_INDEX_END}`;
|
|
|
3345
3444
|
}
|
|
3346
3445
|
async function migrateToProfiles() {
|
|
3347
3446
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3348
|
-
const sharedConfigPath =
|
|
3447
|
+
const sharedConfigPath = join6(homeDir, ".openclaw", "openclaw.json");
|
|
3349
3448
|
let sharedConfig;
|
|
3350
3449
|
try {
|
|
3351
|
-
sharedConfig = JSON.parse(
|
|
3450
|
+
sharedConfig = JSON.parse(readFileSync5(sharedConfigPath, "utf-8"));
|
|
3352
3451
|
} catch {
|
|
3353
3452
|
return;
|
|
3354
3453
|
}
|
|
@@ -3361,19 +3460,19 @@ async function migrateToProfiles() {
|
|
|
3361
3460
|
const codeName = agentEntry["id"];
|
|
3362
3461
|
if (!codeName) continue;
|
|
3363
3462
|
if (codeName === "main") continue;
|
|
3364
|
-
const profileDir =
|
|
3365
|
-
if (existsSync4(
|
|
3463
|
+
const profileDir = join6(homeDir, `.openclaw-${codeName}`);
|
|
3464
|
+
if (existsSync4(join6(profileDir, "openclaw.json"))) continue;
|
|
3366
3465
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
3367
3466
|
if (adapter.seedProfileConfig) {
|
|
3368
3467
|
adapter.seedProfileConfig(codeName);
|
|
3369
3468
|
}
|
|
3370
|
-
const sharedAuthDir =
|
|
3371
|
-
const profileAuthDir =
|
|
3372
|
-
const authFile =
|
|
3469
|
+
const sharedAuthDir = join6(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
3470
|
+
const profileAuthDir = join6(profileDir, "agents", codeName, "agent");
|
|
3471
|
+
const authFile = join6(sharedAuthDir, "auth-profiles.json");
|
|
3373
3472
|
if (existsSync4(authFile)) {
|
|
3374
3473
|
mkdirSync2(profileAuthDir, { recursive: true });
|
|
3375
|
-
const authContent =
|
|
3376
|
-
writeFileSync3(
|
|
3474
|
+
const authContent = readFileSync5(authFile, "utf-8");
|
|
3475
|
+
writeFileSync3(join6(profileAuthDir, "auth-profiles.json"), authContent);
|
|
3377
3476
|
}
|
|
3378
3477
|
allocatePort(codeName);
|
|
3379
3478
|
migrated++;
|
|
@@ -3411,7 +3510,7 @@ function readGatewayToken(codeName) {
|
|
|
3411
3510
|
}
|
|
3412
3511
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3413
3512
|
try {
|
|
3414
|
-
const cfg = JSON.parse(
|
|
3513
|
+
const cfg = JSON.parse(readFileSync5(join6(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
3415
3514
|
return cfg?.gateway?.auth?.token;
|
|
3416
3515
|
} catch {
|
|
3417
3516
|
return void 0;
|
|
@@ -3420,18 +3519,18 @@ function readGatewayToken(codeName) {
|
|
|
3420
3519
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
3421
3520
|
function isGatewayHung(codeName) {
|
|
3422
3521
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3423
|
-
const jobsPath =
|
|
3522
|
+
const jobsPath = join6(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
3424
3523
|
if (!existsSync4(jobsPath)) return false;
|
|
3425
3524
|
try {
|
|
3426
|
-
const data = JSON.parse(
|
|
3525
|
+
const data = JSON.parse(readFileSync5(jobsPath, "utf-8"));
|
|
3427
3526
|
const jobs = data.jobs ?? data;
|
|
3428
3527
|
if (!Array.isArray(jobs)) return false;
|
|
3429
3528
|
const now = Date.now();
|
|
3430
3529
|
for (const job of jobs) {
|
|
3431
|
-
const
|
|
3432
|
-
if (!
|
|
3433
|
-
const runStartedAt =
|
|
3434
|
-
const isRunning =
|
|
3530
|
+
const state5 = job.state;
|
|
3531
|
+
if (!state5) continue;
|
|
3532
|
+
const runStartedAt = state5.runStartedAtMs;
|
|
3533
|
+
const isRunning = state5.status === "running" || state5.running === true;
|
|
3435
3534
|
if (isRunning && runStartedAt && now - runStartedAt > GATEWAY_HUNG_TIMEOUT_MS) {
|
|
3436
3535
|
return true;
|
|
3437
3536
|
}
|
|
@@ -3456,15 +3555,15 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
3456
3555
|
}
|
|
3457
3556
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
3458
3557
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3459
|
-
const cronJobsPath =
|
|
3558
|
+
const cronJobsPath = join6(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
3460
3559
|
clearStaleCronRunState(cronJobsPath);
|
|
3461
3560
|
} else {
|
|
3462
3561
|
if (status.port) {
|
|
3463
3562
|
try {
|
|
3464
3563
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3465
|
-
const configPath =
|
|
3564
|
+
const configPath = join6(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
3466
3565
|
if (existsSync4(configPath)) {
|
|
3467
|
-
const cfg = JSON.parse(
|
|
3566
|
+
const cfg = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
3468
3567
|
if (cfg.gateway?.port !== status.port) {
|
|
3469
3568
|
if (!cfg.gateway) cfg.gateway = {};
|
|
3470
3569
|
cfg.gateway.port = status.port;
|
|
@@ -3488,9 +3587,9 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
3488
3587
|
gatewaysStartedThisCycle.add(codeName);
|
|
3489
3588
|
try {
|
|
3490
3589
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3491
|
-
const configPath =
|
|
3590
|
+
const configPath = join6(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
3492
3591
|
if (existsSync4(configPath)) {
|
|
3493
|
-
const cfg = JSON.parse(
|
|
3592
|
+
const cfg = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
3494
3593
|
if (!cfg.gateway) cfg.gateway = {};
|
|
3495
3594
|
cfg.gateway.port = port;
|
|
3496
3595
|
writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
|
|
@@ -3632,7 +3731,7 @@ async function pollCycle() {
|
|
|
3632
3731
|
const now = Date.now();
|
|
3633
3732
|
if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
|
|
3634
3733
|
try {
|
|
3635
|
-
const firstAgent =
|
|
3734
|
+
const firstAgent = state4.agents[0];
|
|
3636
3735
|
const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework("openclaw");
|
|
3637
3736
|
if (versionAdapter.getVersion) {
|
|
3638
3737
|
cachedFrameworkVersion = await versionAdapter.getVersion();
|
|
@@ -3643,7 +3742,7 @@ async function pollCycle() {
|
|
|
3643
3742
|
}
|
|
3644
3743
|
try {
|
|
3645
3744
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
3646
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
3745
|
+
const { collectDiagnostics } = await import("../persistent-session-JUJY46HW.js");
|
|
3647
3746
|
const diagCodeNames = [...persistentSessionAgents];
|
|
3648
3747
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
3649
3748
|
let tailscaleHostname;
|
|
@@ -3694,7 +3793,7 @@ async function pollCycle() {
|
|
|
3694
3793
|
const {
|
|
3695
3794
|
collectResponsivenessProbes,
|
|
3696
3795
|
getResponsivenessIntervalMs
|
|
3697
|
-
} = await import("../responsiveness-probe-
|
|
3796
|
+
} = await import("../responsiveness-probe-XOEC7J5D.js");
|
|
3698
3797
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
3699
3798
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
3700
3799
|
const probeCodeNames = [...persistentSessionAgents];
|
|
@@ -3754,7 +3853,7 @@ async function pollCycle() {
|
|
|
3754
3853
|
for (const agent of agents) {
|
|
3755
3854
|
const requested = agent.restart_requested_at ?? null;
|
|
3756
3855
|
if (!requested) continue;
|
|
3757
|
-
const prev =
|
|
3856
|
+
const prev = state4.agents.find((a) => a.agentId === agent.agent_id);
|
|
3758
3857
|
const lastProcessed = prev?.lastRestartProcessedAt ?? null;
|
|
3759
3858
|
if (lastProcessed && Date.parse(lastProcessed) >= Date.parse(requested)) continue;
|
|
3760
3859
|
log(`[restart] Dashboard requested restart for '${agent.code_name}' at ${requested}`);
|
|
@@ -3777,7 +3876,7 @@ async function pollCycle() {
|
|
|
3777
3876
|
await processAgent(agent, agentStates);
|
|
3778
3877
|
} catch (err) {
|
|
3779
3878
|
log(`Error processing agent '${agent.code_name}': ${err.message}`);
|
|
3780
|
-
const existing =
|
|
3879
|
+
const existing = state4.agents.find((a) => a.agentId === agent.agent_id);
|
|
3781
3880
|
if (existing) {
|
|
3782
3881
|
agentStates.push(existing);
|
|
3783
3882
|
} else {
|
|
@@ -3803,12 +3902,12 @@ async function pollCycle() {
|
|
|
3803
3902
|
void maybeReportActivityCache({ api, log });
|
|
3804
3903
|
const restartAckStateChanged = applyRestartAcks({
|
|
3805
3904
|
agentStates,
|
|
3806
|
-
priorAgents:
|
|
3905
|
+
priorAgents: state4.agents,
|
|
3807
3906
|
restartAcks
|
|
3808
3907
|
});
|
|
3809
3908
|
if (restartAckStateChanged) {
|
|
3810
3909
|
try {
|
|
3811
|
-
const ackedState = { ...
|
|
3910
|
+
const ackedState = { ...state4, agents: agentStates };
|
|
3812
3911
|
writeFileSync3(getStateFile(), JSON.stringify(ackedState, null, 2));
|
|
3813
3912
|
} catch (err) {
|
|
3814
3913
|
log(`[restart] failed to persist ack immediately: ${err.message}`);
|
|
@@ -3826,7 +3925,7 @@ async function pollCycle() {
|
|
|
3826
3925
|
} catch {
|
|
3827
3926
|
}
|
|
3828
3927
|
const currentIds = new Set(agents.map((a) => a.agent_id));
|
|
3829
|
-
for (const prev of
|
|
3928
|
+
for (const prev of state4.agents) {
|
|
3830
3929
|
if (!currentIds.has(prev.agentId)) {
|
|
3831
3930
|
log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
|
|
3832
3931
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
@@ -3839,7 +3938,7 @@ async function pollCycle() {
|
|
|
3839
3938
|
}
|
|
3840
3939
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
3841
3940
|
freePort(prev.codeName);
|
|
3842
|
-
const agentDir =
|
|
3941
|
+
const agentDir = join6(adapter.getAgentDir(prev.codeName), "provision");
|
|
3843
3942
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
3844
3943
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
3845
3944
|
}
|
|
@@ -3926,10 +4025,10 @@ async function pollCycle() {
|
|
|
3926
4025
|
}
|
|
3927
4026
|
} catch {
|
|
3928
4027
|
}
|
|
3929
|
-
|
|
3930
|
-
...
|
|
4028
|
+
state4 = {
|
|
4029
|
+
...state4,
|
|
3931
4030
|
lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3932
|
-
pollCount:
|
|
4031
|
+
pollCount: state4.pollCount + 1,
|
|
3933
4032
|
agents: agentStates,
|
|
3934
4033
|
// ENG-5441: serialise trip state on every poll so manager restarts
|
|
3935
4034
|
// never silently clear a tripped breaker. Cheap — only tripped
|
|
@@ -3940,9 +4039,9 @@ async function pollCycle() {
|
|
|
3940
4039
|
log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
|
|
3941
4040
|
consecutivePollFailures = 0;
|
|
3942
4041
|
}
|
|
3943
|
-
send({ type: "state-update", state:
|
|
4042
|
+
send({ type: "state-update", state: state4 });
|
|
3944
4043
|
} catch (err) {
|
|
3945
|
-
|
|
4044
|
+
state4.errorCount++;
|
|
3946
4045
|
const message = err.message;
|
|
3947
4046
|
log(`Poll error: ${message}`);
|
|
3948
4047
|
send({ type: "error", message });
|
|
@@ -3980,10 +4079,16 @@ async function processAgent(agent, agentStates) {
|
|
|
3980
4079
|
agentId: agent.agent_id,
|
|
3981
4080
|
log
|
|
3982
4081
|
});
|
|
4082
|
+
void maybeReportTokenUsage({
|
|
4083
|
+
api,
|
|
4084
|
+
codeName: agent.code_name,
|
|
4085
|
+
agentId: agent.agent_id,
|
|
4086
|
+
log
|
|
4087
|
+
});
|
|
3983
4088
|
}
|
|
3984
4089
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3985
4090
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
3986
|
-
let agentDir =
|
|
4091
|
+
let agentDir = join6(adapter.getAgentDir(agent.code_name), "provision");
|
|
3987
4092
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
3988
4093
|
if (previousKnownStatus !== agent.status) {
|
|
3989
4094
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -4104,7 +4209,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4104
4209
|
});
|
|
4105
4210
|
} catch (err) {
|
|
4106
4211
|
log(`Refresh failed for '${agent.code_name}': ${err.message}`);
|
|
4107
|
-
const existing =
|
|
4212
|
+
const existing = state4.agents.find((a) => a.agentId === agent.agent_id);
|
|
4108
4213
|
agentStates.push(existing ?? {
|
|
4109
4214
|
agentId: agent.agent_id,
|
|
4110
4215
|
codeName: agent.code_name,
|
|
@@ -4152,7 +4257,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4152
4257
|
const frameworkId = refreshData.agent.framework ?? "openclaw";
|
|
4153
4258
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
4154
4259
|
const frameworkAdapter = getFramework(frameworkId);
|
|
4155
|
-
agentDir =
|
|
4260
|
+
agentDir = join6(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
4156
4261
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
4157
4262
|
if (frameworkAdapter.seedProfileConfig) {
|
|
4158
4263
|
frameworkAdapter.seedProfileConfig(agent.code_name);
|
|
@@ -4160,7 +4265,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4160
4265
|
const charterVersion = refreshData.charter.version;
|
|
4161
4266
|
const toolsVersion = refreshData.tools.version;
|
|
4162
4267
|
const known = knownVersions.get(agent.agent_id);
|
|
4163
|
-
let lastProvisionAt =
|
|
4268
|
+
let lastProvisionAt = state4.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
|
|
4164
4269
|
const currentChannelIds = launchableChannelIds(refreshData.channel_configs);
|
|
4165
4270
|
const previousChannelIds = knownChannels.get(agent.agent_id);
|
|
4166
4271
|
const channelsChanged = !previousChannelIds || currentChannelIds.size !== previousChannelIds.size || [...currentChannelIds].some((ch) => !previousChannelIds.has(ch)) || [...previousChannelIds].some((ch) => !currentChannelIds.has(ch));
|
|
@@ -4183,7 +4288,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4183
4288
|
const changedFiles = [];
|
|
4184
4289
|
mkdirSync2(agentDir, { recursive: true });
|
|
4185
4290
|
for (const artifact of artifacts) {
|
|
4186
|
-
const filePath =
|
|
4291
|
+
const filePath = join6(agentDir, artifact.relativePath);
|
|
4187
4292
|
let existingHash;
|
|
4188
4293
|
let newHash;
|
|
4189
4294
|
let writeContent = artifact.content;
|
|
@@ -4195,8 +4300,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4195
4300
|
};
|
|
4196
4301
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
4197
4302
|
try {
|
|
4198
|
-
const projectClaudeMd =
|
|
4199
|
-
const existing =
|
|
4303
|
+
const projectClaudeMd = join6(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
4304
|
+
const existing = readFileSync5(projectClaudeMd, "utf-8");
|
|
4200
4305
|
existingHash = sha256(stripDynamicSections(existing));
|
|
4201
4306
|
} catch {
|
|
4202
4307
|
existingHash = null;
|
|
@@ -4214,7 +4319,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4214
4319
|
const generatorKeys = Object.keys(generatorServers);
|
|
4215
4320
|
let existingRaw = "";
|
|
4216
4321
|
try {
|
|
4217
|
-
existingRaw =
|
|
4322
|
+
existingRaw = readFileSync5(filePath, "utf-8");
|
|
4218
4323
|
} catch {
|
|
4219
4324
|
}
|
|
4220
4325
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -4236,22 +4341,22 @@ async function processAgent(agent, agentStates) {
|
|
|
4236
4341
|
}
|
|
4237
4342
|
}
|
|
4238
4343
|
if (changedFiles.length > 0) {
|
|
4239
|
-
const isFirst = !existsSync4(
|
|
4344
|
+
const isFirst = !existsSync4(join6(agentDir, "CHARTER.md"));
|
|
4240
4345
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
4241
4346
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
4242
4347
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
4243
4348
|
for (const file of changedFiles) {
|
|
4244
|
-
const filePath =
|
|
4349
|
+
const filePath = join6(agentDir, file.relativePath);
|
|
4245
4350
|
mkdirSync2(dirname(filePath), { recursive: true });
|
|
4246
4351
|
writeFileSync3(filePath, file.content);
|
|
4247
4352
|
}
|
|
4248
4353
|
try {
|
|
4249
|
-
const provSkillsDir =
|
|
4354
|
+
const provSkillsDir = join6(agentDir, ".claude", "skills");
|
|
4250
4355
|
if (existsSync4(provSkillsDir)) {
|
|
4251
|
-
for (const folder of
|
|
4356
|
+
for (const folder of readdirSync3(provSkillsDir)) {
|
|
4252
4357
|
if (folder.startsWith("knowledge-")) {
|
|
4253
4358
|
try {
|
|
4254
|
-
rmSync2(
|
|
4359
|
+
rmSync2(join6(provSkillsDir, folder), { recursive: true });
|
|
4255
4360
|
} catch {
|
|
4256
4361
|
}
|
|
4257
4362
|
}
|
|
@@ -4264,7 +4369,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4264
4369
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
4265
4370
|
const hashes = /* @__PURE__ */ new Map();
|
|
4266
4371
|
for (const file of trackedFiles2) {
|
|
4267
|
-
const h = hashFile(
|
|
4372
|
+
const h = hashFile(join6(agentDir, file));
|
|
4268
4373
|
if (h) hashes.set(file, h);
|
|
4269
4374
|
}
|
|
4270
4375
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -4311,7 +4416,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4311
4416
|
if (written && existsSync4(agentDir)) {
|
|
4312
4417
|
const driftedFiles = [];
|
|
4313
4418
|
for (const [file, expectedHash] of written) {
|
|
4314
|
-
const localHash = hashFile(
|
|
4419
|
+
const localHash = hashFile(join6(agentDir, file));
|
|
4315
4420
|
if (localHash && localHash !== expectedHash) {
|
|
4316
4421
|
driftedFiles.push(file);
|
|
4317
4422
|
}
|
|
@@ -4322,7 +4427,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4322
4427
|
try {
|
|
4323
4428
|
const localHashes = {};
|
|
4324
4429
|
for (const file of driftedFiles) {
|
|
4325
|
-
localHashes[file] = hashFile(
|
|
4430
|
+
localHashes[file] = hashFile(join6(agentDir, file));
|
|
4326
4431
|
}
|
|
4327
4432
|
await api.post("/host/drift", {
|
|
4328
4433
|
agent_id: agent.agent_id,
|
|
@@ -4480,18 +4585,18 @@ async function processAgent(agent, agentStates) {
|
|
|
4480
4585
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
4481
4586
|
try {
|
|
4482
4587
|
const agentProvisionDir = agentDir;
|
|
4483
|
-
const projectDir =
|
|
4588
|
+
const projectDir = join6(homedir4(), ".augmented", agent.code_name, "project");
|
|
4484
4589
|
mkdirSync2(agentProvisionDir, { recursive: true });
|
|
4485
4590
|
mkdirSync2(projectDir, { recursive: true });
|
|
4486
|
-
const provisionMcpPath =
|
|
4487
|
-
const projectMcpPath =
|
|
4591
|
+
const provisionMcpPath = join6(agentProvisionDir, ".mcp.json");
|
|
4592
|
+
const projectMcpPath = join6(projectDir, ".mcp.json");
|
|
4488
4593
|
let mcpConfig = { mcpServers: {} };
|
|
4489
4594
|
try {
|
|
4490
|
-
mcpConfig = JSON.parse(
|
|
4595
|
+
mcpConfig = JSON.parse(readFileSync5(provisionMcpPath, "utf-8"));
|
|
4491
4596
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
4492
4597
|
} catch {
|
|
4493
4598
|
}
|
|
4494
|
-
const localDirectChatChannel =
|
|
4599
|
+
const localDirectChatChannel = join6(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
4495
4600
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
4496
4601
|
const directChatTz = (() => {
|
|
4497
4602
|
const tz = directChatTeamSettings?.["timezone"];
|
|
@@ -4513,7 +4618,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4513
4618
|
writeFileSync3(projectMcpPath, serialized);
|
|
4514
4619
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
4515
4620
|
}
|
|
4516
|
-
const staleChannelsPath =
|
|
4621
|
+
const staleChannelsPath = join6(projectDir, ".mcp-channels.json");
|
|
4517
4622
|
if (existsSync4(staleChannelsPath)) {
|
|
4518
4623
|
try {
|
|
4519
4624
|
rmSync2(staleChannelsPath, { force: true });
|
|
@@ -4524,7 +4629,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4524
4629
|
log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
|
|
4525
4630
|
}
|
|
4526
4631
|
}
|
|
4527
|
-
let lastSecretsProvisionAt =
|
|
4632
|
+
let lastSecretsProvisionAt = state4.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
|
|
4528
4633
|
let secretsHash = knownSecretsHashes.get(agent.agent_id) ?? null;
|
|
4529
4634
|
try {
|
|
4530
4635
|
const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
|
|
@@ -4580,11 +4685,11 @@ async function processAgent(agent, agentStates) {
|
|
|
4580
4685
|
const intHash = computeIntegrationsHash(integrations);
|
|
4581
4686
|
const prevIntHash = knownIntegrationHashes.get(agent.agent_id);
|
|
4582
4687
|
if (intHash !== prevIntHash) {
|
|
4583
|
-
const projectDir =
|
|
4584
|
-
const envIntPath =
|
|
4688
|
+
const projectDir = join6(homedir4(), ".augmented", agent.code_name, "project");
|
|
4689
|
+
const envIntPath = join6(projectDir, ".env.integrations");
|
|
4585
4690
|
let preWriteEnv;
|
|
4586
4691
|
try {
|
|
4587
|
-
preWriteEnv =
|
|
4692
|
+
preWriteEnv = readFileSync5(envIntPath, "utf-8");
|
|
4588
4693
|
} catch {
|
|
4589
4694
|
preWriteEnv = void 0;
|
|
4590
4695
|
}
|
|
@@ -4596,9 +4701,9 @@ async function processAgent(agent, agentStates) {
|
|
|
4596
4701
|
let rotationHandled = true;
|
|
4597
4702
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
4598
4703
|
try {
|
|
4599
|
-
const projectMcpPath =
|
|
4600
|
-
const postWriteEnv =
|
|
4601
|
-
const mcpContent =
|
|
4704
|
+
const projectMcpPath = join6(projectDir, ".mcp.json");
|
|
4705
|
+
const postWriteEnv = readFileSync5(envIntPath, "utf-8");
|
|
4706
|
+
const mcpContent = readFileSync5(projectMcpPath, "utf-8");
|
|
4602
4707
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
4603
4708
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
4604
4709
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -4664,8 +4769,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4664
4769
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
4665
4770
|
if (mcpPath) {
|
|
4666
4771
|
try {
|
|
4667
|
-
const { readFileSync:
|
|
4668
|
-
const mcpConfig = JSON.parse(
|
|
4772
|
+
const { readFileSync: readFileSync6 } = await import("fs");
|
|
4773
|
+
const mcpConfig = JSON.parse(readFileSync6(mcpPath, "utf-8"));
|
|
4669
4774
|
if (mcpConfig.mcpServers) {
|
|
4670
4775
|
const managedPrefixes = [
|
|
4671
4776
|
"composio_",
|
|
@@ -4754,7 +4859,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4754
4859
|
if (agent.status === "active") {
|
|
4755
4860
|
if (frameworkAdapter.installPlugin) {
|
|
4756
4861
|
try {
|
|
4757
|
-
const pluginPath =
|
|
4862
|
+
const pluginPath = join6(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
4758
4863
|
if (existsSync4(pluginPath)) {
|
|
4759
4864
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
4760
4865
|
agtHost: requireHost(),
|
|
@@ -4820,25 +4925,25 @@ async function processAgent(agent, agentStates) {
|
|
|
4820
4925
|
}
|
|
4821
4926
|
}
|
|
4822
4927
|
try {
|
|
4823
|
-
const { readdirSync:
|
|
4928
|
+
const { readdirSync: readdirSync4, rmSync: rmSync3 } = await import("fs");
|
|
4824
4929
|
const { homedir: homedir5 } = await import("os");
|
|
4825
4930
|
const frameworkId2 = frameworkAdapter.id;
|
|
4826
4931
|
const candidateSkillDirs = [
|
|
4827
4932
|
// Claude Code — framework runtime tree
|
|
4828
|
-
|
|
4933
|
+
join6(homedir5(), ".augmented", agent.code_name, "skills"),
|
|
4829
4934
|
// Claude Code — project tree
|
|
4830
|
-
|
|
4935
|
+
join6(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
4831
4936
|
// OpenClaw — framework runtime tree
|
|
4832
|
-
|
|
4937
|
+
join6(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
|
|
4833
4938
|
// Defensive: legacy provision-side path, not currently an
|
|
4834
4939
|
// install target but cheap to sweep.
|
|
4835
|
-
|
|
4940
|
+
join6(agentDir, ".claude", "skills")
|
|
4836
4941
|
];
|
|
4837
4942
|
const existingDirs = candidateSkillDirs.filter((d) => existsSync4(d));
|
|
4838
4943
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
4839
4944
|
for (const dir of existingDirs) {
|
|
4840
4945
|
try {
|
|
4841
|
-
for (const entry of
|
|
4946
|
+
for (const entry of readdirSync4(dir)) {
|
|
4842
4947
|
if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
|
|
4843
4948
|
discoveredEntries.add(entry);
|
|
4844
4949
|
}
|
|
@@ -4848,7 +4953,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4848
4953
|
}
|
|
4849
4954
|
const removeSkillFolder = (entry, reason) => {
|
|
4850
4955
|
for (const dir of existingDirs) {
|
|
4851
|
-
const p =
|
|
4956
|
+
const p = join6(dir, entry);
|
|
4852
4957
|
if (existsSync4(p)) {
|
|
4853
4958
|
rmSync3(p, { recursive: true, force: true });
|
|
4854
4959
|
}
|
|
@@ -5009,8 +5114,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5009
5114
|
const sess = getSessionState(agent.code_name);
|
|
5010
5115
|
let mcpJsonParsed = null;
|
|
5011
5116
|
try {
|
|
5012
|
-
const mcpPath =
|
|
5013
|
-
mcpJsonParsed = JSON.parse(
|
|
5117
|
+
const mcpPath = join6(getProjectDir(agent.code_name), ".mcp.json");
|
|
5118
|
+
mcpJsonParsed = JSON.parse(readFileSync5(mcpPath, "utf-8"));
|
|
5014
5119
|
} catch {
|
|
5015
5120
|
}
|
|
5016
5121
|
reapMissingMcpSessions({
|
|
@@ -5159,10 +5264,10 @@ async function processAgent(agent, agentStates) {
|
|
|
5159
5264
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
5160
5265
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
5161
5266
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
5162
|
-
const jobsPath =
|
|
5267
|
+
const jobsPath = join6(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
5163
5268
|
if (existsSync4(jobsPath)) {
|
|
5164
5269
|
try {
|
|
5165
|
-
const jobsData = JSON.parse(
|
|
5270
|
+
const jobsData = JSON.parse(readFileSync5(jobsPath, "utf-8"));
|
|
5166
5271
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
5167
5272
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
5168
5273
|
);
|
|
@@ -5306,7 +5411,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
5306
5411
|
if (trackedFiles.length > 0 && existsSync4(agentDir)) {
|
|
5307
5412
|
const hashes = /* @__PURE__ */ new Map();
|
|
5308
5413
|
for (const file of trackedFiles) {
|
|
5309
|
-
const h = hashFile(
|
|
5414
|
+
const h = hashFile(join6(agentDir, file));
|
|
5310
5415
|
if (h) hashes.set(file, h);
|
|
5311
5416
|
}
|
|
5312
5417
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -5340,19 +5445,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
5340
5445
|
lastCleanupAt.set(codeName, Date.now());
|
|
5341
5446
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
5342
5447
|
for (const agentDir of ["main", codeName]) {
|
|
5343
|
-
const sessionsDir =
|
|
5448
|
+
const sessionsDir = join6(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
5344
5449
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
5345
5450
|
}
|
|
5346
|
-
const cronRunsDir =
|
|
5451
|
+
const cronRunsDir = join6(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
5347
5452
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
5348
|
-
const cronJobsPath =
|
|
5453
|
+
const cronJobsPath = join6(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
5349
5454
|
clearStaleCronRunState(cronJobsPath);
|
|
5350
5455
|
}
|
|
5351
5456
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
5352
|
-
const indexPath =
|
|
5457
|
+
const indexPath = join6(sessionsDir, "sessions.json");
|
|
5353
5458
|
if (!existsSync4(indexPath)) return;
|
|
5354
5459
|
try {
|
|
5355
|
-
const raw =
|
|
5460
|
+
const raw = readFileSync5(indexPath, "utf-8");
|
|
5356
5461
|
const index = JSON.parse(raw);
|
|
5357
5462
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
5358
5463
|
key: k,
|
|
@@ -5365,7 +5470,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5365
5470
|
for (const entry of toDelete) {
|
|
5366
5471
|
delete index[entry.key];
|
|
5367
5472
|
if (entry.sessionId) {
|
|
5368
|
-
const sessionFile =
|
|
5473
|
+
const sessionFile = join6(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
5369
5474
|
try {
|
|
5370
5475
|
if (existsSync4(sessionFile)) {
|
|
5371
5476
|
unlinkSync(sessionFile);
|
|
@@ -5387,7 +5492,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5387
5492
|
delete index[parentKey];
|
|
5388
5493
|
if (parentSessionId) {
|
|
5389
5494
|
try {
|
|
5390
|
-
const f =
|
|
5495
|
+
const f = join6(sessionsDir, `${parentSessionId}.jsonl`);
|
|
5391
5496
|
if (existsSync4(f)) {
|
|
5392
5497
|
unlinkSync(f);
|
|
5393
5498
|
deletedFiles++;
|
|
@@ -5408,26 +5513,26 @@ var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
|
5408
5513
|
function clearStaleCronRunState(jobsPath) {
|
|
5409
5514
|
if (!existsSync4(jobsPath)) return;
|
|
5410
5515
|
try {
|
|
5411
|
-
const raw =
|
|
5516
|
+
const raw = readFileSync5(jobsPath, "utf-8");
|
|
5412
5517
|
const data = JSON.parse(raw);
|
|
5413
5518
|
const jobs = data.jobs ?? data;
|
|
5414
5519
|
if (!Array.isArray(jobs)) return;
|
|
5415
5520
|
let changed = false;
|
|
5416
5521
|
const now = Date.now();
|
|
5417
5522
|
for (const job of jobs) {
|
|
5418
|
-
const
|
|
5419
|
-
if (!
|
|
5420
|
-
const runStartedAt =
|
|
5421
|
-
const isRunning =
|
|
5523
|
+
const state5 = job.state;
|
|
5524
|
+
if (!state5) continue;
|
|
5525
|
+
const runStartedAt = state5.runningAtMs ?? state5.runStartedAtMs;
|
|
5526
|
+
const isRunning = state5.running === true || state5.status === "running";
|
|
5422
5527
|
if (isRunning && runStartedAt && now - runStartedAt > STALE_RUN_TIMEOUT_MS) {
|
|
5423
|
-
|
|
5424
|
-
delete
|
|
5425
|
-
delete
|
|
5426
|
-
delete
|
|
5528
|
+
state5.running = false;
|
|
5529
|
+
delete state5.status;
|
|
5530
|
+
delete state5.runStartedAtMs;
|
|
5531
|
+
delete state5.currentRunId;
|
|
5427
5532
|
changed = true;
|
|
5428
5533
|
log(`Cleared stale running state for cron job '${job.name}' (stuck for ${Math.round((now - runStartedAt) / 6e4)}min)`);
|
|
5429
5534
|
} else if (isRunning && !runStartedAt) {
|
|
5430
|
-
|
|
5535
|
+
state5.running = false;
|
|
5431
5536
|
changed = true;
|
|
5432
5537
|
log(`Cleared stale running state for cron job '${job.name}' (no start timestamp)`);
|
|
5433
5538
|
}
|
|
@@ -5443,11 +5548,11 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
|
5443
5548
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
5444
5549
|
let removed = 0;
|
|
5445
5550
|
try {
|
|
5446
|
-
for (const f of
|
|
5551
|
+
for (const f of readdirSync3(dir)) {
|
|
5447
5552
|
if (!f.endsWith(ext)) continue;
|
|
5448
|
-
const fullPath =
|
|
5553
|
+
const fullPath = join6(dir, f);
|
|
5449
5554
|
try {
|
|
5450
|
-
const st =
|
|
5555
|
+
const st = statSync2(fullPath);
|
|
5451
5556
|
if (st.mtimeMs < cutoff) {
|
|
5452
5557
|
unlinkSync(fullPath);
|
|
5453
5558
|
removed++;
|
|
@@ -5491,16 +5596,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
5491
5596
|
enabled: t.enabled ?? true,
|
|
5492
5597
|
triggered_at: t.triggered_at ?? null
|
|
5493
5598
|
}));
|
|
5494
|
-
const
|
|
5495
|
-
claudeSchedulerStates.set(codeName,
|
|
5599
|
+
const state6 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
5600
|
+
claudeSchedulerStates.set(codeName, state6);
|
|
5496
5601
|
knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
5497
5602
|
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
5498
5603
|
}
|
|
5499
5604
|
if (!claudeSchedulerStates.has(codeName)) {
|
|
5500
5605
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
5501
5606
|
}
|
|
5502
|
-
const
|
|
5503
|
-
const ready = getReadyTasks(
|
|
5607
|
+
const state5 = claudeSchedulerStates.get(codeName);
|
|
5608
|
+
const ready = getReadyTasks(state5, inFlightClaudeTasks);
|
|
5504
5609
|
if (ready.length === 0) return;
|
|
5505
5610
|
for (const task of ready) {
|
|
5506
5611
|
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
@@ -5589,7 +5694,7 @@ async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
|
5589
5694
|
}
|
|
5590
5695
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
5591
5696
|
const projectDir = getProjectDir2(codeName);
|
|
5592
|
-
const mcpConfigPath =
|
|
5697
|
+
const mcpConfigPath = join6(projectDir, ".mcp.json");
|
|
5593
5698
|
let runId = null;
|
|
5594
5699
|
let kanbanItemId = null;
|
|
5595
5700
|
let taskResult;
|
|
@@ -5597,11 +5702,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5597
5702
|
const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
|
|
5598
5703
|
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
|
|
5599
5704
|
try {
|
|
5600
|
-
const claudeMdPath =
|
|
5705
|
+
const claudeMdPath = join6(projectDir, "CLAUDE.md");
|
|
5601
5706
|
const serverNames = [];
|
|
5602
5707
|
if (existsSync4(mcpConfigPath)) {
|
|
5603
5708
|
try {
|
|
5604
|
-
const d = JSON.parse(
|
|
5709
|
+
const d = JSON.parse(readFileSync5(mcpConfigPath, "utf-8"));
|
|
5605
5710
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
5606
5711
|
} catch {
|
|
5607
5712
|
}
|
|
@@ -5624,10 +5729,10 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5624
5729
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
5625
5730
|
}
|
|
5626
5731
|
const childEnv = { ...process.env };
|
|
5627
|
-
const envIntPath =
|
|
5732
|
+
const envIntPath = join6(projectDir, ".env.integrations");
|
|
5628
5733
|
if (existsSync4(envIntPath)) {
|
|
5629
5734
|
try {
|
|
5630
|
-
for (const line of
|
|
5735
|
+
for (const line of readFileSync5(envIntPath, "utf-8").split("\n")) {
|
|
5631
5736
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
5632
5737
|
const eqIdx = line.indexOf("=");
|
|
5633
5738
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -5781,8 +5886,8 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
5781
5886
|
}
|
|
5782
5887
|
}
|
|
5783
5888
|
function fireClaudeWorkTrigger(codeName, agentId, boardItems) {
|
|
5784
|
-
const
|
|
5785
|
-
const kanbanTask = findTaskByTemplate(
|
|
5889
|
+
const state5 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
5890
|
+
const kanbanTask = findTaskByTemplate(state5, "kanban-work");
|
|
5786
5891
|
if (!kanbanTask) {
|
|
5787
5892
|
log(`[claude-scheduler] Work trigger: no kanban-work task found for '${codeName}'`);
|
|
5788
5893
|
return;
|
|
@@ -5811,8 +5916,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
|
|
|
5811
5916
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
5812
5917
|
const codeName = agent.code_name;
|
|
5813
5918
|
const projectDir = getProjectDir(codeName);
|
|
5814
|
-
const mcpConfigPath =
|
|
5815
|
-
const claudeMdPath =
|
|
5919
|
+
const mcpConfigPath = join6(projectDir, ".mcp.json");
|
|
5920
|
+
const claudeMdPath = join6(projectDir, "CLAUDE.md");
|
|
5816
5921
|
if (restartBreaker.isTripped(codeName)) {
|
|
5817
5922
|
const trip = restartBreaker.getTrip(codeName);
|
|
5818
5923
|
return {
|
|
@@ -6049,9 +6154,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash2("sha256").
|
|
|
6049
6154
|
} else if (!claudeSchedulerStates.has(codeName)) {
|
|
6050
6155
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
6051
6156
|
}
|
|
6052
|
-
const
|
|
6053
|
-
if (
|
|
6054
|
-
const ready = getReadyTasks(
|
|
6157
|
+
const state5 = claudeSchedulerStates.get(codeName);
|
|
6158
|
+
if (state5) {
|
|
6159
|
+
const ready = getReadyTasks(state5, inFlightClaudeTasks);
|
|
6055
6160
|
if (ready.length > 0) {
|
|
6056
6161
|
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
6162
|
}
|
|
@@ -6430,11 +6535,11 @@ ${escapeXml(msg.content)}
|
|
|
6430
6535
|
if (fw === "claude-code") {
|
|
6431
6536
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-XHJS2MZV.js");
|
|
6432
6537
|
const projDir = ccProjectDir(agent.codeName);
|
|
6433
|
-
const mcpConfigPath =
|
|
6538
|
+
const mcpConfigPath = join6(projDir, ".mcp.json");
|
|
6434
6539
|
const serverNames = [];
|
|
6435
6540
|
if (existsSync4(mcpConfigPath)) {
|
|
6436
6541
|
try {
|
|
6437
|
-
const d = JSON.parse(
|
|
6542
|
+
const d = JSON.parse(readFileSync5(mcpConfigPath, "utf-8"));
|
|
6438
6543
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
6439
6544
|
} catch {
|
|
6440
6545
|
}
|
|
@@ -6453,15 +6558,15 @@ ${escapeXml(msg.content)}
|
|
|
6453
6558
|
"--allowedTools",
|
|
6454
6559
|
allowedTools
|
|
6455
6560
|
];
|
|
6456
|
-
const chatClaudeMd =
|
|
6561
|
+
const chatClaudeMd = join6(projDir, "CLAUDE.md");
|
|
6457
6562
|
if (existsSync4(chatClaudeMd)) {
|
|
6458
6563
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
6459
6564
|
}
|
|
6460
|
-
const envIntPath =
|
|
6565
|
+
const envIntPath = join6(projDir, ".env.integrations");
|
|
6461
6566
|
const childEnv = { ...process.env };
|
|
6462
6567
|
if (existsSync4(envIntPath)) {
|
|
6463
6568
|
try {
|
|
6464
|
-
for (const line of
|
|
6569
|
+
for (const line of readFileSync5(envIntPath, "utf-8").split("\n")) {
|
|
6465
6570
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
6466
6571
|
const eqIdx = line.indexOf("=");
|
|
6467
6572
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -6804,12 +6909,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
6804
6909
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
6805
6910
|
try {
|
|
6806
6911
|
const candidates = [
|
|
6807
|
-
|
|
6808
|
-
|
|
6912
|
+
join6(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
6913
|
+
join6(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
6809
6914
|
];
|
|
6810
6915
|
for (const candidate of candidates) {
|
|
6811
6916
|
if (existsSync4(candidate)) {
|
|
6812
|
-
const content =
|
|
6917
|
+
const content = readFileSync5(candidate, "utf-8");
|
|
6813
6918
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
6814
6919
|
builtInSkillCache.set(skillId, files);
|
|
6815
6920
|
return files;
|
|
@@ -7422,7 +7527,7 @@ async function processClaudePairSessions(agents) {
|
|
|
7422
7527
|
killPairSession,
|
|
7423
7528
|
pairTmuxSession,
|
|
7424
7529
|
finalizeClaudePairOnboarding
|
|
7425
|
-
} = await import("../claude-pair-runtime-
|
|
7530
|
+
} = await import("../claude-pair-runtime-ZQAJOQPN.js");
|
|
7426
7531
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
7427
7532
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
7428
7533
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -7717,8 +7822,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
7717
7822
|
};
|
|
7718
7823
|
}
|
|
7719
7824
|
async function syncMemories(agent, configDir, log2) {
|
|
7720
|
-
const projectDir =
|
|
7721
|
-
const memoryDir =
|
|
7825
|
+
const projectDir = join6(configDir, agent.code_name, "project");
|
|
7826
|
+
const memoryDir = join6(projectDir, "memory");
|
|
7722
7827
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
7723
7828
|
if (isFreshSync) {
|
|
7724
7829
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -7733,10 +7838,10 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7733
7838
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
7734
7839
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
7735
7840
|
const changedMemories = [];
|
|
7736
|
-
for (const file of
|
|
7841
|
+
for (const file of readdirSync3(memoryDir)) {
|
|
7737
7842
|
if (!file.endsWith(".md")) continue;
|
|
7738
7843
|
try {
|
|
7739
|
-
const raw =
|
|
7844
|
+
const raw = readFileSync5(join6(memoryDir, file), "utf-8");
|
|
7740
7845
|
const fileHash = createHash2("sha256").update(raw).digest("hex").slice(0, 16);
|
|
7741
7846
|
currentHashes.set(file, fileHash);
|
|
7742
7847
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -7761,7 +7866,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7761
7866
|
} catch (err) {
|
|
7762
7867
|
for (const mem of changedMemories) {
|
|
7763
7868
|
for (const [file] of currentHashes) {
|
|
7764
|
-
const parsed = parseMemoryFile(
|
|
7869
|
+
const parsed = parseMemoryFile(readFileSync5(join6(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
7765
7870
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
7766
7871
|
}
|
|
7767
7872
|
}
|
|
@@ -7774,7 +7879,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7774
7879
|
}
|
|
7775
7880
|
}
|
|
7776
7881
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
7777
|
-
const localFiles = existsSync4(memoryDir) ?
|
|
7882
|
+
const localFiles = existsSync4(memoryDir) ? readdirSync3(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
7778
7883
|
const localListHash = createHash2("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
7779
7884
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
7780
7885
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
@@ -7796,7 +7901,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
|
7796
7901
|
const mem = dbMemories.memories[i];
|
|
7797
7902
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
7798
7903
|
const slug = rawSlug || `memory-${i}`;
|
|
7799
|
-
const filePath =
|
|
7904
|
+
const filePath = join6(memoryDir, `${slug}.md`);
|
|
7800
7905
|
const desired = `---
|
|
7801
7906
|
name: ${JSON.stringify(mem.name)}
|
|
7802
7907
|
type: ${mem.type}
|
|
@@ -7808,7 +7913,7 @@ ${mem.content}
|
|
|
7808
7913
|
if (existsSync4(filePath)) {
|
|
7809
7914
|
let existing = "";
|
|
7810
7915
|
try {
|
|
7811
|
-
existing =
|
|
7916
|
+
existing = readFileSync5(filePath, "utf-8");
|
|
7812
7917
|
} catch {
|
|
7813
7918
|
}
|
|
7814
7919
|
if (existing === desired) continue;
|
|
@@ -7820,7 +7925,7 @@ ${mem.content}
|
|
|
7820
7925
|
}
|
|
7821
7926
|
}
|
|
7822
7927
|
if (written > 0 || overwritten > 0) {
|
|
7823
|
-
const updatedFiles =
|
|
7928
|
+
const updatedFiles = readdirSync3(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
7824
7929
|
lastLocalFileHash.set(agent.agent_id, createHash2("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
7825
7930
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
7826
7931
|
}
|
|
@@ -8004,11 +8109,11 @@ function startManager(opts) {
|
|
|
8004
8109
|
try {
|
|
8005
8110
|
const stateFile = getStateFile();
|
|
8006
8111
|
if (existsSync4(stateFile)) {
|
|
8007
|
-
const raw =
|
|
8112
|
+
const raw = readFileSync5(stateFile, "utf-8");
|
|
8008
8113
|
const parsed = JSON.parse(raw);
|
|
8009
8114
|
if (Array.isArray(parsed.agents)) {
|
|
8010
|
-
|
|
8011
|
-
log(`[startup] rehydrated ${
|
|
8115
|
+
state4.agents = parsed.agents;
|
|
8116
|
+
log(`[startup] rehydrated ${state4.agents.length} agent state(s) from ${stateFile}`);
|
|
8012
8117
|
}
|
|
8013
8118
|
if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
|
|
8014
8119
|
restartBreaker.hydrate(parsed.circuitBreakerTrips);
|
|
@@ -8020,7 +8125,7 @@ function startManager(opts) {
|
|
|
8020
8125
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
8021
8126
|
}
|
|
8022
8127
|
log(
|
|
8023
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
8128
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join6(homedir4(), ".augmented", "manager.log")}`
|
|
8024
8129
|
);
|
|
8025
8130
|
warnOnConflictingKanbanModes();
|
|
8026
8131
|
deployMcpAssets();
|
|
@@ -8083,14 +8188,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
8083
8188
|
}
|
|
8084
8189
|
}
|
|
8085
8190
|
function deployMcpAssets() {
|
|
8086
|
-
const targetDir =
|
|
8191
|
+
const targetDir = join6(homedir4(), ".augmented", "_mcp");
|
|
8087
8192
|
mkdirSync2(targetDir, { recursive: true });
|
|
8088
8193
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
8089
8194
|
let mcpSourceDir = "";
|
|
8090
8195
|
let dir = moduleDir;
|
|
8091
8196
|
for (let i = 0; i < 6; i++) {
|
|
8092
|
-
const candidate =
|
|
8093
|
-
if (existsSync4(
|
|
8197
|
+
const candidate = join6(dir, "dist", "mcp");
|
|
8198
|
+
if (existsSync4(join6(candidate, "index.js"))) {
|
|
8094
8199
|
mcpSourceDir = candidate;
|
|
8095
8200
|
break;
|
|
8096
8201
|
}
|
|
@@ -8106,7 +8211,7 @@ function deployMcpAssets() {
|
|
|
8106
8211
|
const fileHash = (p) => {
|
|
8107
8212
|
try {
|
|
8108
8213
|
if (!existsSync4(p)) return null;
|
|
8109
|
-
return createHash2("sha256").update(
|
|
8214
|
+
return createHash2("sha256").update(readFileSync5(p)).digest("hex");
|
|
8110
8215
|
} catch {
|
|
8111
8216
|
return null;
|
|
8112
8217
|
}
|
|
@@ -8117,8 +8222,8 @@ function deployMcpAssets() {
|
|
|
8117
8222
|
"telegram-channel.js"
|
|
8118
8223
|
]);
|
|
8119
8224
|
for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
|
|
8120
|
-
const src =
|
|
8121
|
-
const dst =
|
|
8225
|
+
const src = join6(mcpSourceDir, file);
|
|
8226
|
+
const dst = join6(targetDir, file);
|
|
8122
8227
|
if (!existsSync4(src)) continue;
|
|
8123
8228
|
const before = fileHash(dst);
|
|
8124
8229
|
try {
|
|
@@ -8136,16 +8241,16 @@ function deployMcpAssets() {
|
|
|
8136
8241
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
8137
8242
|
restartRunningChannelMcps(changedBasenames);
|
|
8138
8243
|
}
|
|
8139
|
-
const localMcpPath =
|
|
8244
|
+
const localMcpPath = join6(targetDir, "index.js");
|
|
8140
8245
|
try {
|
|
8141
|
-
const agentsDir =
|
|
8246
|
+
const agentsDir = join6(homedir4(), ".augmented", "agents");
|
|
8142
8247
|
if (existsSync4(agentsDir)) {
|
|
8143
|
-
for (const entry of
|
|
8248
|
+
for (const entry of readdirSync3(agentsDir, { withFileTypes: true })) {
|
|
8144
8249
|
if (!entry.isDirectory()) continue;
|
|
8145
8250
|
for (const subdir of ["provision", "project"]) {
|
|
8146
|
-
const mcpJsonPath =
|
|
8251
|
+
const mcpJsonPath = join6(agentsDir, entry.name, subdir, ".mcp.json");
|
|
8147
8252
|
try {
|
|
8148
|
-
const raw =
|
|
8253
|
+
const raw = readFileSync5(mcpJsonPath, "utf-8");
|
|
8149
8254
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
8150
8255
|
const mcpConfig = JSON.parse(raw);
|
|
8151
8256
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|