@integrity-labs/agt-cli 0.28.36 → 0.28.38
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.
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
requireHost,
|
|
24
24
|
safeWriteJsonAtomic,
|
|
25
25
|
setConfigHash
|
|
26
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-IJT4B5PH.js";
|
|
27
27
|
import {
|
|
28
28
|
getProjectDir as getProjectDir2,
|
|
29
29
|
getReadyTasks,
|
|
@@ -106,13 +106,39 @@ import {
|
|
|
106
106
|
} from "../chunk-XWVM4KPK.js";
|
|
107
107
|
|
|
108
108
|
// src/lib/manager-worker.ts
|
|
109
|
-
import { createHash as
|
|
110
|
-
import { readFileSync as
|
|
109
|
+
import { createHash as createHash8 } from "crypto";
|
|
110
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync9, rmSync as rmSync3, readdirSync as readdirSync5, statSync as statSync4, unlinkSync, copyFileSync } from "fs";
|
|
111
111
|
import { execFileSync as syncExecFile } from "child_process";
|
|
112
|
-
import { join as
|
|
113
|
-
import { homedir as
|
|
112
|
+
import { join as join16, dirname as dirname4 } from "path";
|
|
113
|
+
import { homedir as homedir9 } from "os";
|
|
114
114
|
import { fileURLToPath } from "url";
|
|
115
115
|
|
|
116
|
+
// src/lib/claude-code-upgrade-throttle.ts
|
|
117
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
118
|
+
import { dirname, join } from "path";
|
|
119
|
+
import { homedir } from "os";
|
|
120
|
+
var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
121
|
+
function claudeCodeUpgradeMarkerPath() {
|
|
122
|
+
return join(homedir(), ".augmented", ".last-claude-code-upgrade-check");
|
|
123
|
+
}
|
|
124
|
+
function stampClaudeCodeUpgradeMarker() {
|
|
125
|
+
try {
|
|
126
|
+
const markerPath = claudeCodeUpgradeMarkerPath();
|
|
127
|
+
mkdirSync(dirname(markerPath), { recursive: true });
|
|
128
|
+
writeFileSync(markerPath, String(Date.now()));
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function claudeCodeUpgradeThrottled() {
|
|
133
|
+
try {
|
|
134
|
+
const lastCheck = parseInt(readFileSync(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
135
|
+
if (!Number.isFinite(lastCheck)) return false;
|
|
136
|
+
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
116
142
|
// src/lib/mcp-config-drift.ts
|
|
117
143
|
import { createHash } from "crypto";
|
|
118
144
|
function decideMcpDriftAction(currentHash, knownHash) {
|
|
@@ -767,8 +793,8 @@ function extractSlackBehaviourSubset(config2) {
|
|
|
767
793
|
}
|
|
768
794
|
|
|
769
795
|
// src/lib/channel-quarantine.ts
|
|
770
|
-
import { readFileSync } from "fs";
|
|
771
|
-
import { join } from "path";
|
|
796
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
797
|
+
import { join as join2 } from "path";
|
|
772
798
|
var ESSENTIAL_CHANNEL_KEYS = /* @__PURE__ */ new Set(["direct-chat", "augmented"]);
|
|
773
799
|
var OPTIONAL_CHANNEL_KEYS = /* @__PURE__ */ new Set([
|
|
774
800
|
"telegram",
|
|
@@ -797,7 +823,7 @@ function classifyChannelCriticality(serverKey, entry) {
|
|
|
797
823
|
return "essential";
|
|
798
824
|
}
|
|
799
825
|
function defaultQuarantinePath(configDir) {
|
|
800
|
-
return
|
|
826
|
+
return join2(configDir, "channel-quarantine.json");
|
|
801
827
|
}
|
|
802
828
|
var ChannelQuarantineStore = class {
|
|
803
829
|
path;
|
|
@@ -808,7 +834,7 @@ var ChannelQuarantineStore = class {
|
|
|
808
834
|
load() {
|
|
809
835
|
if (this.cache) return this.cache;
|
|
810
836
|
try {
|
|
811
|
-
const raw =
|
|
837
|
+
const raw = readFileSync2(this.path, "utf-8");
|
|
812
838
|
const parsed = JSON.parse(raw);
|
|
813
839
|
this.cache = isQuarantineFile(parsed) ? parsed : {};
|
|
814
840
|
} catch {
|
|
@@ -1395,8 +1421,8 @@ function runCliProbe(binary, args, opts = {}) {
|
|
|
1395
1421
|
}
|
|
1396
1422
|
|
|
1397
1423
|
// src/lib/self-update-coalesce.ts
|
|
1398
|
-
import { readFileSync as
|
|
1399
|
-
import { dirname } from "path";
|
|
1424
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
1425
|
+
import { dirname as dirname2 } from "path";
|
|
1400
1426
|
var DEFAULT_SELF_UPDATE_COALESCE_MS = 30 * 60 * 1e3;
|
|
1401
1427
|
function resolveCoalesceWindowMs(env = process.env) {
|
|
1402
1428
|
const raw = env.AGT_SELF_UPDATE_COALESCE_MS;
|
|
@@ -1405,7 +1431,7 @@ function resolveCoalesceWindowMs(env = process.env) {
|
|
|
1405
1431
|
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_SELF_UPDATE_COALESCE_MS;
|
|
1406
1432
|
return Math.trunc(parsed);
|
|
1407
1433
|
}
|
|
1408
|
-
function readLastSelfUpdateAppliedMs(markerPath, now = Date.now(), read = (p) =>
|
|
1434
|
+
function readLastSelfUpdateAppliedMs(markerPath, now = Date.now(), read = (p) => readFileSync3(p, "utf-8")) {
|
|
1409
1435
|
let raw;
|
|
1410
1436
|
try {
|
|
1411
1437
|
raw = read(markerPath);
|
|
@@ -1418,8 +1444,8 @@ function readLastSelfUpdateAppliedMs(markerPath, now = Date.now(), read = (p) =>
|
|
|
1418
1444
|
return v;
|
|
1419
1445
|
}
|
|
1420
1446
|
function stampLastSelfUpdateApplied(markerPath, now = Date.now(), write = (p, v) => {
|
|
1421
|
-
|
|
1422
|
-
|
|
1447
|
+
mkdirSync2(dirname2(p), { recursive: true });
|
|
1448
|
+
writeFileSync2(p, v);
|
|
1423
1449
|
}) {
|
|
1424
1450
|
try {
|
|
1425
1451
|
write(markerPath, String(now));
|
|
@@ -1549,11 +1575,11 @@ function decideRestartGate(opts) {
|
|
|
1549
1575
|
}
|
|
1550
1576
|
|
|
1551
1577
|
// src/lib/claude-pid-tracker.ts
|
|
1552
|
-
import { existsSync, readFileSync as
|
|
1578
|
+
import { existsSync, readFileSync as readFileSync4 } from "fs";
|
|
1553
1579
|
function readPidFile(path) {
|
|
1554
1580
|
if (!existsSync(path)) return { version: 1, spawns: [] };
|
|
1555
1581
|
try {
|
|
1556
|
-
const raw = JSON.parse(
|
|
1582
|
+
const raw = JSON.parse(readFileSync4(path, "utf-8"));
|
|
1557
1583
|
if (raw.version !== 1 || !Array.isArray(raw.spawns)) return { version: 1, spawns: [] };
|
|
1558
1584
|
const spawns = raw.spawns.filter(
|
|
1559
1585
|
(s) => !!s && typeof s.pid === "number" && Number.isFinite(s.pid) && s.pid > 0
|
|
@@ -1606,7 +1632,7 @@ function formatReaperBootLine(opts) {
|
|
|
1606
1632
|
}
|
|
1607
1633
|
|
|
1608
1634
|
// src/lib/direct-chat-spawn-gate.ts
|
|
1609
|
-
import { join as
|
|
1635
|
+
import { join as join3 } from "path";
|
|
1610
1636
|
var DEFAULT_DIRECT_CHAT_HOST_CONCURRENCY = 2;
|
|
1611
1637
|
var DEFAULT_DIRECT_CHAT_PER_AGENT_CONCURRENCY = 1;
|
|
1612
1638
|
var DEFAULT_DIRECT_CHAT_MAX_AGE_MS = 30 * 6e4;
|
|
@@ -1619,7 +1645,7 @@ function directChatMaxAgeMs() {
|
|
|
1619
1645
|
return Number.isFinite(raw) && raw >= 0 ? raw : DEFAULT_DIRECT_CHAT_MAX_AGE_MS;
|
|
1620
1646
|
}
|
|
1621
1647
|
function directChatDoorbellPath(agentId, home) {
|
|
1622
|
-
return
|
|
1648
|
+
return join3(home, ".augmented", agentId, "direct-chat-doorbell");
|
|
1623
1649
|
}
|
|
1624
1650
|
function isDirectChatMessageExpired(createdAt, nowMs, maxAgeMs) {
|
|
1625
1651
|
if (!maxAgeMs || maxAgeMs <= 0) return false;
|
|
@@ -1722,8 +1748,8 @@ function collectEnvGates(env) {
|
|
|
1722
1748
|
}
|
|
1723
1749
|
|
|
1724
1750
|
// src/lib/artifact-stream.ts
|
|
1725
|
-
import { join as
|
|
1726
|
-
import { homedir } from "os";
|
|
1751
|
+
import { join as join4 } from "path";
|
|
1752
|
+
import { homedir as homedir2 } from "os";
|
|
1727
1753
|
import { readdir, stat, readFile } from "fs/promises";
|
|
1728
1754
|
var ARTEFACT_ENTRY_FILE = "index.html";
|
|
1729
1755
|
function errMessage(err) {
|
|
@@ -1808,7 +1834,7 @@ var ArtifactStreamScanner = class {
|
|
|
1808
1834
|
return;
|
|
1809
1835
|
}
|
|
1810
1836
|
for (const name of names) {
|
|
1811
|
-
const file =
|
|
1837
|
+
const file = join4(this.artifactsDir, name, ARTEFACT_ENTRY_FILE);
|
|
1812
1838
|
const mtime = await this.fsDeps.mtimeMs(file).catch(() => null);
|
|
1813
1839
|
if (mtime === null) continue;
|
|
1814
1840
|
if (this.seenMtime.get(name) === mtime) continue;
|
|
@@ -1839,7 +1865,7 @@ var ArtifactStreamScanner = class {
|
|
|
1839
1865
|
}
|
|
1840
1866
|
};
|
|
1841
1867
|
function artifactsDirFor(codeName) {
|
|
1842
|
-
return
|
|
1868
|
+
return join4(homedir2(), ".augmented", codeName, "artifacts");
|
|
1843
1869
|
}
|
|
1844
1870
|
var nodeArtifactFs = {
|
|
1845
1871
|
async listArtefactNames(artifactsDir) {
|
|
@@ -1914,8 +1940,8 @@ async function maybeReportUsageBanner(args) {
|
|
|
1914
1940
|
}
|
|
1915
1941
|
|
|
1916
1942
|
// src/lib/token-usage-monitor.ts
|
|
1917
|
-
import { readdirSync, readFileSync as
|
|
1918
|
-
import { join as
|
|
1943
|
+
import { readdirSync, readFileSync as readFileSync5, statSync } from "fs";
|
|
1944
|
+
import { join as join5 } from "path";
|
|
1919
1945
|
var MIN_CHECK_INTERVAL_MS2 = 6e4;
|
|
1920
1946
|
var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
1921
1947
|
var MAX_ENTRIES_PER_POST = 200;
|
|
@@ -1944,7 +1970,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1944
1970
|
if (!name.endsWith(".jsonl")) continue;
|
|
1945
1971
|
const sessionId = name.slice(0, -".jsonl".length);
|
|
1946
1972
|
if (!sessionId) continue;
|
|
1947
|
-
const path =
|
|
1973
|
+
const path = join5(dir, name);
|
|
1948
1974
|
let st;
|
|
1949
1975
|
try {
|
|
1950
1976
|
st = statSync(path);
|
|
@@ -1960,7 +1986,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1960
1986
|
}
|
|
1961
1987
|
let content;
|
|
1962
1988
|
try {
|
|
1963
|
-
content =
|
|
1989
|
+
content = readFileSync5(path, "utf-8");
|
|
1964
1990
|
} catch (err) {
|
|
1965
1991
|
log2(`[token-usage] read failed for '${codeName}/${name}': ${err.message}`);
|
|
1966
1992
|
continue;
|
|
@@ -2041,8 +2067,8 @@ async function maybeReportTokenUsage(args) {
|
|
|
2041
2067
|
}
|
|
2042
2068
|
|
|
2043
2069
|
// src/lib/workflow-run-reconciler.ts
|
|
2044
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
2045
|
-
import { join as
|
|
2070
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync2 } from "fs";
|
|
2071
|
+
import { join as join6 } from "path";
|
|
2046
2072
|
var MIN_CHECK_INTERVAL_MS3 = 5 * 6e4;
|
|
2047
2073
|
var SETTLE_MS = 3e4;
|
|
2048
2074
|
var TRANSCRIPT_MTIME_WINDOW_MS2 = 2 * 24 * 60 * 60 * 1e3;
|
|
@@ -2061,7 +2087,7 @@ function collectJsonlRecursive(dir, minMtimeMs, out, depth) {
|
|
|
2061
2087
|
return;
|
|
2062
2088
|
}
|
|
2063
2089
|
for (const name of entries) {
|
|
2064
|
-
const p =
|
|
2090
|
+
const p = join6(dir, name);
|
|
2065
2091
|
let st;
|
|
2066
2092
|
try {
|
|
2067
2093
|
st = statSync2(p);
|
|
@@ -2084,7 +2110,7 @@ function enumerateTranscriptFiles(transcriptDir, nowMs, minMtimeMs = nowMs - TRA
|
|
|
2084
2110
|
return out;
|
|
2085
2111
|
}
|
|
2086
2112
|
for (const name of entries) {
|
|
2087
|
-
const path =
|
|
2113
|
+
const path = join6(transcriptDir, name);
|
|
2088
2114
|
let st;
|
|
2089
2115
|
try {
|
|
2090
2116
|
st = statSync2(path);
|
|
@@ -2096,7 +2122,7 @@ function enumerateTranscriptFiles(transcriptDir, nowMs, minMtimeMs = nowMs - TRA
|
|
|
2096
2122
|
continue;
|
|
2097
2123
|
}
|
|
2098
2124
|
if (st.isDirectory()) {
|
|
2099
|
-
collectJsonlRecursive(
|
|
2125
|
+
collectJsonlRecursive(join6(path, "subagents"), minMtimeMs, out, 0);
|
|
2100
2126
|
}
|
|
2101
2127
|
}
|
|
2102
2128
|
return out;
|
|
@@ -2141,7 +2167,7 @@ async function maybeReconcileWorkflowRunTokens(args) {
|
|
|
2141
2167
|
const contents = [];
|
|
2142
2168
|
for (const path of files) {
|
|
2143
2169
|
try {
|
|
2144
|
-
contents.push(
|
|
2170
|
+
contents.push(readFileSync6(path, "utf-8"));
|
|
2145
2171
|
} catch {
|
|
2146
2172
|
}
|
|
2147
2173
|
}
|
|
@@ -2186,8 +2212,8 @@ async function maybeReconcileWorkflowRunTokens(args) {
|
|
|
2186
2212
|
}
|
|
2187
2213
|
|
|
2188
2214
|
// src/lib/conversation-evaluator.ts
|
|
2189
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
2190
|
-
import { join as
|
|
2215
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
2216
|
+
import { join as join7 } from "path";
|
|
2191
2217
|
var MIN_CHECK_INTERVAL_MS4 = 5 * 6e4;
|
|
2192
2218
|
var TRANSCRIPT_MTIME_WINDOW_MS3 = 3 * 24 * 60 * 60 * 1e3;
|
|
2193
2219
|
var WINDOW_PAD_MS = 5 * 6e4;
|
|
@@ -2424,7 +2450,7 @@ function readRecentTurns(dir, nowMs) {
|
|
|
2424
2450
|
const turns = [];
|
|
2425
2451
|
for (const name of entries) {
|
|
2426
2452
|
if (!name.endsWith(".jsonl")) continue;
|
|
2427
|
-
const full =
|
|
2453
|
+
const full = join7(dir, name);
|
|
2428
2454
|
let mtimeMs;
|
|
2429
2455
|
try {
|
|
2430
2456
|
mtimeMs = statSync3(full).mtimeMs;
|
|
@@ -2434,7 +2460,7 @@ function readRecentTurns(dir, nowMs) {
|
|
|
2434
2460
|
if (nowMs - mtimeMs > TRANSCRIPT_MTIME_WINDOW_MS3) continue;
|
|
2435
2461
|
let content;
|
|
2436
2462
|
try {
|
|
2437
|
-
content =
|
|
2463
|
+
content = readFileSync7(full, "utf8");
|
|
2438
2464
|
} catch {
|
|
2439
2465
|
continue;
|
|
2440
2466
|
}
|
|
@@ -2603,11 +2629,11 @@ async function reportSkip2(api2, agentId, conversationId, log2, codeName) {
|
|
|
2603
2629
|
}
|
|
2604
2630
|
|
|
2605
2631
|
// src/lib/activity-cache-monitor.ts
|
|
2606
|
-
import { existsSync as existsSync2, readFileSync as
|
|
2607
|
-
import { homedir as
|
|
2608
|
-
import { join as
|
|
2632
|
+
import { existsSync as existsSync2, readFileSync as readFileSync8 } from "fs";
|
|
2633
|
+
import { homedir as homedir3 } from "os";
|
|
2634
|
+
import { join as join8 } from "path";
|
|
2609
2635
|
var MIN_CHECK_INTERVAL_MS6 = 6e4;
|
|
2610
|
-
var STATS_CACHE_PATH =
|
|
2636
|
+
var STATS_CACHE_PATH = join8(homedir3(), ".claude", "stats-cache.json");
|
|
2611
2637
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
2612
2638
|
var state5 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
2613
2639
|
function selectNewDailyRows(raw, lastObservedDate) {
|
|
@@ -2655,7 +2681,7 @@ async function maybeReportActivityCache(args) {
|
|
|
2655
2681
|
}
|
|
2656
2682
|
let raw;
|
|
2657
2683
|
try {
|
|
2658
|
-
raw =
|
|
2684
|
+
raw = readFileSync8(STATS_CACHE_PATH, "utf-8");
|
|
2659
2685
|
} catch (err) {
|
|
2660
2686
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
2661
2687
|
return;
|
|
@@ -3104,8 +3130,8 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
3104
3130
|
|
|
3105
3131
|
// src/lib/claude-auth-detect.ts
|
|
3106
3132
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
3107
|
-
import { homedir as
|
|
3108
|
-
import { join as
|
|
3133
|
+
import { homedir as homedir4, platform } from "os";
|
|
3134
|
+
import { join as join9 } from "path";
|
|
3109
3135
|
import { execFile as execFile2 } from "child_process";
|
|
3110
3136
|
import { promisify } from "util";
|
|
3111
3137
|
var execFileAsync = promisify(execFile2);
|
|
@@ -3120,8 +3146,8 @@ async function detectClaudeAuth() {
|
|
|
3120
3146
|
}
|
|
3121
3147
|
async function findClaudeCredentialsPaths() {
|
|
3122
3148
|
const candidates = [
|
|
3123
|
-
|
|
3124
|
-
|
|
3149
|
+
join9(homedir4(), ".claude", ".credentials.json"),
|
|
3150
|
+
join9(homedir4(), ".claude", "credentials.json")
|
|
3125
3151
|
];
|
|
3126
3152
|
const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
|
|
3127
3153
|
if (isLinuxRoot) {
|
|
@@ -3129,8 +3155,8 @@ async function findClaudeCredentialsPaths() {
|
|
|
3129
3155
|
const entries = await readdir2("/home", { withFileTypes: true });
|
|
3130
3156
|
for (const entry of entries) {
|
|
3131
3157
|
if (!entry.isDirectory()) continue;
|
|
3132
|
-
candidates.push(
|
|
3133
|
-
candidates.push(
|
|
3158
|
+
candidates.push(join9("/home", entry.name, ".claude", ".credentials.json"));
|
|
3159
|
+
candidates.push(join9("/home", entry.name, ".claude", "credentials.json"));
|
|
3134
3160
|
}
|
|
3135
3161
|
} catch {
|
|
3136
3162
|
}
|
|
@@ -3222,18 +3248,18 @@ function normalize(value) {
|
|
|
3222
3248
|
}
|
|
3223
3249
|
|
|
3224
3250
|
// src/lib/channel-hash-cache.ts
|
|
3225
|
-
import { existsSync as existsSync3, readFileSync as
|
|
3226
|
-
import { join as
|
|
3251
|
+
import { existsSync as existsSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
3252
|
+
import { join as join10 } from "path";
|
|
3227
3253
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
3228
3254
|
function getChannelHashCacheFile(configDir) {
|
|
3229
|
-
return
|
|
3255
|
+
return join10(configDir, CACHE_FILENAME);
|
|
3230
3256
|
}
|
|
3231
3257
|
function loadChannelHashCache(target, configDir) {
|
|
3232
3258
|
const path = getChannelHashCacheFile(configDir);
|
|
3233
3259
|
if (!existsSync3(path)) return;
|
|
3234
3260
|
let parsed;
|
|
3235
3261
|
try {
|
|
3236
|
-
parsed = JSON.parse(
|
|
3262
|
+
parsed = JSON.parse(readFileSync9(path, "utf-8"));
|
|
3237
3263
|
} catch {
|
|
3238
3264
|
return;
|
|
3239
3265
|
}
|
|
@@ -3247,7 +3273,7 @@ function saveChannelHashCache(source, configDir) {
|
|
|
3247
3273
|
const obj = {};
|
|
3248
3274
|
for (const [key, value] of source) obj[key] = value;
|
|
3249
3275
|
try {
|
|
3250
|
-
|
|
3276
|
+
writeFileSync3(path, JSON.stringify(obj, null, 2));
|
|
3251
3277
|
} catch {
|
|
3252
3278
|
}
|
|
3253
3279
|
}
|
|
@@ -3634,9 +3660,9 @@ function planGlobalSkillSync(globalSkills, prevIds, hashOf, knownHash) {
|
|
|
3634
3660
|
|
|
3635
3661
|
// src/lib/manager/runtime.ts
|
|
3636
3662
|
import { createHash as createHash4 } from "crypto";
|
|
3637
|
-
import { readFileSync as
|
|
3638
|
-
import { join as
|
|
3639
|
-
import { homedir as
|
|
3663
|
+
import { readFileSync as readFileSync10, appendFileSync, mkdirSync as mkdirSync3, chmodSync, existsSync as existsSync4 } from "fs";
|
|
3664
|
+
import { join as join11, dirname as dirname3 } from "path";
|
|
3665
|
+
import { homedir as homedir5 } from "os";
|
|
3640
3666
|
function redactForDiskLog(value) {
|
|
3641
3667
|
try {
|
|
3642
3668
|
return value.replace(/\b(Bearer\s+)[A-Za-z0-9._-]+\b/gi, "$1[REDACTED]").replace(/\bxox[baprs]-[A-Za-z0-9-]+\b/g, "[REDACTED-SLACK]").replace(/\btlk_[A-Za-z0-9._-]+\b/g, "[REDACTED-HOST]").replace(/\bsk-ant-[A-Za-z0-9_-]+\b/g, "[REDACTED-ANTHROPIC]").replace(/\b\d{8,12}:[A-Za-z0-9_-]{30,}\b/g, "[REDACTED-TELEGRAM]").replace(
|
|
@@ -3656,8 +3682,8 @@ function log(msg) {
|
|
|
3656
3682
|
`;
|
|
3657
3683
|
if (!managerLogPath) {
|
|
3658
3684
|
try {
|
|
3659
|
-
managerLogPath =
|
|
3660
|
-
|
|
3685
|
+
managerLogPath = join11(homedir5(), ".augmented", "manager.log");
|
|
3686
|
+
mkdirSync3(dirname3(managerLogPath), { recursive: true });
|
|
3661
3687
|
if (existsSync4(managerLogPath)) {
|
|
3662
3688
|
chmodSync(managerLogPath, 384);
|
|
3663
3689
|
}
|
|
@@ -3686,7 +3712,7 @@ function sha256(content) {
|
|
|
3686
3712
|
}
|
|
3687
3713
|
function hashFile(filePath) {
|
|
3688
3714
|
try {
|
|
3689
|
-
const content =
|
|
3715
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
3690
3716
|
return sha256(content);
|
|
3691
3717
|
} catch {
|
|
3692
3718
|
return null;
|
|
@@ -3786,8 +3812,8 @@ function resolveModelChain(refreshData) {
|
|
|
3786
3812
|
|
|
3787
3813
|
// src/lib/manager/claude-auth.ts
|
|
3788
3814
|
import { existsSync as existsSync5, rmSync } from "fs";
|
|
3789
|
-
import { join as
|
|
3790
|
-
import { homedir as
|
|
3815
|
+
import { join as join12 } from "path";
|
|
3816
|
+
import { homedir as homedir6 } from "os";
|
|
3791
3817
|
async function applyClaudeAuthToEnv(childEnv, label) {
|
|
3792
3818
|
const apiKey = getApiKey();
|
|
3793
3819
|
if (!apiKey) {
|
|
@@ -3799,9 +3825,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3799
3825
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
3800
3826
|
}
|
|
3801
3827
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
3802
|
-
const claudeDir =
|
|
3828
|
+
const claudeDir = join12(homedir6(), ".claude");
|
|
3803
3829
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
3804
|
-
const p =
|
|
3830
|
+
const p = join12(claudeDir, filename);
|
|
3805
3831
|
if (existsSync5(p)) {
|
|
3806
3832
|
try {
|
|
3807
3833
|
rmSync(p, { force: true });
|
|
@@ -3816,8 +3842,8 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3816
3842
|
}
|
|
3817
3843
|
|
|
3818
3844
|
// src/lib/manager/kanban/parsers.ts
|
|
3819
|
-
import { existsSync as existsSync6, readFileSync as
|
|
3820
|
-
import { join as
|
|
3845
|
+
import { existsSync as existsSync6, readFileSync as readFileSync11 } from "fs";
|
|
3846
|
+
import { join as join13 } from "path";
|
|
3821
3847
|
var STANDUP_TEMPLATES = /* @__PURE__ */ new Set(["daily-standup", "end-of-day-summary"]);
|
|
3822
3848
|
var TASK_UPDATE_TEMPLATES = /* @__PURE__ */ new Set(["hourly-status", "task-update"]);
|
|
3823
3849
|
var PLAN_TEMPLATES = /* @__PURE__ */ new Set(["morning-plan"]);
|
|
@@ -3937,12 +3963,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
3937
3963
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
3938
3964
|
try {
|
|
3939
3965
|
const candidates = [
|
|
3940
|
-
|
|
3941
|
-
|
|
3966
|
+
join13(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
3967
|
+
join13(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
3942
3968
|
];
|
|
3943
3969
|
for (const candidate of candidates) {
|
|
3944
3970
|
if (existsSync6(candidate)) {
|
|
3945
|
-
const content =
|
|
3971
|
+
const content = readFileSync11(candidate, "utf-8");
|
|
3946
3972
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
3947
3973
|
builtInSkillCache.set(skillId, files);
|
|
3948
3974
|
return files;
|
|
@@ -4407,11 +4433,140 @@ async function sendTaskNotification(agentCodeName, channel, to, text) {
|
|
|
4407
4433
|
return { ok: false, error_code: `UNKNOWN_CHANNEL:${channel}` };
|
|
4408
4434
|
}
|
|
4409
4435
|
|
|
4410
|
-
// src/lib/manager/scheduler.ts
|
|
4436
|
+
// src/lib/manager/scheduler/tasks.ts
|
|
4437
|
+
function buildSchedulerTaskInput(t) {
|
|
4438
|
+
return {
|
|
4439
|
+
id: t.id,
|
|
4440
|
+
template_id: t.template_id,
|
|
4441
|
+
name: t.name,
|
|
4442
|
+
schedule_kind: t.schedule_kind,
|
|
4443
|
+
schedule_expr: t.schedule_expr ?? null,
|
|
4444
|
+
schedule_every: t.schedule_every ?? null,
|
|
4445
|
+
schedule_at: t.schedule_at ?? null,
|
|
4446
|
+
timezone: t.timezone ?? "UTC",
|
|
4447
|
+
prompt: t.prompt ?? "",
|
|
4448
|
+
session_target: t.session_target ?? "isolated",
|
|
4449
|
+
delivery_mode: t.delivery_mode ?? "none",
|
|
4450
|
+
delivery_policy: t.delivery_policy ?? "always",
|
|
4451
|
+
delivery_channel: t.delivery_channel ?? null,
|
|
4452
|
+
delivery_to: t.delivery_to ?? null,
|
|
4453
|
+
enabled: t.enabled ?? true,
|
|
4454
|
+
triggered_at: t.triggered_at ?? null
|
|
4455
|
+
};
|
|
4456
|
+
}
|
|
4457
|
+
function deriveScheduledTaskNotify(task) {
|
|
4458
|
+
if (task.deliveryChannel !== "msteams") return {};
|
|
4459
|
+
const to = task.deliveryTo;
|
|
4460
|
+
let conversationId = null;
|
|
4461
|
+
if (typeof to === "string" && to.trim().length > 0) {
|
|
4462
|
+
conversationId = to.trim();
|
|
4463
|
+
} else if (to && typeof to === "object") {
|
|
4464
|
+
const cid = to.conversation_id;
|
|
4465
|
+
if (typeof cid === "string" && cid.trim().length > 0) conversationId = cid.trim();
|
|
4466
|
+
}
|
|
4467
|
+
if (!conversationId) return {};
|
|
4468
|
+
return { notify_channel: "msteams", notify_to: conversationId };
|
|
4469
|
+
}
|
|
4470
|
+
function isScheduledViaKanbanEnabled() {
|
|
4471
|
+
const v = process.env["AGT_SCHEDULED_VIA_KANBAN"];
|
|
4472
|
+
return !(v === "0" || v?.toLowerCase() === "false");
|
|
4473
|
+
}
|
|
4474
|
+
|
|
4475
|
+
// src/lib/manager/scheduler/runs.ts
|
|
4411
4476
|
import { createHash as createHash5 } from "crypto";
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4477
|
+
async function startRun(opts) {
|
|
4478
|
+
try {
|
|
4479
|
+
const res = await api.post(
|
|
4480
|
+
"/host/runs/start",
|
|
4481
|
+
opts
|
|
4482
|
+
);
|
|
4483
|
+
return { run_id: res.run_id ?? null, kanban_item_id: res.kanban_item_id ?? null };
|
|
4484
|
+
} catch (err) {
|
|
4485
|
+
const errText = err instanceof Error ? err.message : String(err);
|
|
4486
|
+
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4487
|
+
log(`[runs] start failed for agent_id=${opts.agent_id} source_type=${opts.source_type} error_id=${errId}`);
|
|
4488
|
+
return { run_id: null, kanban_item_id: null };
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
async function finishRun(runId, outcome, options = {}) {
|
|
4492
|
+
try {
|
|
4493
|
+
await api.post("/host/runs/finish", {
|
|
4494
|
+
run_id: runId,
|
|
4495
|
+
outcome,
|
|
4496
|
+
outcome_message: options.outcomeMessage,
|
|
4497
|
+
metadata: options.metadata,
|
|
4498
|
+
complete_kanban_item_id: options.completeKanbanItemId ?? void 0,
|
|
4499
|
+
result: options.result
|
|
4500
|
+
});
|
|
4501
|
+
} catch (err) {
|
|
4502
|
+
const errText = err instanceof Error ? err.message : String(err);
|
|
4503
|
+
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4504
|
+
log(`[runs] finish failed for run_id=${runId} outcome=${outcome} error_id=${errId}`);
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
var MAX_PRIOR_RUNS = 5;
|
|
4508
|
+
async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
4509
|
+
try {
|
|
4510
|
+
const data = await api.post("/host/scheduled-tasks/recent-outputs", {
|
|
4511
|
+
agent_id: agentId,
|
|
4512
|
+
task_id: taskId,
|
|
4513
|
+
since_hours: 24,
|
|
4514
|
+
limit: MAX_PRIOR_RUNS
|
|
4515
|
+
});
|
|
4516
|
+
const rows = Array.isArray(data?.runs) ? data.runs.slice(0, MAX_PRIOR_RUNS) : [];
|
|
4517
|
+
return rows.filter((r) => typeof r.output_text === "string" && r.output_text.length > 0).map((r) => ({ startedAt: r.started_at, output: r.output_text }));
|
|
4518
|
+
} catch (err) {
|
|
4519
|
+
const errText = err instanceof Error ? err.message : String(err);
|
|
4520
|
+
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4521
|
+
log(`[runs] prior-runs lookup failed for task_id=${taskId} error_id=${errId}`);
|
|
4522
|
+
return [];
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
// src/lib/manager/scheduler/state.ts
|
|
4527
|
+
var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
4528
|
+
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
4529
|
+
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
4530
|
+
var claudeSchedulerStates = /* @__PURE__ */ new Map();
|
|
4531
|
+
var scheduledRunsByCode = /* @__PURE__ */ new Map();
|
|
4532
|
+
var claimedScheduledCards = /* @__PURE__ */ new Set();
|
|
4533
|
+
var completedScheduledCards = /* @__PURE__ */ new Set();
|
|
4534
|
+
function claimScheduledCardDelivery(cardId) {
|
|
4535
|
+
if (claimedScheduledCards.has(cardId)) return false;
|
|
4536
|
+
claimedScheduledCards.add(cardId);
|
|
4537
|
+
return true;
|
|
4538
|
+
}
|
|
4539
|
+
function releaseScheduledCardDelivery(cardId) {
|
|
4540
|
+
claimedScheduledCards.delete(cardId);
|
|
4541
|
+
}
|
|
4542
|
+
function markScheduledCardDeliveryComplete(cardId) {
|
|
4543
|
+
completedScheduledCards.add(cardId);
|
|
4544
|
+
}
|
|
4545
|
+
function trackScheduledRun(codeName, cardId, runId) {
|
|
4546
|
+
let m = scheduledRunsByCode.get(codeName);
|
|
4547
|
+
if (!m) {
|
|
4548
|
+
m = /* @__PURE__ */ new Map();
|
|
4549
|
+
scheduledRunsByCode.set(codeName, m);
|
|
4550
|
+
}
|
|
4551
|
+
m.set(cardId, runId);
|
|
4552
|
+
}
|
|
4553
|
+
function isScheduledCardTracked(cardId) {
|
|
4554
|
+
for (const m of scheduledRunsByCode.values()) {
|
|
4555
|
+
if (m.has(cardId)) return true;
|
|
4556
|
+
}
|
|
4557
|
+
return false;
|
|
4558
|
+
}
|
|
4559
|
+
function closeScheduledRunsForCode(codeName, outcome, reason) {
|
|
4560
|
+
const m = scheduledRunsByCode.get(codeName);
|
|
4561
|
+
if (!m || m.size === 0) return;
|
|
4562
|
+
scheduledRunsByCode.delete(codeName);
|
|
4563
|
+
for (const runId of m.values()) {
|
|
4564
|
+
void finishRun(runId, outcome, { outcomeMessage: reason });
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
|
|
4568
|
+
// src/lib/manager/scheduler/kanban-route.ts
|
|
4569
|
+
import { createHash as createHash6 } from "crypto";
|
|
4415
4570
|
|
|
4416
4571
|
// src/lib/delivery-schedule-link.ts
|
|
4417
4572
|
function envSuffixFor2(codeName) {
|
|
@@ -4637,231 +4792,38 @@ async function deliverScheduledTaskOutput(agentCodeName, agentId, rawTarget, bod
|
|
|
4637
4792
|
}
|
|
4638
4793
|
}
|
|
4639
4794
|
|
|
4640
|
-
// src/lib/manager/scheduler.ts
|
|
4641
|
-
function
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
template_id: t.template_id,
|
|
4645
|
-
name: t.name,
|
|
4646
|
-
schedule_kind: t.schedule_kind,
|
|
4647
|
-
schedule_expr: t.schedule_expr ?? null,
|
|
4648
|
-
schedule_every: t.schedule_every ?? null,
|
|
4649
|
-
schedule_at: t.schedule_at ?? null,
|
|
4650
|
-
timezone: t.timezone ?? "UTC",
|
|
4651
|
-
prompt: t.prompt ?? "",
|
|
4652
|
-
session_target: t.session_target ?? "isolated",
|
|
4653
|
-
delivery_mode: t.delivery_mode ?? "none",
|
|
4654
|
-
delivery_policy: t.delivery_policy ?? "always",
|
|
4655
|
-
delivery_channel: t.delivery_channel ?? null,
|
|
4656
|
-
delivery_to: t.delivery_to ?? null,
|
|
4657
|
-
enabled: t.enabled ?? true,
|
|
4658
|
-
triggered_at: t.triggered_at ?? null
|
|
4659
|
-
};
|
|
4660
|
-
}
|
|
4661
|
-
var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
4662
|
-
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
4663
|
-
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
4664
|
-
function claudePidFilePath() {
|
|
4665
|
-
return join13(homedir6(), ".augmented", "manager-claude-pids.json");
|
|
4666
|
-
}
|
|
4667
|
-
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
4668
|
-
function registerClaudeSpawn(record) {
|
|
4669
|
-
inFlightClaudePids.set(record.pid, record);
|
|
4670
|
-
try {
|
|
4671
|
-
addSpawn(claudePidFilePath(), record);
|
|
4672
|
-
} catch (err) {
|
|
4673
|
-
log(`[drain] pid-file write failed: ${err.message}`);
|
|
4795
|
+
// src/lib/manager/scheduler/kanban-route.ts
|
|
4796
|
+
async function deliverScheduledCardResult(codeName, agentId, cardId) {
|
|
4797
|
+
if (!claimScheduledCardDelivery(cardId)) {
|
|
4798
|
+
return completedScheduledCards.has(cardId) ? "terminal" : "in_flight";
|
|
4674
4799
|
}
|
|
4675
|
-
|
|
4676
|
-
function unregisterClaudeSpawn(pid) {
|
|
4677
|
-
inFlightClaudePids.delete(pid);
|
|
4800
|
+
let card;
|
|
4678
4801
|
try {
|
|
4679
|
-
|
|
4680
|
-
|
|
4802
|
+
const resp = await api.post("/host/kanban-item", {
|
|
4803
|
+
agent_id: agentId,
|
|
4804
|
+
item_id: cardId
|
|
4805
|
+
});
|
|
4806
|
+
card = resp.item;
|
|
4807
|
+
} catch (err) {
|
|
4808
|
+
if (err instanceof ApiError && err.status >= 400 && err.status < 500) {
|
|
4809
|
+
log(`[scheduled-kanban] delivery: card ${cardId} on '${codeName}' fetch returned ${err.status} (${err.message}) \u2014 treating as terminal, not retrying`);
|
|
4810
|
+
markScheduledCardDeliveryComplete(cardId);
|
|
4811
|
+
return "terminal";
|
|
4812
|
+
}
|
|
4813
|
+
releaseScheduledCardDelivery(cardId);
|
|
4814
|
+
log(`[scheduled-kanban] delivery fetch failed for card ${cardId} on '${codeName}': ${err.message} \u2014 will retry`);
|
|
4815
|
+
return "retry";
|
|
4681
4816
|
}
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
const codeName = agent.code_name;
|
|
4686
|
-
const stableTasksHash = createHash5("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
4687
|
-
const boardHash = boardItems.length > 0 ? createHash5("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
|
|
4688
|
-
const resolvedModels = resolveModelChain(refreshData);
|
|
4689
|
-
const modelsHash = createHash5("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
4690
|
-
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
4691
|
-
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
4692
|
-
if (combinedHash !== prevHash) {
|
|
4693
|
-
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
4694
|
-
const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
4695
|
-
claudeSchedulerStates.set(codeName, state8);
|
|
4696
|
-
agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
4697
|
-
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
4817
|
+
if (!card || card.status !== "done" || card.source_type !== "scheduled_task" || !card.source_ref) {
|
|
4818
|
+
markScheduledCardDeliveryComplete(cardId);
|
|
4819
|
+
return "terminal";
|
|
4698
4820
|
}
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
for (const task of ready) {
|
|
4706
|
-
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
4707
|
-
if (KANBAN_WORK_TEMPLATES.has(task.templateId)) {
|
|
4708
|
-
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
4709
|
-
claudeSchedulerStates.set(codeName, updated);
|
|
4710
|
-
continue;
|
|
4711
|
-
}
|
|
4712
|
-
let prompt = task.prompt;
|
|
4713
|
-
if (BOARD_INJECT_TEMPLATES.has(task.templateId) && boardItems.length > 0) {
|
|
4714
|
-
const template = PLAN_TEMPLATES.has(task.templateId) ? "morning-plan" : "follow-up";
|
|
4715
|
-
const boardPrefix = formatBoardForPrompt(boardItems, template);
|
|
4716
|
-
prompt = boardPrefix + prompt;
|
|
4717
|
-
}
|
|
4718
|
-
inFlightClaudeTasks.add(task.taskId);
|
|
4719
|
-
claudeTaskConcurrency.set(codeName, (claudeTaskConcurrency.get(codeName) ?? 0) + 1);
|
|
4720
|
-
if (isScheduledViaKanbanEnabled() && isPlainScheduledTemplate(task.templateId)) {
|
|
4721
|
-
await fireScheduledTaskViaKanban(codeName, agent.agent_id, task, prompt);
|
|
4722
|
-
inFlightClaudeTasks.delete(task.taskId);
|
|
4723
|
-
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
4724
|
-
continue;
|
|
4725
|
-
}
|
|
4726
|
-
log(`[claude-scheduler] Firing '${task.name}' for '${codeName}'`);
|
|
4727
|
-
executeAndProcessClaudeTask(codeName, agent.agent_id, task, prompt).finally(() => {
|
|
4728
|
-
inFlightClaudeTasks.delete(task.taskId);
|
|
4729
|
-
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
4730
|
-
});
|
|
4731
|
-
}
|
|
4732
|
-
}
|
|
4733
|
-
function deriveScheduledTaskNotify(task) {
|
|
4734
|
-
if (task.deliveryChannel !== "msteams") return {};
|
|
4735
|
-
const to = task.deliveryTo;
|
|
4736
|
-
let conversationId = null;
|
|
4737
|
-
if (typeof to === "string" && to.trim().length > 0) {
|
|
4738
|
-
conversationId = to.trim();
|
|
4739
|
-
} else if (to && typeof to === "object") {
|
|
4740
|
-
const cid = to.conversation_id;
|
|
4741
|
-
if (typeof cid === "string" && cid.trim().length > 0) conversationId = cid.trim();
|
|
4742
|
-
}
|
|
4743
|
-
if (!conversationId) return {};
|
|
4744
|
-
return { notify_channel: "msteams", notify_to: conversationId };
|
|
4745
|
-
}
|
|
4746
|
-
async function startRun(opts) {
|
|
4747
|
-
try {
|
|
4748
|
-
const res = await api.post(
|
|
4749
|
-
"/host/runs/start",
|
|
4750
|
-
opts
|
|
4751
|
-
);
|
|
4752
|
-
return { run_id: res.run_id ?? null, kanban_item_id: res.kanban_item_id ?? null };
|
|
4753
|
-
} catch (err) {
|
|
4754
|
-
const errText = err instanceof Error ? err.message : String(err);
|
|
4755
|
-
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4756
|
-
log(`[runs] start failed for agent_id=${opts.agent_id} source_type=${opts.source_type} error_id=${errId}`);
|
|
4757
|
-
return { run_id: null, kanban_item_id: null };
|
|
4758
|
-
}
|
|
4759
|
-
}
|
|
4760
|
-
async function finishRun(runId, outcome, options = {}) {
|
|
4761
|
-
try {
|
|
4762
|
-
await api.post("/host/runs/finish", {
|
|
4763
|
-
run_id: runId,
|
|
4764
|
-
outcome,
|
|
4765
|
-
outcome_message: options.outcomeMessage,
|
|
4766
|
-
metadata: options.metadata,
|
|
4767
|
-
complete_kanban_item_id: options.completeKanbanItemId ?? void 0,
|
|
4768
|
-
result: options.result
|
|
4769
|
-
});
|
|
4770
|
-
} catch (err) {
|
|
4771
|
-
const errText = err instanceof Error ? err.message : String(err);
|
|
4772
|
-
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4773
|
-
log(`[runs] finish failed for run_id=${runId} outcome=${outcome} error_id=${errId}`);
|
|
4774
|
-
}
|
|
4775
|
-
}
|
|
4776
|
-
var MAX_PRIOR_RUNS = 5;
|
|
4777
|
-
async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
4778
|
-
try {
|
|
4779
|
-
const data = await api.post("/host/scheduled-tasks/recent-outputs", {
|
|
4780
|
-
agent_id: agentId,
|
|
4781
|
-
task_id: taskId,
|
|
4782
|
-
since_hours: 24,
|
|
4783
|
-
limit: MAX_PRIOR_RUNS
|
|
4784
|
-
});
|
|
4785
|
-
const rows = Array.isArray(data?.runs) ? data.runs.slice(0, MAX_PRIOR_RUNS) : [];
|
|
4786
|
-
return rows.filter((r) => typeof r.output_text === "string" && r.output_text.length > 0).map((r) => ({ startedAt: r.started_at, output: r.output_text }));
|
|
4787
|
-
} catch (err) {
|
|
4788
|
-
const errText = err instanceof Error ? err.message : String(err);
|
|
4789
|
-
const errId = createHash5("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4790
|
-
log(`[runs] prior-runs lookup failed for task_id=${taskId} error_id=${errId}`);
|
|
4791
|
-
return [];
|
|
4792
|
-
}
|
|
4793
|
-
}
|
|
4794
|
-
function isScheduledViaKanbanEnabled() {
|
|
4795
|
-
const v = process.env["AGT_SCHEDULED_VIA_KANBAN"];
|
|
4796
|
-
return !(v === "0" || v?.toLowerCase() === "false");
|
|
4797
|
-
}
|
|
4798
|
-
var scheduledRunsByCode = /* @__PURE__ */ new Map();
|
|
4799
|
-
var claimedScheduledCards = /* @__PURE__ */ new Set();
|
|
4800
|
-
var completedScheduledCards = /* @__PURE__ */ new Set();
|
|
4801
|
-
function claimScheduledCardDelivery(cardId) {
|
|
4802
|
-
if (claimedScheduledCards.has(cardId)) return false;
|
|
4803
|
-
claimedScheduledCards.add(cardId);
|
|
4804
|
-
return true;
|
|
4805
|
-
}
|
|
4806
|
-
function releaseScheduledCardDelivery(cardId) {
|
|
4807
|
-
claimedScheduledCards.delete(cardId);
|
|
4808
|
-
}
|
|
4809
|
-
function markScheduledCardDeliveryComplete(cardId) {
|
|
4810
|
-
completedScheduledCards.add(cardId);
|
|
4811
|
-
}
|
|
4812
|
-
function trackScheduledRun(codeName, cardId, runId) {
|
|
4813
|
-
let m = scheduledRunsByCode.get(codeName);
|
|
4814
|
-
if (!m) {
|
|
4815
|
-
m = /* @__PURE__ */ new Map();
|
|
4816
|
-
scheduledRunsByCode.set(codeName, m);
|
|
4817
|
-
}
|
|
4818
|
-
m.set(cardId, runId);
|
|
4819
|
-
}
|
|
4820
|
-
function isScheduledCardTracked(cardId) {
|
|
4821
|
-
for (const m of scheduledRunsByCode.values()) {
|
|
4822
|
-
if (m.has(cardId)) return true;
|
|
4823
|
-
}
|
|
4824
|
-
return false;
|
|
4825
|
-
}
|
|
4826
|
-
function closeScheduledRunsForCode(codeName, outcome, reason) {
|
|
4827
|
-
const m = scheduledRunsByCode.get(codeName);
|
|
4828
|
-
if (!m || m.size === 0) return;
|
|
4829
|
-
scheduledRunsByCode.delete(codeName);
|
|
4830
|
-
for (const runId of m.values()) {
|
|
4831
|
-
void finishRun(runId, outcome, { outcomeMessage: reason });
|
|
4832
|
-
}
|
|
4833
|
-
}
|
|
4834
|
-
async function deliverScheduledCardResult(codeName, agentId, cardId) {
|
|
4835
|
-
if (!claimScheduledCardDelivery(cardId)) {
|
|
4836
|
-
return completedScheduledCards.has(cardId) ? "terminal" : "in_flight";
|
|
4837
|
-
}
|
|
4838
|
-
let card;
|
|
4839
|
-
try {
|
|
4840
|
-
const resp = await api.post("/host/kanban-item", {
|
|
4841
|
-
agent_id: agentId,
|
|
4842
|
-
item_id: cardId
|
|
4843
|
-
});
|
|
4844
|
-
card = resp.item;
|
|
4845
|
-
} catch (err) {
|
|
4846
|
-
if (err instanceof ApiError && err.status >= 400 && err.status < 500) {
|
|
4847
|
-
log(`[scheduled-kanban] delivery: card ${cardId} on '${codeName}' fetch returned ${err.status} (${err.message}) \u2014 treating as terminal, not retrying`);
|
|
4848
|
-
markScheduledCardDeliveryComplete(cardId);
|
|
4849
|
-
return "terminal";
|
|
4850
|
-
}
|
|
4851
|
-
releaseScheduledCardDelivery(cardId);
|
|
4852
|
-
log(`[scheduled-kanban] delivery fetch failed for card ${cardId} on '${codeName}': ${err.message} \u2014 will retry`);
|
|
4853
|
-
return "retry";
|
|
4854
|
-
}
|
|
4855
|
-
if (!card || card.status !== "done" || card.source_type !== "scheduled_task" || !card.source_ref) {
|
|
4856
|
-
markScheduledCardDeliveryComplete(cardId);
|
|
4857
|
-
return "terminal";
|
|
4858
|
-
}
|
|
4859
|
-
const state7 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
4860
|
-
const task = state7.tasks[card.source_ref];
|
|
4861
|
-
if (!task) {
|
|
4862
|
-
log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
|
|
4863
|
-
markScheduledCardDeliveryComplete(cardId);
|
|
4864
|
-
return "terminal";
|
|
4821
|
+
const state7 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
4822
|
+
const task = state7.tasks[card.source_ref];
|
|
4823
|
+
if (!task) {
|
|
4824
|
+
log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
|
|
4825
|
+
markScheduledCardDeliveryComplete(cardId);
|
|
4826
|
+
return "terminal";
|
|
4865
4827
|
}
|
|
4866
4828
|
if (!isPlainScheduledTemplate(task.templateId)) {
|
|
4867
4829
|
log(`[scheduled-kanban] delivery: card ${cardId} template '${task.templateId}' on '${codeName}' is not a plain scheduled template \u2014 skipping manager delivery`);
|
|
@@ -4996,9 +4958,152 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
|
|
|
4996
4958
|
}
|
|
4997
4959
|
return routed;
|
|
4998
4960
|
}
|
|
4961
|
+
async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput, delivery) {
|
|
4962
|
+
try {
|
|
4963
|
+
const classification = classifyOutput(rawOutput);
|
|
4964
|
+
if (classification.action === "suppress") {
|
|
4965
|
+
const trimmed = (rawOutput ?? "").trim();
|
|
4966
|
+
const outputHash = trimmed.length === 0 ? "empty" : createHash6("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
4967
|
+
log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
4968
|
+
if (classification.suppressedNotes) {
|
|
4969
|
+
const notesHash = createHash6("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
|
|
4970
|
+
log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
|
|
4971
|
+
}
|
|
4972
|
+
if (delivery?.mode === "announce" && delivery.to) {
|
|
4973
|
+
await reportDeliveryStatus(agentId, delivery.taskId, {
|
|
4974
|
+
status: "skipped",
|
|
4975
|
+
error_code: "NO_CONTENT"
|
|
4976
|
+
});
|
|
4977
|
+
}
|
|
4978
|
+
return;
|
|
4979
|
+
}
|
|
4980
|
+
const output = classification.deliverable;
|
|
4981
|
+
if (classification.action === "strip") {
|
|
4982
|
+
log(`[claude-scheduler] Stripped '<no-delivery/>' sentinel from '${codeName}' output (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 agent mixed it with real content; delivering the rest.`);
|
|
4983
|
+
}
|
|
4984
|
+
if (STANDUP_TEMPLATES.has(templateId)) {
|
|
4985
|
+
const standup = parseStandupSummary(output);
|
|
4986
|
+
await api.post("/host/agent-status", {
|
|
4987
|
+
agent_code_name: codeName,
|
|
4988
|
+
standup,
|
|
4989
|
+
current_status: "idle"
|
|
4990
|
+
});
|
|
4991
|
+
log(`[claude-scheduler] Standup posted for '${codeName}'`);
|
|
4992
|
+
} else if (TASK_UPDATE_TEMPLATES.has(templateId)) {
|
|
4993
|
+
await api.post("/host/agent-status", {
|
|
4994
|
+
agent_code_name: codeName,
|
|
4995
|
+
current_tasks: output.slice(0, 2e3)
|
|
4996
|
+
});
|
|
4997
|
+
log(`[claude-scheduler] Task update posted for '${codeName}'`);
|
|
4998
|
+
} else if (PLAN_TEMPLATES.has(templateId)) {
|
|
4999
|
+
const planItems = parsePlanItems(output);
|
|
5000
|
+
if (planItems.length > 0) {
|
|
5001
|
+
await api.post("/host/kanban", {
|
|
5002
|
+
agent_id: agentId,
|
|
5003
|
+
add: planItems
|
|
5004
|
+
});
|
|
5005
|
+
log(`[claude-scheduler] Plan items posted for '${codeName}' (${planItems.length} items)`);
|
|
5006
|
+
}
|
|
5007
|
+
} else if (KANBAN_WORK_TEMPLATES.has(templateId)) {
|
|
5008
|
+
const kanbanUpdates = parseKanbanUpdates(output);
|
|
5009
|
+
if (kanbanUpdates.length > 0) {
|
|
5010
|
+
await api.post("/host/kanban", {
|
|
5011
|
+
agent_id: agentId,
|
|
5012
|
+
update: kanbanUpdates
|
|
5013
|
+
});
|
|
5014
|
+
log(`[claude-scheduler] Kanban updates posted for '${codeName}' (${kanbanUpdates.length} updates)`);
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
if (delivery?.mode === "announce" && delivery.to) {
|
|
5018
|
+
await deliverScheduledTaskOutput(
|
|
5019
|
+
codeName,
|
|
5020
|
+
agentId,
|
|
5021
|
+
delivery.to,
|
|
5022
|
+
output.slice(0, 4e3),
|
|
5023
|
+
delivery.taskId
|
|
5024
|
+
);
|
|
5025
|
+
}
|
|
5026
|
+
} catch (err) {
|
|
5027
|
+
log(`[claude-scheduler] Failed to post result for '${codeName}': ${err.message}`);
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
|
|
5031
|
+
// src/lib/manager/scheduler/execution.ts
|
|
5032
|
+
import { createHash as createHash7 } from "crypto";
|
|
5033
|
+
import { readFileSync as readFileSync12, existsSync as existsSync7 } from "fs";
|
|
5034
|
+
import { homedir as homedir7 } from "os";
|
|
5035
|
+
import { join as join14 } from "path";
|
|
5036
|
+
function claudePidFilePath() {
|
|
5037
|
+
return join14(homedir7(), ".augmented", "manager-claude-pids.json");
|
|
5038
|
+
}
|
|
5039
|
+
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
5040
|
+
function registerClaudeSpawn(record) {
|
|
5041
|
+
inFlightClaudePids.set(record.pid, record);
|
|
5042
|
+
try {
|
|
5043
|
+
addSpawn(claudePidFilePath(), record);
|
|
5044
|
+
} catch (err) {
|
|
5045
|
+
log(`[drain] pid-file write failed: ${err.message}`);
|
|
5046
|
+
}
|
|
5047
|
+
}
|
|
5048
|
+
function unregisterClaudeSpawn(pid) {
|
|
5049
|
+
inFlightClaudePids.delete(pid);
|
|
5050
|
+
try {
|
|
5051
|
+
removeSpawn(claudePidFilePath(), pid);
|
|
5052
|
+
} catch {
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
5055
|
+
async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData) {
|
|
5056
|
+
const codeName = agent.code_name;
|
|
5057
|
+
const stableTasksHash = createHash7("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
5058
|
+
const boardHash = boardItems.length > 0 ? createHash7("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
|
|
5059
|
+
const resolvedModels = resolveModelChain(refreshData);
|
|
5060
|
+
const modelsHash = createHash7("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
5061
|
+
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
5062
|
+
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
5063
|
+
if (combinedHash !== prevHash) {
|
|
5064
|
+
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
5065
|
+
const state8 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
5066
|
+
claudeSchedulerStates.set(codeName, state8);
|
|
5067
|
+
agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
5068
|
+
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
5069
|
+
}
|
|
5070
|
+
if (!claudeSchedulerStates.has(codeName)) {
|
|
5071
|
+
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
5072
|
+
}
|
|
5073
|
+
const state7 = claudeSchedulerStates.get(codeName);
|
|
5074
|
+
const ready = getReadyTasks(state7, inFlightClaudeTasks);
|
|
5075
|
+
if (ready.length === 0) return;
|
|
5076
|
+
for (const task of ready) {
|
|
5077
|
+
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
5078
|
+
if (KANBAN_WORK_TEMPLATES.has(task.templateId)) {
|
|
5079
|
+
const updated = markTaskFired(codeName, task.taskId, "ok");
|
|
5080
|
+
claudeSchedulerStates.set(codeName, updated);
|
|
5081
|
+
continue;
|
|
5082
|
+
}
|
|
5083
|
+
let prompt = task.prompt;
|
|
5084
|
+
if (BOARD_INJECT_TEMPLATES.has(task.templateId) && boardItems.length > 0) {
|
|
5085
|
+
const template = PLAN_TEMPLATES.has(task.templateId) ? "morning-plan" : "follow-up";
|
|
5086
|
+
const boardPrefix = formatBoardForPrompt(boardItems, template);
|
|
5087
|
+
prompt = boardPrefix + prompt;
|
|
5088
|
+
}
|
|
5089
|
+
inFlightClaudeTasks.add(task.taskId);
|
|
5090
|
+
claudeTaskConcurrency.set(codeName, (claudeTaskConcurrency.get(codeName) ?? 0) + 1);
|
|
5091
|
+
if (isScheduledViaKanbanEnabled() && isPlainScheduledTemplate(task.templateId)) {
|
|
5092
|
+
await fireScheduledTaskViaKanban(codeName, agent.agent_id, task, prompt);
|
|
5093
|
+
inFlightClaudeTasks.delete(task.taskId);
|
|
5094
|
+
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
5095
|
+
continue;
|
|
5096
|
+
}
|
|
5097
|
+
log(`[claude-scheduler] Firing '${task.name}' for '${codeName}'`);
|
|
5098
|
+
executeAndProcessClaudeTask(codeName, agent.agent_id, task, prompt).finally(() => {
|
|
5099
|
+
inFlightClaudeTasks.delete(task.taskId);
|
|
5100
|
+
claudeTaskConcurrency.set(codeName, Math.max(0, (claudeTaskConcurrency.get(codeName) ?? 1) - 1));
|
|
5101
|
+
});
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
4999
5104
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
5000
5105
|
const projectDir = getProjectDir2(codeName);
|
|
5001
|
-
const mcpConfigPath =
|
|
5106
|
+
const mcpConfigPath = join14(projectDir, ".mcp.json");
|
|
5002
5107
|
let runId = null;
|
|
5003
5108
|
let kanbanItemId = null;
|
|
5004
5109
|
let taskResult;
|
|
@@ -5006,11 +5111,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5006
5111
|
const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
|
|
5007
5112
|
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
|
|
5008
5113
|
try {
|
|
5009
|
-
const claudeMdPath =
|
|
5114
|
+
const claudeMdPath = join14(projectDir, "CLAUDE.md");
|
|
5010
5115
|
const serverNames = [];
|
|
5011
5116
|
if (existsSync7(mcpConfigPath)) {
|
|
5012
5117
|
try {
|
|
5013
|
-
const d = JSON.parse(
|
|
5118
|
+
const d = JSON.parse(readFileSync12(mcpConfigPath, "utf-8"));
|
|
5014
5119
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
5015
5120
|
} catch {
|
|
5016
5121
|
}
|
|
@@ -5033,10 +5138,10 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5033
5138
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
5034
5139
|
}
|
|
5035
5140
|
const childEnv = { ...process.env };
|
|
5036
|
-
const envIntPath =
|
|
5141
|
+
const envIntPath = join14(projectDir, ".env.integrations");
|
|
5037
5142
|
if (existsSync7(envIntPath)) {
|
|
5038
5143
|
try {
|
|
5039
|
-
Object.assign(childEnv, parseEnvIntegrations(
|
|
5144
|
+
Object.assign(childEnv, parseEnvIntegrations(readFileSync12(envIntPath, "utf-8")));
|
|
5040
5145
|
} catch {
|
|
5041
5146
|
}
|
|
5042
5147
|
}
|
|
@@ -5133,75 +5238,6 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5133
5238
|
claudeSchedulerStates.set(codeName, updated);
|
|
5134
5239
|
}
|
|
5135
5240
|
}
|
|
5136
|
-
async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput, delivery) {
|
|
5137
|
-
try {
|
|
5138
|
-
const classification = classifyOutput(rawOutput);
|
|
5139
|
-
if (classification.action === "suppress") {
|
|
5140
|
-
const trimmed = (rawOutput ?? "").trim();
|
|
5141
|
-
const outputHash = trimmed.length === 0 ? "empty" : createHash5("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
5142
|
-
log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
5143
|
-
if (classification.suppressedNotes) {
|
|
5144
|
-
const notesHash = createHash5("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
|
|
5145
|
-
log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
|
|
5146
|
-
}
|
|
5147
|
-
if (delivery?.mode === "announce" && delivery.to) {
|
|
5148
|
-
await reportDeliveryStatus(agentId, delivery.taskId, {
|
|
5149
|
-
status: "skipped",
|
|
5150
|
-
error_code: "NO_CONTENT"
|
|
5151
|
-
});
|
|
5152
|
-
}
|
|
5153
|
-
return;
|
|
5154
|
-
}
|
|
5155
|
-
const output = classification.deliverable;
|
|
5156
|
-
if (classification.action === "strip") {
|
|
5157
|
-
log(`[claude-scheduler] Stripped '<no-delivery/>' sentinel from '${codeName}' output (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 agent mixed it with real content; delivering the rest.`);
|
|
5158
|
-
}
|
|
5159
|
-
if (STANDUP_TEMPLATES.has(templateId)) {
|
|
5160
|
-
const standup = parseStandupSummary(output);
|
|
5161
|
-
await api.post("/host/agent-status", {
|
|
5162
|
-
agent_code_name: codeName,
|
|
5163
|
-
standup,
|
|
5164
|
-
current_status: "idle"
|
|
5165
|
-
});
|
|
5166
|
-
log(`[claude-scheduler] Standup posted for '${codeName}'`);
|
|
5167
|
-
} else if (TASK_UPDATE_TEMPLATES.has(templateId)) {
|
|
5168
|
-
await api.post("/host/agent-status", {
|
|
5169
|
-
agent_code_name: codeName,
|
|
5170
|
-
current_tasks: output.slice(0, 2e3)
|
|
5171
|
-
});
|
|
5172
|
-
log(`[claude-scheduler] Task update posted for '${codeName}'`);
|
|
5173
|
-
} else if (PLAN_TEMPLATES.has(templateId)) {
|
|
5174
|
-
const planItems = parsePlanItems(output);
|
|
5175
|
-
if (planItems.length > 0) {
|
|
5176
|
-
await api.post("/host/kanban", {
|
|
5177
|
-
agent_id: agentId,
|
|
5178
|
-
add: planItems
|
|
5179
|
-
});
|
|
5180
|
-
log(`[claude-scheduler] Plan items posted for '${codeName}' (${planItems.length} items)`);
|
|
5181
|
-
}
|
|
5182
|
-
} else if (KANBAN_WORK_TEMPLATES.has(templateId)) {
|
|
5183
|
-
const kanbanUpdates = parseKanbanUpdates(output);
|
|
5184
|
-
if (kanbanUpdates.length > 0) {
|
|
5185
|
-
await api.post("/host/kanban", {
|
|
5186
|
-
agent_id: agentId,
|
|
5187
|
-
update: kanbanUpdates
|
|
5188
|
-
});
|
|
5189
|
-
log(`[claude-scheduler] Kanban updates posted for '${codeName}' (${kanbanUpdates.length} updates)`);
|
|
5190
|
-
}
|
|
5191
|
-
}
|
|
5192
|
-
if (delivery?.mode === "announce" && delivery.to) {
|
|
5193
|
-
await deliverScheduledTaskOutput(
|
|
5194
|
-
codeName,
|
|
5195
|
-
agentId,
|
|
5196
|
-
delivery.to,
|
|
5197
|
-
output.slice(0, 4e3),
|
|
5198
|
-
delivery.taskId
|
|
5199
|
-
);
|
|
5200
|
-
}
|
|
5201
|
-
} catch (err) {
|
|
5202
|
-
log(`[claude-scheduler] Failed to post result for '${codeName}': ${err.message}`);
|
|
5203
|
-
}
|
|
5204
|
-
}
|
|
5205
5241
|
|
|
5206
5242
|
// src/lib/wedge-detection.ts
|
|
5207
5243
|
var DEFAULTS = {
|
|
@@ -5342,15 +5378,15 @@ function partitionActionableByPoison(actionable, states, config2) {
|
|
|
5342
5378
|
}
|
|
5343
5379
|
|
|
5344
5380
|
// src/lib/restart-flags.ts
|
|
5345
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
5346
|
-
import { homedir as
|
|
5347
|
-
import { join as
|
|
5381
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync13, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
5382
|
+
import { homedir as homedir8 } from "os";
|
|
5383
|
+
import { join as join15 } from "path";
|
|
5348
5384
|
import { randomUUID } from "crypto";
|
|
5349
5385
|
function restartFlagsDir() {
|
|
5350
|
-
return
|
|
5386
|
+
return join15(homedir8(), ".augmented", "restart-flags");
|
|
5351
5387
|
}
|
|
5352
5388
|
function flagPath(codeName) {
|
|
5353
|
-
return
|
|
5389
|
+
return join15(restartFlagsDir(), `${codeName}.flag`);
|
|
5354
5390
|
}
|
|
5355
5391
|
function readRestartFlags() {
|
|
5356
5392
|
const dir = restartFlagsDir();
|
|
@@ -5359,7 +5395,7 @@ function readRestartFlags() {
|
|
|
5359
5395
|
for (const entry of readdirSync4(dir)) {
|
|
5360
5396
|
if (!entry.endsWith(".flag")) continue;
|
|
5361
5397
|
try {
|
|
5362
|
-
const raw =
|
|
5398
|
+
const raw = readFileSync13(join15(dir, entry), "utf8");
|
|
5363
5399
|
const parsed = JSON.parse(raw);
|
|
5364
5400
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
5365
5401
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -5912,8 +5948,8 @@ function applyRestartAcks(args) {
|
|
|
5912
5948
|
var GATEWAY_PORT_BASE = 18800;
|
|
5913
5949
|
var GATEWAY_PORT_STEP = 10;
|
|
5914
5950
|
var GATEWAY_PORT_MAX = 18899;
|
|
5915
|
-
var AUGMENTED_DIR =
|
|
5916
|
-
var GATEWAY_PORTS_FILE =
|
|
5951
|
+
var AUGMENTED_DIR = join16(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
5952
|
+
var GATEWAY_PORTS_FILE = join16(AUGMENTED_DIR, "gateway-ports.json");
|
|
5917
5953
|
var CHANNEL_SWEEP_INTERVAL_MS = (() => {
|
|
5918
5954
|
const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
|
|
5919
5955
|
if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
|
|
@@ -6411,15 +6447,15 @@ var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
|
6411
6447
|
var runningChannelSecretHashes = /* @__PURE__ */ new Map();
|
|
6412
6448
|
function projectMcpHash(_codeName, projectDir) {
|
|
6413
6449
|
try {
|
|
6414
|
-
const raw =
|
|
6415
|
-
return
|
|
6450
|
+
const raw = readFileSync14(join16(projectDir, ".mcp.json"), "utf-8");
|
|
6451
|
+
return createHash8("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
6416
6452
|
} catch {
|
|
6417
6453
|
return null;
|
|
6418
6454
|
}
|
|
6419
6455
|
}
|
|
6420
6456
|
function projectMcpKeys(_codeName, projectDir) {
|
|
6421
6457
|
try {
|
|
6422
|
-
const raw =
|
|
6458
|
+
const raw = readFileSync14(join16(projectDir, ".mcp.json"), "utf-8");
|
|
6423
6459
|
const parsed = JSON.parse(raw);
|
|
6424
6460
|
const servers = parsed.mcpServers;
|
|
6425
6461
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -6430,7 +6466,7 @@ function projectMcpKeys(_codeName, projectDir) {
|
|
|
6430
6466
|
}
|
|
6431
6467
|
function readMcpHttpServerConfig(projectDir, serverKey, env) {
|
|
6432
6468
|
try {
|
|
6433
|
-
const raw =
|
|
6469
|
+
const raw = readFileSync14(join16(projectDir, ".mcp.json"), "utf-8");
|
|
6434
6470
|
const servers = JSON.parse(raw).mcpServers ?? {};
|
|
6435
6471
|
const entry = servers[serverKey];
|
|
6436
6472
|
if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
|
|
@@ -6468,9 +6504,9 @@ async function runAgentConnectivityProbes(agent, integrations, projectDir) {
|
|
|
6468
6504
|
if (integrations.length === 0) return;
|
|
6469
6505
|
const probeEnv = { ...process.env };
|
|
6470
6506
|
try {
|
|
6471
|
-
const envIntPath =
|
|
6507
|
+
const envIntPath = join16(projectDir, ".env.integrations");
|
|
6472
6508
|
if (existsSync9(envIntPath)) {
|
|
6473
|
-
Object.assign(probeEnv, parseEnvIntegrations(
|
|
6509
|
+
Object.assign(probeEnv, parseEnvIntegrations(readFileSync14(envIntPath, "utf-8")));
|
|
6474
6510
|
}
|
|
6475
6511
|
} catch {
|
|
6476
6512
|
}
|
|
@@ -6683,7 +6719,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
|
6683
6719
|
function projectChannelSecretHash(projectDir) {
|
|
6684
6720
|
try {
|
|
6685
6721
|
const entries = parseEnvIntegrations(
|
|
6686
|
-
|
|
6722
|
+
readFileSync14(join16(projectDir, ".env.integrations"), "utf-8")
|
|
6687
6723
|
);
|
|
6688
6724
|
return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
|
|
6689
6725
|
} catch {
|
|
@@ -6771,7 +6807,7 @@ var cachedMaintenanceWindow = null;
|
|
|
6771
6807
|
var lastVersionCheckAt = 0;
|
|
6772
6808
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
6773
6809
|
var lastResponsivenessProbeAt = 0;
|
|
6774
|
-
var agtCliVersion = true ? "0.28.
|
|
6810
|
+
var agtCliVersion = true ? "0.28.38" : "dev";
|
|
6775
6811
|
function resolveBrewPath(execFileSync4) {
|
|
6776
6812
|
try {
|
|
6777
6813
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -6866,7 +6902,7 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
6866
6902
|
toolkitCliEnsured.add(toolkitSlug);
|
|
6867
6903
|
return;
|
|
6868
6904
|
}
|
|
6869
|
-
brewBinDir =
|
|
6905
|
+
brewBinDir = dirname4(brewPath);
|
|
6870
6906
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
6871
6907
|
log(`[toolkit-install] ${toolkitSlug}: installing via brew (${pkg})\u2026`);
|
|
6872
6908
|
if (isRoot) {
|
|
@@ -6948,7 +6984,7 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
6948
6984
|
try {
|
|
6949
6985
|
let settings = {};
|
|
6950
6986
|
if (existsSync9(path)) {
|
|
6951
|
-
const raw =
|
|
6987
|
+
const raw = readFileSync14(path, "utf-8").trim();
|
|
6952
6988
|
if (raw) {
|
|
6953
6989
|
let parsed;
|
|
6954
6990
|
try {
|
|
@@ -6964,8 +7000,8 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
6964
7000
|
}
|
|
6965
7001
|
if (settings.channelsEnabled === true) return "ok";
|
|
6966
7002
|
settings.channelsEnabled = true;
|
|
6967
|
-
|
|
6968
|
-
|
|
7003
|
+
mkdirSync5(dirname4(path), { recursive: true });
|
|
7004
|
+
writeFileSync5(path, `${JSON.stringify(settings, null, 2)}
|
|
6969
7005
|
`);
|
|
6970
7006
|
log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
|
|
6971
7007
|
return "ok";
|
|
@@ -7006,7 +7042,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
7006
7042
|
log(`Claude Code install failed: ${err.message}`);
|
|
7007
7043
|
return;
|
|
7008
7044
|
}
|
|
7009
|
-
const brewBinDir =
|
|
7045
|
+
const brewBinDir = dirname4(brewPath);
|
|
7010
7046
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
7011
7047
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
7012
7048
|
}
|
|
@@ -7019,26 +7055,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
7019
7055
|
}
|
|
7020
7056
|
agentRuntimeAuthenticated = await checkClaudeAuth();
|
|
7021
7057
|
}
|
|
7022
|
-
var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
7023
7058
|
var claudeCodeUpgradeInFlight = false;
|
|
7024
|
-
function claudeCodeUpgradeMarkerPath() {
|
|
7025
|
-
return join15(homedir8(), ".augmented", ".last-claude-code-upgrade-check");
|
|
7026
|
-
}
|
|
7027
|
-
function stampClaudeCodeUpgradeMarker() {
|
|
7028
|
-
try {
|
|
7029
|
-
writeFileSync4(claudeCodeUpgradeMarkerPath(), String(Date.now()));
|
|
7030
|
-
} catch {
|
|
7031
|
-
}
|
|
7032
|
-
}
|
|
7033
|
-
function claudeCodeUpgradeThrottled() {
|
|
7034
|
-
try {
|
|
7035
|
-
const lastCheck = parseInt(readFileSync13(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
7036
|
-
if (!Number.isFinite(lastCheck)) return false;
|
|
7037
|
-
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
7038
|
-
} catch {
|
|
7039
|
-
return false;
|
|
7040
|
-
}
|
|
7041
|
-
}
|
|
7042
7059
|
async function maybeUpgradeClaudeCode() {
|
|
7043
7060
|
if (claudeCodeUpgradeInFlight) return;
|
|
7044
7061
|
if (claudeCodeUpgradeThrottled()) return;
|
|
@@ -7085,7 +7102,7 @@ ${r.stderr}`;
|
|
|
7085
7102
|
}
|
|
7086
7103
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
7087
7104
|
function selfUpdateAppliedMarkerPath() {
|
|
7088
|
-
return
|
|
7105
|
+
return join16(homedir9(), ".augmented", ".last-self-update-applied");
|
|
7089
7106
|
}
|
|
7090
7107
|
var selfUpdateUpToDateLogged = false;
|
|
7091
7108
|
var restartAfterUpgrade = false;
|
|
@@ -7108,7 +7125,7 @@ async function checkAndUpdateCli() {
|
|
|
7108
7125
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
7109
7126
|
if (!isBrewFormula && !isNpmGlobal) return;
|
|
7110
7127
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
7111
|
-
const markerPath =
|
|
7128
|
+
const markerPath = join16(homedir9(), ".augmented", ".last-update-check");
|
|
7112
7129
|
try {
|
|
7113
7130
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
7114
7131
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
@@ -7366,13 +7383,13 @@ async function checkClaudeAuth() {
|
|
|
7366
7383
|
var evalEmptyMcpConfigPath = null;
|
|
7367
7384
|
function ensureEvalEmptyMcpConfig() {
|
|
7368
7385
|
if (evalEmptyMcpConfigPath && existsSync9(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
|
|
7369
|
-
const dir =
|
|
7386
|
+
const dir = join16(homedir9(), ".augmented");
|
|
7370
7387
|
try {
|
|
7371
|
-
|
|
7388
|
+
mkdirSync5(dir, { recursive: true });
|
|
7372
7389
|
} catch {
|
|
7373
7390
|
}
|
|
7374
|
-
const p =
|
|
7375
|
-
|
|
7391
|
+
const p = join16(dir, ".eval-empty-mcp.json");
|
|
7392
|
+
writeFileSync5(p, JSON.stringify({ mcpServers: {} }));
|
|
7376
7393
|
evalEmptyMcpConfigPath = p;
|
|
7377
7394
|
return p;
|
|
7378
7395
|
}
|
|
@@ -7397,7 +7414,7 @@ async function runEvalClaude(prompt, model) {
|
|
|
7397
7414
|
""
|
|
7398
7415
|
];
|
|
7399
7416
|
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
|
|
7400
|
-
cwd:
|
|
7417
|
+
cwd: homedir9(),
|
|
7401
7418
|
timeout: 12e4,
|
|
7402
7419
|
stdin: "ignore",
|
|
7403
7420
|
env: childEnv,
|
|
@@ -7446,14 +7463,14 @@ function resolveConversationEvalBackend() {
|
|
|
7446
7463
|
}
|
|
7447
7464
|
function loadGatewayPorts() {
|
|
7448
7465
|
try {
|
|
7449
|
-
return JSON.parse(
|
|
7466
|
+
return JSON.parse(readFileSync14(GATEWAY_PORTS_FILE, "utf-8"));
|
|
7450
7467
|
} catch {
|
|
7451
7468
|
return {};
|
|
7452
7469
|
}
|
|
7453
7470
|
}
|
|
7454
7471
|
function saveGatewayPorts(ports) {
|
|
7455
|
-
|
|
7456
|
-
|
|
7472
|
+
mkdirSync5(AUGMENTED_DIR, { recursive: true });
|
|
7473
|
+
writeFileSync5(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
7457
7474
|
}
|
|
7458
7475
|
function allocatePort(codeName) {
|
|
7459
7476
|
const ports = loadGatewayPorts();
|
|
@@ -7476,10 +7493,10 @@ function freePort(codeName) {
|
|
|
7476
7493
|
}
|
|
7477
7494
|
}
|
|
7478
7495
|
function getStateFile() {
|
|
7479
|
-
return
|
|
7496
|
+
return join16(config?.configDir ?? join16(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
7480
7497
|
}
|
|
7481
7498
|
function channelHashCacheDir() {
|
|
7482
|
-
return config?.configDir ??
|
|
7499
|
+
return config?.configDir ?? join16(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
7483
7500
|
}
|
|
7484
7501
|
function loadChannelHashCache2() {
|
|
7485
7502
|
loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -7490,7 +7507,7 @@ function saveChannelHashCache2() {
|
|
|
7490
7507
|
var _channelQuarantineStore = null;
|
|
7491
7508
|
function channelQuarantineStore() {
|
|
7492
7509
|
if (!_channelQuarantineStore) {
|
|
7493
|
-
const dir = config?.configDir ??
|
|
7510
|
+
const dir = config?.configDir ?? join16(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
7494
7511
|
_channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
|
|
7495
7512
|
}
|
|
7496
7513
|
return _channelQuarantineStore;
|
|
@@ -7498,7 +7515,7 @@ function channelQuarantineStore() {
|
|
|
7498
7515
|
var _hostFlagStore = null;
|
|
7499
7516
|
function hostFlagStore() {
|
|
7500
7517
|
if (!_hostFlagStore) {
|
|
7501
|
-
const dir = config?.configDir ??
|
|
7518
|
+
const dir = config?.configDir ?? join16(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
7502
7519
|
_hostFlagStore = new HostFlagStore({ cachePath: defaultFlagsCachePath(dir), log });
|
|
7503
7520
|
}
|
|
7504
7521
|
return _hostFlagStore;
|
|
@@ -7549,13 +7566,13 @@ function parseSkillFrontmatter(content) {
|
|
|
7549
7566
|
return out;
|
|
7550
7567
|
}
|
|
7551
7568
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
7552
|
-
const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync:
|
|
7553
|
-
const skillsDir =
|
|
7554
|
-
const claudeMdPath =
|
|
7569
|
+
const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync6 } = await import("fs");
|
|
7570
|
+
const skillsDir = join16(configDir, codeName, "project", ".claude", "skills");
|
|
7571
|
+
const claudeMdPath = join16(configDir, codeName, "project", "CLAUDE.md");
|
|
7555
7572
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
7556
7573
|
const entries = [];
|
|
7557
7574
|
for (const dir of readdirSync6(skillsDir).sort()) {
|
|
7558
|
-
const skillFile =
|
|
7575
|
+
const skillFile = join16(skillsDir, dir, "SKILL.md");
|
|
7559
7576
|
if (!ex(skillFile)) continue;
|
|
7560
7577
|
try {
|
|
7561
7578
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -7597,16 +7614,16 @@ ${SKILLS_INDEX_END}`;
|
|
|
7597
7614
|
next = current.trimEnd() + "\n\n" + section + "\n";
|
|
7598
7615
|
}
|
|
7599
7616
|
if (next !== current) {
|
|
7600
|
-
|
|
7617
|
+
writeFileSync6(claudeMdPath, next, "utf-8");
|
|
7601
7618
|
log2(`Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)`);
|
|
7602
7619
|
}
|
|
7603
7620
|
}
|
|
7604
7621
|
async function migrateToProfiles() {
|
|
7605
7622
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
7606
|
-
const sharedConfigPath =
|
|
7623
|
+
const sharedConfigPath = join16(homeDir, ".openclaw", "openclaw.json");
|
|
7607
7624
|
let sharedConfig;
|
|
7608
7625
|
try {
|
|
7609
|
-
sharedConfig = JSON.parse(
|
|
7626
|
+
sharedConfig = JSON.parse(readFileSync14(sharedConfigPath, "utf-8"));
|
|
7610
7627
|
} catch {
|
|
7611
7628
|
return;
|
|
7612
7629
|
}
|
|
@@ -7619,19 +7636,19 @@ async function migrateToProfiles() {
|
|
|
7619
7636
|
const codeName = agentEntry["id"];
|
|
7620
7637
|
if (!codeName) continue;
|
|
7621
7638
|
if (codeName === "main") continue;
|
|
7622
|
-
const profileDir =
|
|
7623
|
-
if (existsSync9(
|
|
7639
|
+
const profileDir = join16(homeDir, `.openclaw-${codeName}`);
|
|
7640
|
+
if (existsSync9(join16(profileDir, "openclaw.json"))) continue;
|
|
7624
7641
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
7625
7642
|
if (adapter.seedProfileConfig) {
|
|
7626
7643
|
adapter.seedProfileConfig(codeName);
|
|
7627
7644
|
}
|
|
7628
|
-
const sharedAuthDir =
|
|
7629
|
-
const profileAuthDir =
|
|
7630
|
-
const authFile =
|
|
7645
|
+
const sharedAuthDir = join16(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
7646
|
+
const profileAuthDir = join16(profileDir, "agents", codeName, "agent");
|
|
7647
|
+
const authFile = join16(sharedAuthDir, "auth-profiles.json");
|
|
7631
7648
|
if (existsSync9(authFile)) {
|
|
7632
|
-
|
|
7633
|
-
const authContent =
|
|
7634
|
-
|
|
7649
|
+
mkdirSync5(profileAuthDir, { recursive: true });
|
|
7650
|
+
const authContent = readFileSync14(authFile, "utf-8");
|
|
7651
|
+
writeFileSync5(join16(profileAuthDir, "auth-profiles.json"), authContent);
|
|
7635
7652
|
}
|
|
7636
7653
|
allocatePort(codeName);
|
|
7637
7654
|
migrated++;
|
|
@@ -7647,7 +7664,7 @@ function readGatewayToken(codeName) {
|
|
|
7647
7664
|
}
|
|
7648
7665
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
7649
7666
|
try {
|
|
7650
|
-
const cfg = JSON.parse(
|
|
7667
|
+
const cfg = JSON.parse(readFileSync14(join16(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
7651
7668
|
return cfg?.gateway?.auth?.token;
|
|
7652
7669
|
} catch {
|
|
7653
7670
|
return void 0;
|
|
@@ -7656,10 +7673,10 @@ function readGatewayToken(codeName) {
|
|
|
7656
7673
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
7657
7674
|
function isGatewayHung(codeName) {
|
|
7658
7675
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
7659
|
-
const jobsPath =
|
|
7676
|
+
const jobsPath = join16(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
7660
7677
|
if (!existsSync9(jobsPath)) return false;
|
|
7661
7678
|
try {
|
|
7662
|
-
const data = JSON.parse(
|
|
7679
|
+
const data = JSON.parse(readFileSync14(jobsPath, "utf-8"));
|
|
7663
7680
|
const jobs = data.jobs ?? data;
|
|
7664
7681
|
if (!Array.isArray(jobs)) return false;
|
|
7665
7682
|
const now = Date.now();
|
|
@@ -7692,19 +7709,19 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
7692
7709
|
}
|
|
7693
7710
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
7694
7711
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
7695
|
-
const cronJobsPath =
|
|
7712
|
+
const cronJobsPath = join16(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
7696
7713
|
clearStaleCronRunState(cronJobsPath);
|
|
7697
7714
|
} else {
|
|
7698
7715
|
if (status.port) {
|
|
7699
7716
|
try {
|
|
7700
7717
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
7701
|
-
const configPath =
|
|
7718
|
+
const configPath = join16(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
7702
7719
|
if (existsSync9(configPath)) {
|
|
7703
|
-
const cfg = JSON.parse(
|
|
7720
|
+
const cfg = JSON.parse(readFileSync14(configPath, "utf-8"));
|
|
7704
7721
|
if (cfg.gateway?.port !== status.port) {
|
|
7705
7722
|
if (!cfg.gateway) cfg.gateway = {};
|
|
7706
7723
|
cfg.gateway.port = status.port;
|
|
7707
|
-
|
|
7724
|
+
writeFileSync5(configPath, JSON.stringify(cfg, null, 2));
|
|
7708
7725
|
}
|
|
7709
7726
|
}
|
|
7710
7727
|
} catch {
|
|
@@ -7724,12 +7741,12 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
7724
7741
|
gatewaysStartedThisCycle.add(codeName);
|
|
7725
7742
|
try {
|
|
7726
7743
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
7727
|
-
const configPath =
|
|
7744
|
+
const configPath = join16(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
7728
7745
|
if (existsSync9(configPath)) {
|
|
7729
|
-
const cfg = JSON.parse(
|
|
7746
|
+
const cfg = JSON.parse(readFileSync14(configPath, "utf-8"));
|
|
7730
7747
|
if (!cfg.gateway) cfg.gateway = {};
|
|
7731
7748
|
cfg.gateway.port = port;
|
|
7732
|
-
|
|
7749
|
+
writeFileSync5(configPath, JSON.stringify(cfg, null, 2));
|
|
7733
7750
|
}
|
|
7734
7751
|
} catch {
|
|
7735
7752
|
}
|
|
@@ -7929,7 +7946,7 @@ async function pollCycle() {
|
|
|
7929
7946
|
claudeAuth = await detectClaudeAuth();
|
|
7930
7947
|
} catch (err) {
|
|
7931
7948
|
const errText = err instanceof Error ? err.message : String(err);
|
|
7932
|
-
const errId =
|
|
7949
|
+
const errId = createHash8("sha256").update(errText).digest("hex").slice(0, 12);
|
|
7933
7950
|
log(`Claude auth detection failed (error_id=${errId})`);
|
|
7934
7951
|
}
|
|
7935
7952
|
const hostHasClaudeCode = state6.agents.some(
|
|
@@ -8289,7 +8306,7 @@ async function pollCycle() {
|
|
|
8289
8306
|
}
|
|
8290
8307
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
8291
8308
|
freePort(prev.codeName);
|
|
8292
|
-
const agentDir =
|
|
8309
|
+
const agentDir = join16(adapter.getAgentDir(prev.codeName), "provision");
|
|
8293
8310
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
8294
8311
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
8295
8312
|
}
|
|
@@ -8375,10 +8392,10 @@ async function pollCycle() {
|
|
|
8375
8392
|
// pending-inbound marker. Best-effort: a write failure is logged by
|
|
8376
8393
|
// the watchdog, never fails the poll cycle.
|
|
8377
8394
|
signalGiveUp: (codeName) => {
|
|
8378
|
-
const dir =
|
|
8395
|
+
const dir = join16(homedir9(), ".augmented", codeName);
|
|
8379
8396
|
if (!existsSync9(dir)) return;
|
|
8380
8397
|
atomicWriteFileSync(
|
|
8381
|
-
|
|
8398
|
+
join16(dir, "watchdog-give-up.json"),
|
|
8382
8399
|
JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
|
|
8383
8400
|
);
|
|
8384
8401
|
}
|
|
@@ -8505,7 +8522,7 @@ async function processAgent(agent, agentStates) {
|
|
|
8505
8522
|
}
|
|
8506
8523
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8507
8524
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
8508
|
-
let agentDir =
|
|
8525
|
+
let agentDir = join16(adapter.getAgentDir(agent.code_name), "provision");
|
|
8509
8526
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
8510
8527
|
if (previousKnownStatus !== agent.status) {
|
|
8511
8528
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -8709,7 +8726,7 @@ async function processAgent(agent, agentStates) {
|
|
|
8709
8726
|
const frameworkId = refreshData.agent.framework ?? "openclaw";
|
|
8710
8727
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
8711
8728
|
const frameworkAdapter = getFramework(frameworkId);
|
|
8712
|
-
agentDir =
|
|
8729
|
+
agentDir = join16(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
8713
8730
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
8714
8731
|
if (frameworkAdapter.migrateSecretStorage && !migratedSecretStorage.has(agent.code_name)) {
|
|
8715
8732
|
try {
|
|
@@ -8750,9 +8767,9 @@ async function processAgent(agent, agentStates) {
|
|
|
8750
8767
|
try {
|
|
8751
8768
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
8752
8769
|
const changedFiles = [];
|
|
8753
|
-
|
|
8770
|
+
mkdirSync5(agentDir, { recursive: true });
|
|
8754
8771
|
for (const artifact of artifacts) {
|
|
8755
|
-
const filePath =
|
|
8772
|
+
const filePath = join16(agentDir, artifact.relativePath);
|
|
8756
8773
|
let existingHash;
|
|
8757
8774
|
let newHash;
|
|
8758
8775
|
let writeContent = artifact.content;
|
|
@@ -8771,8 +8788,8 @@ async function processAgent(agent, agentStates) {
|
|
|
8771
8788
|
};
|
|
8772
8789
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
8773
8790
|
try {
|
|
8774
|
-
const projectClaudeMd =
|
|
8775
|
-
const existing =
|
|
8791
|
+
const projectClaudeMd = join16(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
8792
|
+
const existing = readFileSync14(projectClaudeMd, "utf-8");
|
|
8776
8793
|
existingHash = sha256(stripDynamicSections(existing));
|
|
8777
8794
|
} catch {
|
|
8778
8795
|
existingHash = null;
|
|
@@ -8790,7 +8807,7 @@ async function processAgent(agent, agentStates) {
|
|
|
8790
8807
|
const generatorKeys = Object.keys(generatorServers);
|
|
8791
8808
|
let existingRaw = "";
|
|
8792
8809
|
try {
|
|
8793
|
-
existingRaw =
|
|
8810
|
+
existingRaw = readFileSync14(filePath, "utf-8");
|
|
8794
8811
|
} catch {
|
|
8795
8812
|
}
|
|
8796
8813
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -8812,26 +8829,26 @@ async function processAgent(agent, agentStates) {
|
|
|
8812
8829
|
}
|
|
8813
8830
|
}
|
|
8814
8831
|
if (changedFiles.length > 0) {
|
|
8815
|
-
const isFirst = !existsSync9(
|
|
8832
|
+
const isFirst = !existsSync9(join16(agentDir, "CHARTER.md"));
|
|
8816
8833
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
8817
8834
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
8818
8835
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
8819
8836
|
for (const file of changedFiles) {
|
|
8820
|
-
const filePath =
|
|
8821
|
-
|
|
8837
|
+
const filePath = join16(agentDir, file.relativePath);
|
|
8838
|
+
mkdirSync5(dirname4(filePath), { recursive: true });
|
|
8822
8839
|
if (file.relativePath === ".mcp.json") {
|
|
8823
8840
|
safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
|
|
8824
8841
|
} else {
|
|
8825
|
-
|
|
8842
|
+
writeFileSync5(filePath, file.content);
|
|
8826
8843
|
}
|
|
8827
8844
|
}
|
|
8828
8845
|
try {
|
|
8829
|
-
const provSkillsDir =
|
|
8846
|
+
const provSkillsDir = join16(agentDir, ".claude", "skills");
|
|
8830
8847
|
if (existsSync9(provSkillsDir)) {
|
|
8831
8848
|
for (const folder of readdirSync5(provSkillsDir)) {
|
|
8832
8849
|
if (folder.startsWith("knowledge-")) {
|
|
8833
8850
|
try {
|
|
8834
|
-
rmSync3(
|
|
8851
|
+
rmSync3(join16(provSkillsDir, folder), { recursive: true });
|
|
8835
8852
|
} catch {
|
|
8836
8853
|
}
|
|
8837
8854
|
}
|
|
@@ -8844,7 +8861,7 @@ async function processAgent(agent, agentStates) {
|
|
|
8844
8861
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
8845
8862
|
const hashes = /* @__PURE__ */ new Map();
|
|
8846
8863
|
for (const file of trackedFiles2) {
|
|
8847
|
-
const h = hashFile(
|
|
8864
|
+
const h = hashFile(join16(agentDir, file));
|
|
8848
8865
|
if (h) hashes.set(file, h);
|
|
8849
8866
|
}
|
|
8850
8867
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -8862,14 +8879,14 @@ async function processAgent(agent, agentStates) {
|
|
|
8862
8879
|
}
|
|
8863
8880
|
if (Array.isArray(refreshData.workflows)) {
|
|
8864
8881
|
try {
|
|
8865
|
-
const provWorkflowsDir =
|
|
8882
|
+
const provWorkflowsDir = join16(agentDir, ".claude", "workflows");
|
|
8866
8883
|
if (existsSync9(provWorkflowsDir)) {
|
|
8867
8884
|
const expected = new Set(refreshData.workflows.map((w) => `${w.name}.js`));
|
|
8868
8885
|
for (const file of readdirSync5(provWorkflowsDir)) {
|
|
8869
8886
|
if (!file.endsWith(".js")) continue;
|
|
8870
8887
|
if (expected.has(file)) continue;
|
|
8871
8888
|
try {
|
|
8872
|
-
rmSync3(
|
|
8889
|
+
rmSync3(join16(provWorkflowsDir, file));
|
|
8873
8890
|
} catch {
|
|
8874
8891
|
}
|
|
8875
8892
|
}
|
|
@@ -8929,7 +8946,7 @@ async function processAgent(agent, agentStates) {
|
|
|
8929
8946
|
if (written && existsSync9(agentDir)) {
|
|
8930
8947
|
const driftedFiles = [];
|
|
8931
8948
|
for (const [file, expectedHash] of written) {
|
|
8932
|
-
const localHash = hashFile(
|
|
8949
|
+
const localHash = hashFile(join16(agentDir, file));
|
|
8933
8950
|
if (localHash && localHash !== expectedHash) {
|
|
8934
8951
|
driftedFiles.push(file);
|
|
8935
8952
|
}
|
|
@@ -8940,7 +8957,7 @@ async function processAgent(agent, agentStates) {
|
|
|
8940
8957
|
try {
|
|
8941
8958
|
const localHashes = {};
|
|
8942
8959
|
for (const file of driftedFiles) {
|
|
8943
|
-
localHashes[file] = hashFile(
|
|
8960
|
+
localHashes[file] = hashFile(join16(agentDir, file));
|
|
8944
8961
|
}
|
|
8945
8962
|
await api.post("/host/drift", {
|
|
8946
8963
|
agent_id: agent.agent_id,
|
|
@@ -9014,7 +9031,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9014
9031
|
const sessionModeForHash = refreshData.agent.session_mode;
|
|
9015
9032
|
const senderPolicyForHash = refreshData.sender_policy ?? null;
|
|
9016
9033
|
const CHANNEL_WRITE_VERSION = 9;
|
|
9017
|
-
const configHash =
|
|
9034
|
+
const configHash = createHash8("sha256").update(
|
|
9018
9035
|
canonicalJson({
|
|
9019
9036
|
writeVersion: CHANNEL_WRITE_VERSION,
|
|
9020
9037
|
config: entry.config,
|
|
@@ -9128,7 +9145,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9128
9145
|
if (channelConfigConverged) {
|
|
9129
9146
|
const hasSenderPolicyChannel = currentChannelIds.has("slack") || currentChannelIds.has("msteams");
|
|
9130
9147
|
const senderPolicyForRestartHash = refreshData.sender_policy ?? null;
|
|
9131
|
-
const senderPolicyHash =
|
|
9148
|
+
const senderPolicyHash = createHash8("sha256").update(canonicalJson({ senderPolicy: senderPolicyForRestartHash })).digest("hex");
|
|
9132
9149
|
const prevSenderPolicyHash = agentState.knownSenderPolicyHashes.get(agent.agent_id);
|
|
9133
9150
|
const senderPolicyDecision = hasSenderPolicyChannel ? decideSenderPolicyRestart({
|
|
9134
9151
|
previousHash: prevSenderPolicyHash,
|
|
@@ -9170,7 +9187,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9170
9187
|
const behaviourSubset = extractMsTeamsBehaviourSubset(
|
|
9171
9188
|
msteamsEntry?.config
|
|
9172
9189
|
);
|
|
9173
|
-
const behaviourHash =
|
|
9190
|
+
const behaviourHash = createHash8("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
|
|
9174
9191
|
const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
|
|
9175
9192
|
const behaviourDecision = decideSenderPolicyRestart({
|
|
9176
9193
|
previousHash: prevBehaviourHash,
|
|
@@ -9217,7 +9234,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9217
9234
|
const slackBehaviourSubset = extractSlackBehaviourSubset(
|
|
9218
9235
|
slackEntry?.config
|
|
9219
9236
|
);
|
|
9220
|
-
const slackBehaviourHash =
|
|
9237
|
+
const slackBehaviourHash = createHash8("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
|
|
9221
9238
|
const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
|
|
9222
9239
|
const slackBehaviourDecision = decideSenderPolicyRestart({
|
|
9223
9240
|
previousHash: prevSlackBehaviourHash,
|
|
@@ -9264,18 +9281,18 @@ async function processAgent(agent, agentStates) {
|
|
|
9264
9281
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
9265
9282
|
try {
|
|
9266
9283
|
const agentProvisionDir = agentDir;
|
|
9267
|
-
const projectDir =
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
const provisionMcpPath =
|
|
9271
|
-
const projectMcpPath =
|
|
9284
|
+
const projectDir = join16(homedir9(), ".augmented", agent.code_name, "project");
|
|
9285
|
+
mkdirSync5(agentProvisionDir, { recursive: true });
|
|
9286
|
+
mkdirSync5(projectDir, { recursive: true });
|
|
9287
|
+
const provisionMcpPath = join16(agentProvisionDir, ".mcp.json");
|
|
9288
|
+
const projectMcpPath = join16(projectDir, ".mcp.json");
|
|
9272
9289
|
let mcpConfig = { mcpServers: {} };
|
|
9273
9290
|
try {
|
|
9274
|
-
mcpConfig = JSON.parse(
|
|
9291
|
+
mcpConfig = JSON.parse(readFileSync14(provisionMcpPath, "utf-8"));
|
|
9275
9292
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
9276
9293
|
} catch {
|
|
9277
9294
|
}
|
|
9278
|
-
const localDirectChatChannel =
|
|
9295
|
+
const localDirectChatChannel = join16(homedir9(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
9279
9296
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
9280
9297
|
const directChatTz = (() => {
|
|
9281
9298
|
const tz = directChatTeamSettings?.["timezone"];
|
|
@@ -9305,7 +9322,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9305
9322
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
9306
9323
|
}
|
|
9307
9324
|
}
|
|
9308
|
-
const staleChannelsPath =
|
|
9325
|
+
const staleChannelsPath = join16(projectDir, ".mcp-channels.json");
|
|
9309
9326
|
if (existsSync9(staleChannelsPath)) {
|
|
9310
9327
|
try {
|
|
9311
9328
|
rmSync3(staleChannelsPath, { force: true });
|
|
@@ -9370,7 +9387,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9370
9387
|
}
|
|
9371
9388
|
if (process.env.AGT_CONNECTIVITY_PROBE_ENABLED === "true") {
|
|
9372
9389
|
try {
|
|
9373
|
-
const probeProjectDir =
|
|
9390
|
+
const probeProjectDir = join16(homedir9(), ".augmented", agent.code_name, "project");
|
|
9374
9391
|
await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
|
|
9375
9392
|
} catch (err) {
|
|
9376
9393
|
log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -9388,11 +9405,11 @@ async function processAgent(agent, agentStates) {
|
|
|
9388
9405
|
recordConfigChurnEvent(agent.agent_id, agent.code_name, FLAP_CHANNEL_INTEGRATIONS, intMembership);
|
|
9389
9406
|
}
|
|
9390
9407
|
if (intHash !== prevIntHash) {
|
|
9391
|
-
const projectDir =
|
|
9392
|
-
const envIntPath =
|
|
9408
|
+
const projectDir = join16(homedir9(), ".augmented", agent.code_name, "project");
|
|
9409
|
+
const envIntPath = join16(projectDir, ".env.integrations");
|
|
9393
9410
|
let preWriteEnv;
|
|
9394
9411
|
try {
|
|
9395
|
-
preWriteEnv =
|
|
9412
|
+
preWriteEnv = readFileSync14(envIntPath, "utf-8");
|
|
9396
9413
|
} catch {
|
|
9397
9414
|
preWriteEnv = void 0;
|
|
9398
9415
|
}
|
|
@@ -9404,9 +9421,9 @@ async function processAgent(agent, agentStates) {
|
|
|
9404
9421
|
let rotationHandled = true;
|
|
9405
9422
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
9406
9423
|
try {
|
|
9407
|
-
const projectMcpPath =
|
|
9408
|
-
const postWriteEnv =
|
|
9409
|
-
const mcpContent =
|
|
9424
|
+
const projectMcpPath = join16(projectDir, ".mcp.json");
|
|
9425
|
+
const postWriteEnv = readFileSync14(envIntPath, "utf-8");
|
|
9426
|
+
const mcpContent = readFileSync14(projectMcpPath, "utf-8");
|
|
9410
9427
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
9411
9428
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
9412
9429
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -9461,10 +9478,10 @@ async function processAgent(agent, agentStates) {
|
|
|
9461
9478
|
desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
|
|
9462
9479
|
}
|
|
9463
9480
|
const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
|
|
9464
|
-
const headersHash =
|
|
9481
|
+
const headersHash = createHash8("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
|
|
9465
9482
|
return `${e.serverId}|${e.url}|${headersHash}`;
|
|
9466
9483
|
}).join("\n");
|
|
9467
|
-
const mcpHash =
|
|
9484
|
+
const mcpHash = createHash8("sha256").update(hashBasis).digest("hex").slice(0, 16);
|
|
9468
9485
|
const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
|
|
9469
9486
|
const structureHash = managedMcpStructureHash(desiredEntries);
|
|
9470
9487
|
const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
|
|
@@ -9479,15 +9496,15 @@ async function processAgent(agent, agentStates) {
|
|
|
9479
9496
|
if (mcpHash !== prevMcpHash) {
|
|
9480
9497
|
for (const e of desiredEntries) {
|
|
9481
9498
|
frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
|
|
9482
|
-
const urlHash =
|
|
9499
|
+
const urlHash = createHash8("sha256").update(e.url).digest("hex").slice(0, 12);
|
|
9483
9500
|
log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
|
|
9484
9501
|
}
|
|
9485
9502
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
|
|
9486
9503
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
9487
9504
|
if (mcpPath) {
|
|
9488
9505
|
try {
|
|
9489
|
-
const { readFileSync:
|
|
9490
|
-
const mcpConfig = JSON.parse(
|
|
9506
|
+
const { readFileSync: readFileSync15 } = await import("fs");
|
|
9507
|
+
const mcpConfig = JSON.parse(readFileSync15(mcpPath, "utf-8"));
|
|
9491
9508
|
if (mcpConfig.mcpServers) {
|
|
9492
9509
|
const managedPrefixes = [
|
|
9493
9510
|
"composio_",
|
|
@@ -9588,7 +9605,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9588
9605
|
if (agent.status === "active") {
|
|
9589
9606
|
if (frameworkAdapter.installPlugin) {
|
|
9590
9607
|
try {
|
|
9591
|
-
const pluginPath =
|
|
9608
|
+
const pluginPath = join16(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
9592
9609
|
if (existsSync9(pluginPath)) {
|
|
9593
9610
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
9594
9611
|
agtHost: requireHost(),
|
|
@@ -9613,7 +9630,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9613
9630
|
if (frameworkAdapter.installSkillFiles) {
|
|
9614
9631
|
const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
|
|
9615
9632
|
const installedIntegrationSkills = [];
|
|
9616
|
-
const { createHash:
|
|
9633
|
+
const { createHash: createHash9 } = await import("crypto");
|
|
9617
9634
|
const refreshAny = refreshData;
|
|
9618
9635
|
const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
|
|
9619
9636
|
const contextBySlug = /* @__PURE__ */ new Map();
|
|
@@ -9642,7 +9659,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9642
9659
|
)
|
|
9643
9660
|
}));
|
|
9644
9661
|
const bundle = buildIntegrationBundle(renderedScopes);
|
|
9645
|
-
const contentHash =
|
|
9662
|
+
const contentHash = createHash9("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
|
|
9646
9663
|
const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
|
|
9647
9664
|
if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
|
|
9648
9665
|
frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
|
|
@@ -9655,18 +9672,18 @@ async function processAgent(agent, agentStates) {
|
|
|
9655
9672
|
}
|
|
9656
9673
|
try {
|
|
9657
9674
|
const { readdirSync: readdirSync6, rmSync: rmSync4 } = await import("fs");
|
|
9658
|
-
const { homedir:
|
|
9675
|
+
const { homedir: homedir10 } = await import("os");
|
|
9659
9676
|
const frameworkId2 = frameworkAdapter.id;
|
|
9660
9677
|
const candidateSkillDirs = [
|
|
9661
9678
|
// Claude Code — framework runtime tree
|
|
9662
|
-
|
|
9679
|
+
join16(homedir10(), ".augmented", agent.code_name, "skills"),
|
|
9663
9680
|
// Claude Code — project tree
|
|
9664
|
-
|
|
9681
|
+
join16(homedir10(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
9665
9682
|
// OpenClaw — framework runtime tree
|
|
9666
|
-
|
|
9683
|
+
join16(homedir10(), `.openclaw-${agent.code_name}`, "skills"),
|
|
9667
9684
|
// Defensive: legacy provision-side path, not currently an
|
|
9668
9685
|
// install target but cheap to sweep.
|
|
9669
|
-
|
|
9686
|
+
join16(agentDir, ".claude", "skills")
|
|
9670
9687
|
];
|
|
9671
9688
|
const existingDirs = candidateSkillDirs.filter((d) => existsSync9(d));
|
|
9672
9689
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
@@ -9682,7 +9699,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9682
9699
|
}
|
|
9683
9700
|
const removeSkillFolder = (entry, reason) => {
|
|
9684
9701
|
for (const dir of existingDirs) {
|
|
9685
|
-
const p =
|
|
9702
|
+
const p = join16(dir, entry);
|
|
9686
9703
|
if (existsSync9(p)) {
|
|
9687
9704
|
rmSync4(p, { recursive: true, force: true });
|
|
9688
9705
|
}
|
|
@@ -9701,7 +9718,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9701
9718
|
const plan = planGlobalSkillSync(
|
|
9702
9719
|
refreshAny.global_skills ?? [],
|
|
9703
9720
|
agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
|
|
9704
|
-
(content) =>
|
|
9721
|
+
(content) => createHash9("sha256").update(content).digest("hex").slice(0, 12),
|
|
9705
9722
|
(skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`)
|
|
9706
9723
|
);
|
|
9707
9724
|
for (const { skillId, content, hash } of plan.installs) {
|
|
@@ -9711,16 +9728,16 @@ async function processAgent(agent, agentStates) {
|
|
|
9711
9728
|
}
|
|
9712
9729
|
if (plan.removes.length) {
|
|
9713
9730
|
const { rmSync: rmSync4 } = await import("fs");
|
|
9714
|
-
const { homedir:
|
|
9731
|
+
const { homedir: homedir10 } = await import("os");
|
|
9715
9732
|
const globalSkillDirs = [
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9733
|
+
join16(homedir10(), ".augmented", agent.code_name, "skills"),
|
|
9734
|
+
join16(homedir10(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
9735
|
+
join16(homedir10(), `.openclaw-${agent.code_name}`, "skills"),
|
|
9736
|
+
join16(agentDir, ".claude", "skills")
|
|
9720
9737
|
];
|
|
9721
9738
|
for (const id of plan.removes) {
|
|
9722
9739
|
for (const dir of globalSkillDirs) {
|
|
9723
|
-
const p =
|
|
9740
|
+
const p = join16(dir, id);
|
|
9724
9741
|
if (existsSync9(p)) rmSync4(p, { recursive: true, force: true });
|
|
9725
9742
|
}
|
|
9726
9743
|
agentState.knownSkillHashes.delete(`global-skill:${agent.agent_id}:${id}`);
|
|
@@ -9745,7 +9762,7 @@ async function processAgent(agent, agentStates) {
|
|
|
9745
9762
|
const slug = hook.integration_slug ?? hook.plugin_slug;
|
|
9746
9763
|
if (!slug) continue;
|
|
9747
9764
|
try {
|
|
9748
|
-
const scriptHash =
|
|
9765
|
+
const scriptHash = createHash9("sha256").update(hook.script).digest("hex").slice(0, 12);
|
|
9749
9766
|
const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
|
|
9750
9767
|
if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
|
|
9751
9768
|
const result = await frameworkAdapter.executePluginHook({
|
|
@@ -9760,9 +9777,9 @@ async function processAgent(agent, agentStates) {
|
|
|
9760
9777
|
} else if (result.timedOut) {
|
|
9761
9778
|
log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
|
|
9762
9779
|
} else {
|
|
9763
|
-
const stderrHash =
|
|
9780
|
+
const stderrHash = createHash9("sha256").update(result.stderr).digest("hex").slice(0, 12);
|
|
9764
9781
|
const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
|
|
9765
|
-
const missingCmdHash = missingCmd ?
|
|
9782
|
+
const missingCmdHash = missingCmd ? createHash9("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
|
|
9766
9783
|
log(
|
|
9767
9784
|
`Integration hook on_install '${slug}' exited ${result.exitCode} for '${agent.code_name}' ` + (missingCmdHash ? `[missing_command_hash=${missingCmdHash}] ` : "") + `[stderr_hash=${stderrHash} stderr_len=${result.stderr.length}]`
|
|
9768
9785
|
);
|
|
@@ -9896,8 +9913,8 @@ async function processAgent(agent, agentStates) {
|
|
|
9896
9913
|
const sess = getSessionState(agent.code_name);
|
|
9897
9914
|
let mcpJsonParsed = null;
|
|
9898
9915
|
try {
|
|
9899
|
-
const mcpPath =
|
|
9900
|
-
mcpJsonParsed = JSON.parse(
|
|
9916
|
+
const mcpPath = join16(getProjectDir(agent.code_name), ".mcp.json");
|
|
9917
|
+
mcpJsonParsed = JSON.parse(readFileSync14(mcpPath, "utf-8"));
|
|
9901
9918
|
} catch {
|
|
9902
9919
|
}
|
|
9903
9920
|
reapMissingMcpSessions({
|
|
@@ -10031,10 +10048,10 @@ async function processAgent(agent, agentStates) {
|
|
|
10031
10048
|
} else if (agentFw === "claude-code" && tasks.length > 0) {
|
|
10032
10049
|
await syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData);
|
|
10033
10050
|
} else if (frameworkAdapter.syncScheduledTasks && gatewayRunning && gatewayPort) {
|
|
10034
|
-
const stableTasksHash =
|
|
10035
|
-
const boardHash = boardItems.length > 0 ?
|
|
10051
|
+
const stableTasksHash = createHash8("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
10052
|
+
const boardHash = boardItems.length > 0 ? createHash8("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
|
|
10036
10053
|
const resolvedModels = resolveModelChain(refreshData);
|
|
10037
|
-
const modelsHash =
|
|
10054
|
+
const modelsHash = createHash8("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
10038
10055
|
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
10039
10056
|
const prevTasksHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
10040
10057
|
if (combinedHash !== prevTasksHash) {
|
|
@@ -10106,10 +10123,10 @@ async function processAgent(agent, agentStates) {
|
|
|
10106
10123
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
10107
10124
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
10108
10125
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
10109
|
-
const jobsPath =
|
|
10126
|
+
const jobsPath = join16(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
10110
10127
|
if (existsSync9(jobsPath)) {
|
|
10111
10128
|
try {
|
|
10112
|
-
const jobsData = JSON.parse(
|
|
10129
|
+
const jobsData = JSON.parse(readFileSync14(jobsPath, "utf-8"));
|
|
10113
10130
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
10114
10131
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
10115
10132
|
);
|
|
@@ -10246,7 +10263,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
10246
10263
|
if (trackedFiles.length > 0 && existsSync9(agentDir)) {
|
|
10247
10264
|
const hashes = /* @__PURE__ */ new Map();
|
|
10248
10265
|
for (const file of trackedFiles) {
|
|
10249
|
-
const h = hashFile(
|
|
10266
|
+
const h = hashFile(join16(agentDir, file));
|
|
10250
10267
|
if (h) hashes.set(file, h);
|
|
10251
10268
|
}
|
|
10252
10269
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -10280,19 +10297,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
10280
10297
|
lastCleanupAt.set(codeName, Date.now());
|
|
10281
10298
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
10282
10299
|
for (const agentDir of ["main", codeName]) {
|
|
10283
|
-
const sessionsDir =
|
|
10300
|
+
const sessionsDir = join16(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
10284
10301
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
10285
10302
|
}
|
|
10286
|
-
const cronRunsDir =
|
|
10303
|
+
const cronRunsDir = join16(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
10287
10304
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
10288
|
-
const cronJobsPath =
|
|
10305
|
+
const cronJobsPath = join16(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
10289
10306
|
clearStaleCronRunState(cronJobsPath);
|
|
10290
10307
|
}
|
|
10291
10308
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
10292
|
-
const indexPath =
|
|
10309
|
+
const indexPath = join16(sessionsDir, "sessions.json");
|
|
10293
10310
|
if (!existsSync9(indexPath)) return;
|
|
10294
10311
|
try {
|
|
10295
|
-
const raw =
|
|
10312
|
+
const raw = readFileSync14(indexPath, "utf-8");
|
|
10296
10313
|
const index = JSON.parse(raw);
|
|
10297
10314
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
10298
10315
|
key: k,
|
|
@@ -10305,7 +10322,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
10305
10322
|
for (const entry of toDelete) {
|
|
10306
10323
|
delete index[entry.key];
|
|
10307
10324
|
if (entry.sessionId) {
|
|
10308
|
-
const sessionFile =
|
|
10325
|
+
const sessionFile = join16(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
10309
10326
|
try {
|
|
10310
10327
|
if (existsSync9(sessionFile)) {
|
|
10311
10328
|
unlinkSync(sessionFile);
|
|
@@ -10327,7 +10344,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
10327
10344
|
delete index[parentKey];
|
|
10328
10345
|
if (parentSessionId) {
|
|
10329
10346
|
try {
|
|
10330
|
-
const f =
|
|
10347
|
+
const f = join16(sessionsDir, `${parentSessionId}.jsonl`);
|
|
10331
10348
|
if (existsSync9(f)) {
|
|
10332
10349
|
unlinkSync(f);
|
|
10333
10350
|
deletedFiles++;
|
|
@@ -10337,7 +10354,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
10337
10354
|
}
|
|
10338
10355
|
}
|
|
10339
10356
|
}
|
|
10340
|
-
|
|
10357
|
+
writeFileSync5(indexPath, JSON.stringify(index));
|
|
10341
10358
|
if (toDelete.length > 0) {
|
|
10342
10359
|
log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
|
|
10343
10360
|
}
|
|
@@ -10348,7 +10365,7 @@ var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
|
10348
10365
|
function clearStaleCronRunState(jobsPath) {
|
|
10349
10366
|
if (!existsSync9(jobsPath)) return;
|
|
10350
10367
|
try {
|
|
10351
|
-
const raw =
|
|
10368
|
+
const raw = readFileSync14(jobsPath, "utf-8");
|
|
10352
10369
|
const data = JSON.parse(raw);
|
|
10353
10370
|
const jobs = data.jobs ?? data;
|
|
10354
10371
|
if (!Array.isArray(jobs)) return;
|
|
@@ -10373,7 +10390,7 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
10373
10390
|
}
|
|
10374
10391
|
}
|
|
10375
10392
|
if (changed) {
|
|
10376
|
-
|
|
10393
|
+
writeFileSync5(jobsPath, JSON.stringify(data, null, 2));
|
|
10377
10394
|
}
|
|
10378
10395
|
} catch {
|
|
10379
10396
|
}
|
|
@@ -10385,7 +10402,7 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
|
10385
10402
|
try {
|
|
10386
10403
|
for (const f of readdirSync5(dir)) {
|
|
10387
10404
|
if (!f.endsWith(ext)) continue;
|
|
10388
|
-
const fullPath =
|
|
10405
|
+
const fullPath = join16(dir, f);
|
|
10389
10406
|
try {
|
|
10390
10407
|
const st = statSync4(fullPath);
|
|
10391
10408
|
if (st.mtimeMs < cutoff) {
|
|
@@ -10407,8 +10424,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
|
|
|
10407
10424
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
10408
10425
|
const codeName = agent.code_name;
|
|
10409
10426
|
const projectDir = getProjectDir(codeName);
|
|
10410
|
-
const mcpConfigPath =
|
|
10411
|
-
const claudeMdPath =
|
|
10427
|
+
const mcpConfigPath = join16(projectDir, ".mcp.json");
|
|
10428
|
+
const claudeMdPath = join16(projectDir, "CLAUDE.md");
|
|
10412
10429
|
if (restartBreaker.isTripped(codeName)) {
|
|
10413
10430
|
const trip = restartBreaker.getTrip(codeName);
|
|
10414
10431
|
return {
|
|
@@ -10525,7 +10542,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
10525
10542
|
const ctx = getLastFailureContext(codeName);
|
|
10526
10543
|
const recovery = prepareForRespawn(codeName);
|
|
10527
10544
|
const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
|
|
10528
|
-
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${
|
|
10545
|
+
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash8("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
|
|
10529
10546
|
const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
|
|
10530
10547
|
const recoverySummary = recovery ? `; recovery=${recovery}` : "";
|
|
10531
10548
|
log(
|
|
@@ -10539,7 +10556,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash6("sha256").
|
|
|
10539
10556
|
);
|
|
10540
10557
|
getHostId().then((hostId) => {
|
|
10541
10558
|
if (!hostId) return;
|
|
10542
|
-
const paneTailHash = zombie.paneTail ? `sha256:${
|
|
10559
|
+
const paneTailHash = zombie.paneTail ? `sha256:${createHash8("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
|
|
10543
10560
|
return api.post("/host/events", {
|
|
10544
10561
|
host_id: hostId,
|
|
10545
10562
|
agent_code_name: codeName,
|
|
@@ -10618,7 +10635,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash6("sha256").
|
|
|
10618
10635
|
if (!claudeAuthTupleBySession.has(codeName)) {
|
|
10619
10636
|
claudeAuthTupleBySession.set(codeName, currentAuthTuple);
|
|
10620
10637
|
}
|
|
10621
|
-
const stableTasksHash =
|
|
10638
|
+
const stableTasksHash = createHash8("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
10622
10639
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
10623
10640
|
if (stableTasksHash !== prevHash) {
|
|
10624
10641
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
@@ -11007,9 +11024,9 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
11007
11024
|
if (isSessionHealthy(agent.codeName)) {
|
|
11008
11025
|
if (hostFlagStore().getBoolean("direct-chat-doorbell")) {
|
|
11009
11026
|
try {
|
|
11010
|
-
const doorbell = directChatDoorbellPath(agent.agentId,
|
|
11011
|
-
|
|
11012
|
-
|
|
11027
|
+
const doorbell = directChatDoorbellPath(agent.agentId, homedir9());
|
|
11028
|
+
mkdirSync5(dirname4(doorbell), { recursive: true });
|
|
11029
|
+
writeFileSync5(doorbell, String(Date.now()));
|
|
11013
11030
|
log(`[direct-chat] Doorbell rung for '${agent.codeName}' (msg=${msg.id}) \u2014 in-session MCP will pull via the cursor`);
|
|
11014
11031
|
return;
|
|
11015
11032
|
} catch (err) {
|
|
@@ -11053,11 +11070,11 @@ ${escapeXml(msg.content)}
|
|
|
11053
11070
|
log(`[direct-chat] One-shot spawn for '${agent.codeName}' (msg=${msg.id}; host in-flight=${directChatSpawnGate.hostInFlight}, queued=${directChatSpawnGate.queuedCount})`);
|
|
11054
11071
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
|
|
11055
11072
|
const projDir = ccProjectDir(agent.codeName);
|
|
11056
|
-
const mcpConfigPath =
|
|
11073
|
+
const mcpConfigPath = join16(projDir, ".mcp.json");
|
|
11057
11074
|
const serverNames = [];
|
|
11058
11075
|
if (existsSync9(mcpConfigPath)) {
|
|
11059
11076
|
try {
|
|
11060
|
-
const d = JSON.parse(
|
|
11077
|
+
const d = JSON.parse(readFileSync14(mcpConfigPath, "utf-8"));
|
|
11061
11078
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
11062
11079
|
} catch {
|
|
11063
11080
|
}
|
|
@@ -11076,15 +11093,15 @@ ${escapeXml(msg.content)}
|
|
|
11076
11093
|
"--allowedTools",
|
|
11077
11094
|
allowedTools
|
|
11078
11095
|
];
|
|
11079
|
-
const chatClaudeMd =
|
|
11096
|
+
const chatClaudeMd = join16(projDir, "CLAUDE.md");
|
|
11080
11097
|
if (existsSync9(chatClaudeMd)) {
|
|
11081
11098
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
11082
11099
|
}
|
|
11083
|
-
const envIntPath =
|
|
11100
|
+
const envIntPath = join16(projDir, ".env.integrations");
|
|
11084
11101
|
const childEnv = { ...process.env };
|
|
11085
11102
|
if (existsSync9(envIntPath)) {
|
|
11086
11103
|
try {
|
|
11087
|
-
Object.assign(childEnv, parseEnvIntegrations(
|
|
11104
|
+
Object.assign(childEnv, parseEnvIntegrations(readFileSync14(envIntPath, "utf-8")));
|
|
11088
11105
|
} catch {
|
|
11089
11106
|
}
|
|
11090
11107
|
}
|
|
@@ -11144,7 +11161,7 @@ ${escapeXml(msg.content)}
|
|
|
11144
11161
|
log(`[direct-chat] Reply sent for '${agent.codeName}'`);
|
|
11145
11162
|
} catch (err) {
|
|
11146
11163
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
11147
|
-
const errorId =
|
|
11164
|
+
const errorId = createHash8("sha256").update(errMsg).digest("hex").slice(0, 12);
|
|
11148
11165
|
log(`[direct-chat] Failed to process message for '${agent.codeName}': error_id=${errorId} error=${errMsg.slice(0, 500)}`);
|
|
11149
11166
|
try {
|
|
11150
11167
|
await api.post("/host/direct-chat/reply", {
|
|
@@ -11680,8 +11697,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
11680
11697
|
};
|
|
11681
11698
|
}
|
|
11682
11699
|
async function syncMemories(agent, configDir, log2) {
|
|
11683
|
-
const projectDir =
|
|
11684
|
-
const memoryDir =
|
|
11700
|
+
const projectDir = join16(configDir, agent.code_name, "project");
|
|
11701
|
+
const memoryDir = join16(projectDir, "memory");
|
|
11685
11702
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
11686
11703
|
if (isFreshSync) {
|
|
11687
11704
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -11699,8 +11716,8 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
11699
11716
|
for (const file of readdirSync5(memoryDir)) {
|
|
11700
11717
|
if (!file.endsWith(".md")) continue;
|
|
11701
11718
|
try {
|
|
11702
|
-
const raw =
|
|
11703
|
-
const fileHash =
|
|
11719
|
+
const raw = readFileSync14(join16(memoryDir, file), "utf-8");
|
|
11720
|
+
const fileHash = createHash8("sha256").update(raw).digest("hex").slice(0, 16);
|
|
11704
11721
|
currentHashes.set(file, fileHash);
|
|
11705
11722
|
if (prevHashes.get(file) === fileHash) continue;
|
|
11706
11723
|
const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
|
|
@@ -11724,7 +11741,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
11724
11741
|
} catch (err) {
|
|
11725
11742
|
for (const mem of changedMemories) {
|
|
11726
11743
|
for (const [file] of currentHashes) {
|
|
11727
|
-
const parsed = parseMemoryFile(
|
|
11744
|
+
const parsed = parseMemoryFile(readFileSync14(join16(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
11728
11745
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
11729
11746
|
}
|
|
11730
11747
|
}
|
|
@@ -11738,28 +11755,28 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
11738
11755
|
}
|
|
11739
11756
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
11740
11757
|
const localFiles = existsSync9(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
11741
|
-
const localListHash =
|
|
11758
|
+
const localListHash = createHash8("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
11742
11759
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
11743
11760
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
11744
11761
|
try {
|
|
11745
11762
|
const dbMemories = await api.post("/host/memories", {
|
|
11746
11763
|
agent_id: agent.agent_id
|
|
11747
11764
|
});
|
|
11748
|
-
const responseHash =
|
|
11765
|
+
const responseHash = createHash8("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
|
|
11749
11766
|
if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
|
|
11750
11767
|
return true;
|
|
11751
11768
|
}
|
|
11752
11769
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
11753
11770
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
11754
11771
|
if (dbMemories.memories?.length) {
|
|
11755
|
-
|
|
11772
|
+
mkdirSync5(memoryDir, { recursive: true });
|
|
11756
11773
|
let written = 0;
|
|
11757
11774
|
let overwritten = 0;
|
|
11758
11775
|
for (let i = 0; i < dbMemories.memories.length; i++) {
|
|
11759
11776
|
const mem = dbMemories.memories[i];
|
|
11760
11777
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
11761
11778
|
const slug = rawSlug || `memory-${i}`;
|
|
11762
|
-
const filePath =
|
|
11779
|
+
const filePath = join16(memoryDir, `${slug}.md`);
|
|
11763
11780
|
const desired = `---
|
|
11764
11781
|
name: ${JSON.stringify(mem.name)}
|
|
11765
11782
|
type: ${mem.type}
|
|
@@ -11771,20 +11788,20 @@ ${mem.content}
|
|
|
11771
11788
|
if (existsSync9(filePath)) {
|
|
11772
11789
|
let existing = "";
|
|
11773
11790
|
try {
|
|
11774
|
-
existing =
|
|
11791
|
+
existing = readFileSync14(filePath, "utf-8");
|
|
11775
11792
|
} catch {
|
|
11776
11793
|
}
|
|
11777
11794
|
if (existing === desired) continue;
|
|
11778
|
-
|
|
11795
|
+
writeFileSync5(filePath, desired);
|
|
11779
11796
|
overwritten++;
|
|
11780
11797
|
} else {
|
|
11781
|
-
|
|
11798
|
+
writeFileSync5(filePath, desired);
|
|
11782
11799
|
written++;
|
|
11783
11800
|
}
|
|
11784
11801
|
}
|
|
11785
11802
|
if (written > 0 || overwritten > 0) {
|
|
11786
11803
|
const updatedFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
11787
|
-
lastLocalFileHash.set(agent.agent_id,
|
|
11804
|
+
lastLocalFileHash.set(agent.agent_id, createHash8("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
11788
11805
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
11789
11806
|
}
|
|
11790
11807
|
}
|
|
@@ -12046,7 +12063,7 @@ function startManager(opts) {
|
|
|
12046
12063
|
try {
|
|
12047
12064
|
const stateFile = getStateFile();
|
|
12048
12065
|
if (existsSync9(stateFile)) {
|
|
12049
|
-
const raw =
|
|
12066
|
+
const raw = readFileSync14(stateFile, "utf-8");
|
|
12050
12067
|
const parsed = JSON.parse(raw);
|
|
12051
12068
|
if (Array.isArray(parsed.agents)) {
|
|
12052
12069
|
state6.agents = parsed.agents;
|
|
@@ -12067,7 +12084,7 @@ function startManager(opts) {
|
|
|
12067
12084
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
12068
12085
|
}
|
|
12069
12086
|
log(
|
|
12070
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
12087
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join16(homedir9(), ".augmented", "manager.log")}`
|
|
12071
12088
|
);
|
|
12072
12089
|
deployMcpAssets();
|
|
12073
12090
|
reapOrphanChannelMcps({ log });
|
|
@@ -12088,7 +12105,7 @@ async function reapOrphanedClaudePids() {
|
|
|
12088
12105
|
const looksLikeClaude = (pid) => {
|
|
12089
12106
|
if (process.platform !== "linux") return true;
|
|
12090
12107
|
try {
|
|
12091
|
-
const comm =
|
|
12108
|
+
const comm = readFileSync14(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
12092
12109
|
return comm.includes("claude");
|
|
12093
12110
|
} catch {
|
|
12094
12111
|
return false;
|
|
@@ -12198,18 +12215,18 @@ function restartRunningChannelMcps(basenames) {
|
|
|
12198
12215
|
}
|
|
12199
12216
|
}
|
|
12200
12217
|
function deployMcpAssets() {
|
|
12201
|
-
const targetDir =
|
|
12202
|
-
|
|
12203
|
-
const moduleDir =
|
|
12218
|
+
const targetDir = join16(homedir9(), ".augmented", "_mcp");
|
|
12219
|
+
mkdirSync5(targetDir, { recursive: true });
|
|
12220
|
+
const moduleDir = dirname4(fileURLToPath(import.meta.url));
|
|
12204
12221
|
let mcpSourceDir = "";
|
|
12205
12222
|
let dir = moduleDir;
|
|
12206
12223
|
for (let i = 0; i < 6; i++) {
|
|
12207
|
-
const candidate =
|
|
12208
|
-
if (existsSync9(
|
|
12224
|
+
const candidate = join16(dir, "dist", "mcp");
|
|
12225
|
+
if (existsSync9(join16(candidate, "index.js"))) {
|
|
12209
12226
|
mcpSourceDir = candidate;
|
|
12210
12227
|
break;
|
|
12211
12228
|
}
|
|
12212
|
-
const parent =
|
|
12229
|
+
const parent = dirname4(dir);
|
|
12213
12230
|
if (parent === dir) break;
|
|
12214
12231
|
dir = parent;
|
|
12215
12232
|
}
|
|
@@ -12221,7 +12238,7 @@ function deployMcpAssets() {
|
|
|
12221
12238
|
const fileHash = (p) => {
|
|
12222
12239
|
try {
|
|
12223
12240
|
if (!existsSync9(p)) return null;
|
|
12224
|
-
return
|
|
12241
|
+
return createHash8("sha256").update(readFileSync14(p)).digest("hex");
|
|
12225
12242
|
} catch {
|
|
12226
12243
|
return null;
|
|
12227
12244
|
}
|
|
@@ -12248,8 +12265,8 @@ function deployMcpAssets() {
|
|
|
12248
12265
|
// natural session restart.
|
|
12249
12266
|
"augmented-admin.js"
|
|
12250
12267
|
]) {
|
|
12251
|
-
const src =
|
|
12252
|
-
const dst =
|
|
12268
|
+
const src = join16(mcpSourceDir, file);
|
|
12269
|
+
const dst = join16(targetDir, file);
|
|
12253
12270
|
if (!existsSync9(src)) continue;
|
|
12254
12271
|
const before = fileHash(dst);
|
|
12255
12272
|
try {
|
|
@@ -12267,23 +12284,23 @@ function deployMcpAssets() {
|
|
|
12267
12284
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
12268
12285
|
restartRunningChannelMcps(changedBasenames);
|
|
12269
12286
|
}
|
|
12270
|
-
const localMcpPath =
|
|
12287
|
+
const localMcpPath = join16(targetDir, "index.js");
|
|
12271
12288
|
try {
|
|
12272
|
-
const agentsDir =
|
|
12289
|
+
const agentsDir = join16(homedir9(), ".augmented", "agents");
|
|
12273
12290
|
if (existsSync9(agentsDir)) {
|
|
12274
12291
|
for (const entry of readdirSync5(agentsDir, { withFileTypes: true })) {
|
|
12275
12292
|
if (!entry.isDirectory()) continue;
|
|
12276
12293
|
for (const subdir of ["provision", "project"]) {
|
|
12277
|
-
const mcpJsonPath =
|
|
12294
|
+
const mcpJsonPath = join16(agentsDir, entry.name, subdir, ".mcp.json");
|
|
12278
12295
|
try {
|
|
12279
|
-
const raw =
|
|
12296
|
+
const raw = readFileSync14(mcpJsonPath, "utf-8");
|
|
12280
12297
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
12281
12298
|
const mcpConfig = JSON.parse(raw);
|
|
12282
12299
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|
|
12283
12300
|
if (!augServer) continue;
|
|
12284
12301
|
augServer.command = "node";
|
|
12285
12302
|
augServer.args = [localMcpPath];
|
|
12286
|
-
|
|
12303
|
+
writeFileSync5(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
|
|
12287
12304
|
log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
|
|
12288
12305
|
} catch {
|
|
12289
12306
|
}
|