@integrity-labs/agt-cli 0.27.84 → 0.27.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agt.js +4 -4
- package/dist/{chunk-S2QLE5BQ.js → chunk-4LHN3FAL.js} +2 -2
- package/dist/{chunk-TXE2LLKI.js → chunk-5IWPCN3V.js} +9 -1
- package/dist/chunk-5IWPCN3V.js.map +1 -0
- package/dist/{chunk-WISOJYIV.js → chunk-SXE77YAJ.js} +2 -2
- package/dist/{claude-pair-runtime-MKK7LSQG.js → claude-pair-runtime-DY2LN3ED.js} +2 -2
- package/dist/lib/manager-worker.js +571 -229
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/teams-channel.js +86 -0
- package/dist/{persistent-session-BTXWOKJ6.js → persistent-session-YNVCVUK3.js} +3 -3
- package/dist/{responsiveness-probe-Y6TCM6T4.js → responsiveness-probe-5ICEBNCM.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-TXE2LLKI.js.map +0 -1
- /package/dist/{chunk-S2QLE5BQ.js.map → chunk-4LHN3FAL.js.map} +0 -0
- /package/dist/{chunk-WISOJYIV.js.map → chunk-SXE77YAJ.js.map} +0 -0
- /package/dist/{claude-pair-runtime-MKK7LSQG.js.map → claude-pair-runtime-DY2LN3ED.js.map} +0 -0
- /package/dist/{persistent-session-BTXWOKJ6.js.map → persistent-session-YNVCVUK3.js.map} +0 -0
- /package/dist/{responsiveness-probe-Y6TCM6T4.js.map → responsiveness-probe-5ICEBNCM.js.map} +0 -0
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
provisionStopHook,
|
|
17
17
|
requireHost,
|
|
18
18
|
safeWriteJsonAtomic
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-SXE77YAJ.js";
|
|
20
20
|
import {
|
|
21
21
|
getProjectDir as getProjectDir2,
|
|
22
22
|
getReadyTasks,
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
stopPersistentSession,
|
|
54
54
|
takeWatchdogGiveUpCount,
|
|
55
55
|
takeZombieDetection
|
|
56
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-4LHN3FAL.js";
|
|
57
57
|
import {
|
|
58
58
|
KANBAN_CHECK_COMMAND,
|
|
59
59
|
appendDmFooter,
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
resolveConnectivityProbe,
|
|
77
77
|
resolveDmTarget,
|
|
78
78
|
wrapScheduledTaskPrompt
|
|
79
|
-
} from "../chunk-
|
|
79
|
+
} from "../chunk-5IWPCN3V.js";
|
|
80
80
|
import {
|
|
81
81
|
parsePsRows,
|
|
82
82
|
reapOrphanChannelMcps
|
|
@@ -84,10 +84,10 @@ import {
|
|
|
84
84
|
|
|
85
85
|
// src/lib/manager-worker.ts
|
|
86
86
|
import { createHash as createHash3 } from "crypto";
|
|
87
|
-
import { readFileSync as
|
|
87
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, appendFileSync, mkdirSync as mkdirSync4, chmodSync, existsSync as existsSync5, rmSync as rmSync2, readdirSync as readdirSync4, statSync as statSync3, unlinkSync, copyFileSync } from "fs";
|
|
88
88
|
import https from "https";
|
|
89
89
|
import { execFileSync as syncExecFile } from "child_process";
|
|
90
|
-
import { join as
|
|
90
|
+
import { join as join8, dirname as dirname3 } from "path";
|
|
91
91
|
import { homedir as homedir4 } from "os";
|
|
92
92
|
import { fileURLToPath } from "url";
|
|
93
93
|
|
|
@@ -473,14 +473,14 @@ function reapMissingMcpSessions(args) {
|
|
|
473
473
|
if (!missingRaw.includes(key)) liveKeys.add(key);
|
|
474
474
|
}
|
|
475
475
|
for (const key of liveKeys) {
|
|
476
|
-
const
|
|
477
|
-
if (
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
476
|
+
const state6 = presenceReaperState.get(stateKey(codeName, key));
|
|
477
|
+
if (state6) {
|
|
478
|
+
state6.attempts = 0;
|
|
479
|
+
state6.lastSeenLiveAt = now();
|
|
480
|
+
state6.lastAttemptedSessionStartedAt = null;
|
|
481
|
+
state6.gaveUpLogged = false;
|
|
482
|
+
state6.firstMissingAt = null;
|
|
483
|
+
state6.quarantineLogged = false;
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
if (missing.length === 0) {
|
|
@@ -498,7 +498,7 @@ function reapMissingMcpSessions(args) {
|
|
|
498
498
|
const wouldQuarantine = [];
|
|
499
499
|
for (const key of missing) {
|
|
500
500
|
const sk = stateKey(codeName, key);
|
|
501
|
-
const
|
|
501
|
+
const state6 = presenceReaperState.get(sk) ?? {
|
|
502
502
|
attempts: 0,
|
|
503
503
|
lastSeenLiveAt: null,
|
|
504
504
|
lastAttemptedSessionStartedAt: null,
|
|
@@ -506,19 +506,19 @@ function reapMissingMcpSessions(args) {
|
|
|
506
506
|
firstMissingAt: null,
|
|
507
507
|
quarantineLogged: false
|
|
508
508
|
};
|
|
509
|
-
if (
|
|
510
|
-
if (
|
|
511
|
-
|
|
512
|
-
|
|
509
|
+
if (state6.firstMissingAt === null) state6.firstMissingAt = nowMs;
|
|
510
|
+
if (state6.lastAttemptedSessionStartedAt !== sessionStartedAt) {
|
|
511
|
+
state6.attempts += 1;
|
|
512
|
+
state6.lastAttemptedSessionStartedAt = sessionStartedAt;
|
|
513
513
|
}
|
|
514
|
-
presenceReaperState.set(sk,
|
|
515
|
-
if (
|
|
514
|
+
presenceReaperState.set(sk, state6);
|
|
515
|
+
if (state6.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
|
|
516
516
|
givenUp.push(key);
|
|
517
|
-
if (!
|
|
517
|
+
if (!state6.gaveUpLogged) {
|
|
518
518
|
log2(
|
|
519
519
|
`[mcp-presence-reaper] giving up on '${codeName}:${key}' after ${MAX_PRESENCE_RESTART_ATTEMPTS} consecutive failed restarts \u2014 declared but never recovers (likely orphaned config; see ENG-5279)`
|
|
520
520
|
);
|
|
521
|
-
|
|
521
|
+
state6.gaveUpLogged = true;
|
|
522
522
|
if (onGiveUp) {
|
|
523
523
|
try {
|
|
524
524
|
onGiveUp(codeName, key);
|
|
@@ -529,10 +529,10 @@ function reapMissingMcpSessions(args) {
|
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
531
|
}
|
|
532
|
-
if (quarantineMode !== "off" && !
|
|
533
|
-
const dwellElapsed = quarantineDwellMs <= 0 ||
|
|
532
|
+
if (quarantineMode !== "off" && !state6.quarantineLogged && classifyKey(key) === "optional") {
|
|
533
|
+
const dwellElapsed = quarantineDwellMs <= 0 || state6.firstMissingAt !== null && nowMs - state6.firstMissingAt >= quarantineDwellMs;
|
|
534
534
|
if (dwellElapsed) {
|
|
535
|
-
|
|
535
|
+
state6.quarantineLogged = true;
|
|
536
536
|
if (quarantineMode === "enforce") {
|
|
537
537
|
quarantined.push(key);
|
|
538
538
|
log2(
|
|
@@ -1647,14 +1647,272 @@ async function maybeReportTokenUsage(args) {
|
|
|
1647
1647
|
state2.set(codeName, next);
|
|
1648
1648
|
}
|
|
1649
1649
|
|
|
1650
|
+
// src/lib/conversation-evaluator.ts
|
|
1651
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
1652
|
+
import { join as join3 } from "path";
|
|
1653
|
+
var MIN_CHECK_INTERVAL_MS3 = 5 * 6e4;
|
|
1654
|
+
var TRANSCRIPT_MTIME_WINDOW_MS2 = 3 * 24 * 60 * 60 * 1e3;
|
|
1655
|
+
var WINDOW_PAD_MS = 5 * 6e4;
|
|
1656
|
+
var MAX_TURN_CHARS = 1500;
|
|
1657
|
+
var MAX_TRANSCRIPT_CHARS = 6e3;
|
|
1658
|
+
var DEFAULT_CLAUDE_EVAL_MODEL = "claude-haiku-4-5-20251001";
|
|
1659
|
+
var DEFAULT_LOCAL_EVAL_URL = "http://localhost:11434/v1/chat/completions";
|
|
1660
|
+
var DEFAULT_LOCAL_EVAL_MODEL = "gemma4:12b";
|
|
1661
|
+
var EVAL_TIMEOUT_MS = 12e4;
|
|
1662
|
+
async function runLocalEvalChat(prompt, opts) {
|
|
1663
|
+
const endpoint = new URL(opts.url);
|
|
1664
|
+
const hostname = endpoint.hostname.toLowerCase();
|
|
1665
|
+
const isLoopback = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
1666
|
+
if (!isLoopback) {
|
|
1667
|
+
throw new Error(`local eval url must be loopback-only, got host '${endpoint.hostname}'`);
|
|
1668
|
+
}
|
|
1669
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
1670
|
+
const res = await doFetch(opts.url, {
|
|
1671
|
+
method: "POST",
|
|
1672
|
+
headers: {
|
|
1673
|
+
"Content-Type": "application/json",
|
|
1674
|
+
...opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : {}
|
|
1675
|
+
},
|
|
1676
|
+
body: JSON.stringify({
|
|
1677
|
+
model: opts.model,
|
|
1678
|
+
messages: [{ role: "user", content: prompt }],
|
|
1679
|
+
temperature: 0,
|
|
1680
|
+
max_tokens: 512,
|
|
1681
|
+
stream: false
|
|
1682
|
+
}),
|
|
1683
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? EVAL_TIMEOUT_MS)
|
|
1684
|
+
});
|
|
1685
|
+
if (!res.ok) {
|
|
1686
|
+
throw new Error(`local eval endpoint ${opts.url} returned ${res.status}`);
|
|
1687
|
+
}
|
|
1688
|
+
const data = await res.json();
|
|
1689
|
+
const text = data.choices?.[0]?.message?.content;
|
|
1690
|
+
if (typeof text !== "string" || !text.trim()) {
|
|
1691
|
+
throw new Error("local eval endpoint returned no message content");
|
|
1692
|
+
}
|
|
1693
|
+
return text;
|
|
1694
|
+
}
|
|
1695
|
+
var state3 = /* @__PURE__ */ new Map();
|
|
1696
|
+
function channelRefTokens(channelRef) {
|
|
1697
|
+
return channelRef.split(":").slice(1).filter((p) => p && p !== "dm");
|
|
1698
|
+
}
|
|
1699
|
+
function contentToText(content) {
|
|
1700
|
+
if (typeof content === "string") return content;
|
|
1701
|
+
if (Array.isArray(content)) {
|
|
1702
|
+
return content.map((b) => {
|
|
1703
|
+
if (typeof b === "string") return b;
|
|
1704
|
+
if (b && typeof b === "object" && b.type === "text") {
|
|
1705
|
+
return String(b.text ?? "");
|
|
1706
|
+
}
|
|
1707
|
+
return "";
|
|
1708
|
+
}).filter(Boolean).join(" ");
|
|
1709
|
+
}
|
|
1710
|
+
return "";
|
|
1711
|
+
}
|
|
1712
|
+
function parseTranscriptTurns(jsonl) {
|
|
1713
|
+
const turns = [];
|
|
1714
|
+
for (const line of jsonl.split("\n")) {
|
|
1715
|
+
const trimmed = line.trim();
|
|
1716
|
+
if (!trimmed) continue;
|
|
1717
|
+
let obj;
|
|
1718
|
+
try {
|
|
1719
|
+
obj = JSON.parse(trimmed);
|
|
1720
|
+
} catch {
|
|
1721
|
+
continue;
|
|
1722
|
+
}
|
|
1723
|
+
const role = obj.type === "user" || obj.type === "assistant" ? obj.type : void 0;
|
|
1724
|
+
if (!role) continue;
|
|
1725
|
+
const text = contentToText(obj.message?.content).trim();
|
|
1726
|
+
if (!text) continue;
|
|
1727
|
+
const ts = obj.timestamp ? Date.parse(obj.timestamp) : NaN;
|
|
1728
|
+
turns.push({ role, text, ts: Number.isFinite(ts) ? ts : 0 });
|
|
1729
|
+
}
|
|
1730
|
+
return turns;
|
|
1731
|
+
}
|
|
1732
|
+
function stripChannelTag(text) {
|
|
1733
|
+
return text.replace(/<channel\b[^>]*\/?>/i, "").replace(/<\/channel>/i, "").trim();
|
|
1734
|
+
}
|
|
1735
|
+
function reconstructConversation(allTurns, tokens, windowStartMs, windowEndMs) {
|
|
1736
|
+
if (tokens.length === 0) return [];
|
|
1737
|
+
const sorted = [...allTurns].sort((a, b) => a.ts - b.ts);
|
|
1738
|
+
const out = [];
|
|
1739
|
+
let inConversation = false;
|
|
1740
|
+
for (const turn of sorted) {
|
|
1741
|
+
if (turn.ts && (turn.ts < windowStartMs || turn.ts > windowEndMs)) continue;
|
|
1742
|
+
if (turn.role === "user") {
|
|
1743
|
+
inConversation = tokens.every((t) => turn.text.includes(t));
|
|
1744
|
+
if (inConversation) {
|
|
1745
|
+
out.push({ ...turn, text: stripChannelTag(turn.text) });
|
|
1746
|
+
}
|
|
1747
|
+
} else if (inConversation) {
|
|
1748
|
+
out.push(turn);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
return out;
|
|
1752
|
+
}
|
|
1753
|
+
function renderTranscript(turns) {
|
|
1754
|
+
const lines = [];
|
|
1755
|
+
let total = 0;
|
|
1756
|
+
for (const turn of turns) {
|
|
1757
|
+
const label = turn.role === "user" ? "User" : "Agent";
|
|
1758
|
+
const body = turn.text.length > MAX_TURN_CHARS ? `${turn.text.slice(0, MAX_TURN_CHARS)}\u2026` : turn.text;
|
|
1759
|
+
const line = `${label}: ${body}`;
|
|
1760
|
+
if (total + line.length > MAX_TRANSCRIPT_CHARS) break;
|
|
1761
|
+
lines.push(line);
|
|
1762
|
+
total += line.length;
|
|
1763
|
+
}
|
|
1764
|
+
return lines.join("\n\n");
|
|
1765
|
+
}
|
|
1766
|
+
function buildEvalPrompt(channel, transcript) {
|
|
1767
|
+
return `You are a strict QA evaluator scoring whether an AI agent successfully helped an end-user in a single ${channel} conversation. Judge ONLY the agent's effectiveness for the user \u2014 not tone, length, or policy.
|
|
1768
|
+
|
|
1769
|
+
Conversation transcript:
|
|
1770
|
+
"""
|
|
1771
|
+
${transcript}
|
|
1772
|
+
"""
|
|
1773
|
+
|
|
1774
|
+
Score the agent's success:
|
|
1775
|
+
- score: integer 0-100 (0 = ignored/unhelpful/wrong, 100 = fully resolved the user's need)
|
|
1776
|
+
- verdict: "success" (need clearly met), "partial" (some help but incomplete/ambiguous), or "failure" (did not help / made it worse)
|
|
1777
|
+
- summary: ONE short sentence (max 140 chars) explaining the score. Do NOT quote sensitive user content.
|
|
1778
|
+
|
|
1779
|
+
Respond with ONLY a JSON object, no other text:
|
|
1780
|
+
{"score": 0-100, "verdict": "success|partial|failure", "summary": "..."}`;
|
|
1781
|
+
}
|
|
1782
|
+
function parseVerdict(raw) {
|
|
1783
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
1784
|
+
if (!match) return null;
|
|
1785
|
+
let obj;
|
|
1786
|
+
try {
|
|
1787
|
+
obj = JSON.parse(match[0]);
|
|
1788
|
+
} catch {
|
|
1789
|
+
return null;
|
|
1790
|
+
}
|
|
1791
|
+
const score = typeof obj.score === "number" ? obj.score : Number(obj.score);
|
|
1792
|
+
if (!Number.isFinite(score) || score < 0 || score > 100) return null;
|
|
1793
|
+
if (obj.verdict !== "success" && obj.verdict !== "partial" && obj.verdict !== "failure") return null;
|
|
1794
|
+
const summary = typeof obj.summary === "string" ? obj.summary.slice(0, 140) : "";
|
|
1795
|
+
return { score: Math.round(score), verdict: obj.verdict, summary };
|
|
1796
|
+
}
|
|
1797
|
+
async function maybeEvaluateConversations(args) {
|
|
1798
|
+
const { api: api2, backend, codeName, agentId, log: log2 } = args;
|
|
1799
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
1800
|
+
const nowMs = now.getTime();
|
|
1801
|
+
const existing = state3.get(codeName);
|
|
1802
|
+
if (existing && nowMs - existing.lastCheckedAt < MIN_CHECK_INTERVAL_MS3) {
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
state3.set(codeName, { lastCheckedAt: nowMs });
|
|
1806
|
+
let pending;
|
|
1807
|
+
try {
|
|
1808
|
+
const resp = await api2.get(
|
|
1809
|
+
`/host/conversations/pending-evaluation?agent_id=${encodeURIComponent(agentId)}`
|
|
1810
|
+
);
|
|
1811
|
+
pending = resp?.conversations ?? [];
|
|
1812
|
+
} catch (err) {
|
|
1813
|
+
log2(`[conversation-eval] ${codeName}: pending fetch failed: ${err.message}`);
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
if (pending.length === 0) return;
|
|
1817
|
+
const dir = args.transcriptDir ?? sessionTranscriptDir(getProjectDir(codeName));
|
|
1818
|
+
const allTurns = readRecentTurns(dir, nowMs);
|
|
1819
|
+
if (allTurns.length === 0) {
|
|
1820
|
+
for (const conv of pending) {
|
|
1821
|
+
await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
|
|
1822
|
+
}
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
for (const conv of pending) {
|
|
1826
|
+
const tokens = channelRefTokens(conv.channel_ref);
|
|
1827
|
+
const windowStart = Date.parse(conv.started_at) - WINDOW_PAD_MS;
|
|
1828
|
+
const windowEnd = Date.parse(conv.last_message_at) + WINDOW_PAD_MS;
|
|
1829
|
+
const turns = reconstructConversation(allTurns, tokens, windowStart, windowEnd);
|
|
1830
|
+
if (turns.length === 0) {
|
|
1831
|
+
await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
const transcript = renderTranscript(turns);
|
|
1835
|
+
if (!transcript.trim()) {
|
|
1836
|
+
await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
|
|
1837
|
+
continue;
|
|
1838
|
+
}
|
|
1839
|
+
let verdict;
|
|
1840
|
+
try {
|
|
1841
|
+
const out = await backend.run(buildEvalPrompt(conv.channel, transcript));
|
|
1842
|
+
verdict = parseVerdict(out);
|
|
1843
|
+
} catch (err) {
|
|
1844
|
+
log2(`[conversation-eval] ${codeName}: scoring failed: ${err.message}`);
|
|
1845
|
+
continue;
|
|
1846
|
+
}
|
|
1847
|
+
if (!verdict) {
|
|
1848
|
+
log2(`[conversation-eval] ${codeName}: unparseable verdict for ${conv.conversation_id.slice(0, 8)}`);
|
|
1849
|
+
continue;
|
|
1850
|
+
}
|
|
1851
|
+
try {
|
|
1852
|
+
await api2.post("/host/conversations/evaluation", {
|
|
1853
|
+
agent_id: agentId,
|
|
1854
|
+
conversation_id: conv.conversation_id,
|
|
1855
|
+
score: verdict.score,
|
|
1856
|
+
verdict: verdict.verdict,
|
|
1857
|
+
summary: verdict.summary,
|
|
1858
|
+
model: backend.model
|
|
1859
|
+
});
|
|
1860
|
+
log2(
|
|
1861
|
+
`[conversation-eval] ${codeName}: ${conv.conversation_id.slice(0, 8)} \u2192 ${verdict.verdict} (${verdict.score})`
|
|
1862
|
+
);
|
|
1863
|
+
} catch (err) {
|
|
1864
|
+
log2(`[conversation-eval] ${codeName}: report failed: ${err.message}`);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
async function reportSkip(api2, agentId, conversationId, log2, codeName) {
|
|
1869
|
+
try {
|
|
1870
|
+
await api2.post("/host/conversations/evaluation", {
|
|
1871
|
+
agent_id: agentId,
|
|
1872
|
+
conversation_id: conversationId,
|
|
1873
|
+
skipped: true
|
|
1874
|
+
});
|
|
1875
|
+
} catch (err) {
|
|
1876
|
+
log2(`[conversation-eval] ${codeName}: skip report failed: ${err.message}`);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
function readRecentTurns(dir, nowMs) {
|
|
1880
|
+
let entries;
|
|
1881
|
+
try {
|
|
1882
|
+
entries = readdirSync2(dir);
|
|
1883
|
+
} catch {
|
|
1884
|
+
return [];
|
|
1885
|
+
}
|
|
1886
|
+
const turns = [];
|
|
1887
|
+
for (const name of entries) {
|
|
1888
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
1889
|
+
const full = join3(dir, name);
|
|
1890
|
+
let mtimeMs;
|
|
1891
|
+
try {
|
|
1892
|
+
mtimeMs = statSync2(full).mtimeMs;
|
|
1893
|
+
} catch {
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1896
|
+
if (nowMs - mtimeMs > TRANSCRIPT_MTIME_WINDOW_MS2) continue;
|
|
1897
|
+
let content;
|
|
1898
|
+
try {
|
|
1899
|
+
content = readFileSync5(full, "utf8");
|
|
1900
|
+
} catch {
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
turns.push(...parseTranscriptTurns(content));
|
|
1904
|
+
}
|
|
1905
|
+
return turns;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1650
1908
|
// src/lib/activity-cache-monitor.ts
|
|
1651
|
-
import { existsSync as existsSync2, readFileSync as
|
|
1909
|
+
import { existsSync as existsSync2, readFileSync as readFileSync6 } from "fs";
|
|
1652
1910
|
import { homedir } from "os";
|
|
1653
|
-
import { join as
|
|
1654
|
-
var
|
|
1655
|
-
var STATS_CACHE_PATH =
|
|
1911
|
+
import { join as join4 } from "path";
|
|
1912
|
+
var MIN_CHECK_INTERVAL_MS4 = 6e4;
|
|
1913
|
+
var STATS_CACHE_PATH = join4(homedir(), ".claude", "stats-cache.json");
|
|
1656
1914
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
1657
|
-
var
|
|
1915
|
+
var state4 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
1658
1916
|
function selectNewDailyRows(raw, lastObservedDate) {
|
|
1659
1917
|
let parsed;
|
|
1660
1918
|
try {
|
|
@@ -1693,24 +1951,24 @@ async function maybeReportActivityCache(args) {
|
|
|
1693
1951
|
const { api: api2, log: log2 } = args;
|
|
1694
1952
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
1695
1953
|
const nowMs = now.getTime();
|
|
1696
|
-
if (nowMs -
|
|
1697
|
-
|
|
1954
|
+
if (nowMs - state4.lastCheckedAt < MIN_CHECK_INTERVAL_MS4) return;
|
|
1955
|
+
state4.lastCheckedAt = nowMs;
|
|
1698
1956
|
if (!existsSync2(STATS_CACHE_PATH)) {
|
|
1699
1957
|
return;
|
|
1700
1958
|
}
|
|
1701
1959
|
let raw;
|
|
1702
1960
|
try {
|
|
1703
|
-
raw =
|
|
1961
|
+
raw = readFileSync6(STATS_CACHE_PATH, "utf-8");
|
|
1704
1962
|
} catch (err) {
|
|
1705
1963
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
1706
1964
|
return;
|
|
1707
1965
|
}
|
|
1708
|
-
const rows = selectNewDailyRows(raw,
|
|
1966
|
+
const rows = selectNewDailyRows(raw, state4.lastObservedDate);
|
|
1709
1967
|
if (rows.length === 0) return;
|
|
1710
1968
|
for (const row of rows) {
|
|
1711
1969
|
try {
|
|
1712
1970
|
await api2.post("/host/activity-observations", row);
|
|
1713
|
-
|
|
1971
|
+
state4.lastObservedDate = row.date;
|
|
1714
1972
|
} catch (err) {
|
|
1715
1973
|
log2(
|
|
1716
1974
|
`[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
|
|
@@ -2150,7 +2408,7 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
2150
2408
|
// src/lib/claude-auth-detect.ts
|
|
2151
2409
|
import { readFile, readdir } from "fs/promises";
|
|
2152
2410
|
import { homedir as homedir2, platform } from "os";
|
|
2153
|
-
import { join as
|
|
2411
|
+
import { join as join5 } from "path";
|
|
2154
2412
|
import { execFile as execFile2 } from "child_process";
|
|
2155
2413
|
import { promisify } from "util";
|
|
2156
2414
|
var execFileAsync = promisify(execFile2);
|
|
@@ -2165,8 +2423,8 @@ async function detectClaudeAuth() {
|
|
|
2165
2423
|
}
|
|
2166
2424
|
async function findClaudeCredentialsPaths() {
|
|
2167
2425
|
const candidates = [
|
|
2168
|
-
|
|
2169
|
-
|
|
2426
|
+
join5(homedir2(), ".claude", ".credentials.json"),
|
|
2427
|
+
join5(homedir2(), ".claude", "credentials.json")
|
|
2170
2428
|
];
|
|
2171
2429
|
const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
|
|
2172
2430
|
if (isLinuxRoot) {
|
|
@@ -2174,8 +2432,8 @@ async function findClaudeCredentialsPaths() {
|
|
|
2174
2432
|
const entries = await readdir("/home", { withFileTypes: true });
|
|
2175
2433
|
for (const entry of entries) {
|
|
2176
2434
|
if (!entry.isDirectory()) continue;
|
|
2177
|
-
candidates.push(
|
|
2178
|
-
candidates.push(
|
|
2435
|
+
candidates.push(join5("/home", entry.name, ".claude", ".credentials.json"));
|
|
2436
|
+
candidates.push(join5("/home", entry.name, ".claude", "credentials.json"));
|
|
2179
2437
|
}
|
|
2180
2438
|
} catch {
|
|
2181
2439
|
}
|
|
@@ -2267,18 +2525,18 @@ function normalize(value) {
|
|
|
2267
2525
|
}
|
|
2268
2526
|
|
|
2269
2527
|
// src/lib/channel-hash-cache.ts
|
|
2270
|
-
import { existsSync as existsSync3, readFileSync as
|
|
2271
|
-
import { join as
|
|
2528
|
+
import { existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
|
|
2529
|
+
import { join as join6 } from "path";
|
|
2272
2530
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
2273
2531
|
function getChannelHashCacheFile(configDir) {
|
|
2274
|
-
return
|
|
2532
|
+
return join6(configDir, CACHE_FILENAME);
|
|
2275
2533
|
}
|
|
2276
2534
|
function loadChannelHashCache(target, configDir) {
|
|
2277
2535
|
const path = getChannelHashCacheFile(configDir);
|
|
2278
2536
|
if (!existsSync3(path)) return;
|
|
2279
2537
|
let parsed;
|
|
2280
2538
|
try {
|
|
2281
|
-
parsed = JSON.parse(
|
|
2539
|
+
parsed = JSON.parse(readFileSync7(path, "utf-8"));
|
|
2282
2540
|
} catch {
|
|
2283
2541
|
return;
|
|
2284
2542
|
}
|
|
@@ -2732,24 +2990,24 @@ function clearAgentState(agentId, codeName) {
|
|
|
2732
2990
|
}
|
|
2733
2991
|
|
|
2734
2992
|
// src/lib/restart-flags.ts
|
|
2735
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as
|
|
2993
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync8, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2736
2994
|
import { homedir as homedir3 } from "os";
|
|
2737
|
-
import { join as
|
|
2995
|
+
import { join as join7 } from "path";
|
|
2738
2996
|
import { randomUUID } from "crypto";
|
|
2739
2997
|
function restartFlagsDir() {
|
|
2740
|
-
return
|
|
2998
|
+
return join7(homedir3(), ".augmented", "restart-flags");
|
|
2741
2999
|
}
|
|
2742
3000
|
function flagPath(codeName) {
|
|
2743
|
-
return
|
|
3001
|
+
return join7(restartFlagsDir(), `${codeName}.flag`);
|
|
2744
3002
|
}
|
|
2745
3003
|
function readRestartFlags() {
|
|
2746
3004
|
const dir = restartFlagsDir();
|
|
2747
3005
|
if (!existsSync4(dir)) return [];
|
|
2748
3006
|
const out = [];
|
|
2749
|
-
for (const entry of
|
|
3007
|
+
for (const entry of readdirSync3(dir)) {
|
|
2750
3008
|
if (!entry.endsWith(".flag")) continue;
|
|
2751
3009
|
try {
|
|
2752
|
-
const raw =
|
|
3010
|
+
const raw = readFileSync8(join7(dir, entry), "utf8");
|
|
2753
3011
|
const parsed = JSON.parse(raw);
|
|
2754
3012
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
2755
3013
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -3296,8 +3554,8 @@ function applyRestartAcks(args) {
|
|
|
3296
3554
|
var GATEWAY_PORT_BASE = 18800;
|
|
3297
3555
|
var GATEWAY_PORT_STEP = 10;
|
|
3298
3556
|
var GATEWAY_PORT_MAX = 18899;
|
|
3299
|
-
var AUGMENTED_DIR =
|
|
3300
|
-
var GATEWAY_PORTS_FILE =
|
|
3557
|
+
var AUGMENTED_DIR = join8(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
3558
|
+
var GATEWAY_PORTS_FILE = join8(AUGMENTED_DIR, "gateway-ports.json");
|
|
3301
3559
|
var CHANNEL_SWEEP_INTERVAL_MS = (() => {
|
|
3302
3560
|
const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
|
|
3303
3561
|
if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
|
|
@@ -3404,8 +3662,8 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
|
|
|
3404
3662
|
function shouldSkipRevokedCleanup(previousKnownStatus) {
|
|
3405
3663
|
return previousKnownStatus === "revoked";
|
|
3406
3664
|
}
|
|
3407
|
-
function hasRevokedResiduals(
|
|
3408
|
-
return
|
|
3665
|
+
function hasRevokedResiduals(state6) {
|
|
3666
|
+
return state6.gatewayRunning || state6.portAllocated || state6.provisionDirExists;
|
|
3409
3667
|
}
|
|
3410
3668
|
var pendingSessionRestarts = /* @__PURE__ */ new Map();
|
|
3411
3669
|
var restartBreaker = new RestartBreaker();
|
|
@@ -3482,7 +3740,7 @@ function inboundAgeSecondsFor(codeName) {
|
|
|
3482
3740
|
}
|
|
3483
3741
|
function paneLogAgeSecondsFor(codeName) {
|
|
3484
3742
|
try {
|
|
3485
|
-
const mtimeMs =
|
|
3743
|
+
const mtimeMs = statSync3(paneLogPath(codeName)).mtimeMs;
|
|
3486
3744
|
return Math.max(0, Math.floor((Date.now() - mtimeMs) / 1e3));
|
|
3487
3745
|
} catch (err) {
|
|
3488
3746
|
if (err?.code === "ENOENT") return null;
|
|
@@ -3502,7 +3760,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
|
3502
3760
|
var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
3503
3761
|
function projectMcpHash(_codeName, projectDir) {
|
|
3504
3762
|
try {
|
|
3505
|
-
const raw =
|
|
3763
|
+
const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
|
|
3506
3764
|
return createHash3("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
3507
3765
|
} catch {
|
|
3508
3766
|
return null;
|
|
@@ -3510,7 +3768,7 @@ function projectMcpHash(_codeName, projectDir) {
|
|
|
3510
3768
|
}
|
|
3511
3769
|
function projectMcpKeys(_codeName, projectDir) {
|
|
3512
3770
|
try {
|
|
3513
|
-
const raw =
|
|
3771
|
+
const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
|
|
3514
3772
|
const parsed = JSON.parse(raw);
|
|
3515
3773
|
const servers = parsed.mcpServers;
|
|
3516
3774
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -3521,7 +3779,7 @@ function projectMcpKeys(_codeName, projectDir) {
|
|
|
3521
3779
|
}
|
|
3522
3780
|
function readMcpHttpServerConfig(projectDir, serverKey) {
|
|
3523
3781
|
try {
|
|
3524
|
-
const raw =
|
|
3782
|
+
const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
|
|
3525
3783
|
const servers = JSON.parse(raw).mcpServers ?? {};
|
|
3526
3784
|
const entry = servers[serverKey];
|
|
3527
3785
|
if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
|
|
@@ -3674,7 +3932,7 @@ var taskDisplayInfo = /* @__PURE__ */ new Map();
|
|
|
3674
3932
|
var agentChannelTokens = /* @__PURE__ */ new Map();
|
|
3675
3933
|
var activeChannels = /* @__PURE__ */ new Map();
|
|
3676
3934
|
var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
|
|
3677
|
-
var
|
|
3935
|
+
var state5 = {
|
|
3678
3936
|
pid: process.pid,
|
|
3679
3937
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3680
3938
|
lastPollAt: null,
|
|
@@ -3713,7 +3971,7 @@ var cachedMaintenanceWindow = null;
|
|
|
3713
3971
|
var lastVersionCheckAt = 0;
|
|
3714
3972
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3715
3973
|
var lastResponsivenessProbeAt = 0;
|
|
3716
|
-
var agtCliVersion = true ? "0.27.
|
|
3974
|
+
var agtCliVersion = true ? "0.27.86" : "dev";
|
|
3717
3975
|
function resolveBrewPath(execFileSync4) {
|
|
3718
3976
|
try {
|
|
3719
3977
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -3890,7 +4148,7 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
3890
4148
|
try {
|
|
3891
4149
|
let settings = {};
|
|
3892
4150
|
if (existsSync5(path)) {
|
|
3893
|
-
const raw =
|
|
4151
|
+
const raw = readFileSync9(path, "utf-8").trim();
|
|
3894
4152
|
if (raw) {
|
|
3895
4153
|
let parsed;
|
|
3896
4154
|
try {
|
|
@@ -3962,7 +4220,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
3962
4220
|
var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3963
4221
|
var claudeCodeUpgradeInFlight = false;
|
|
3964
4222
|
function claudeCodeUpgradeMarkerPath() {
|
|
3965
|
-
return
|
|
4223
|
+
return join8(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
|
|
3966
4224
|
}
|
|
3967
4225
|
function stampClaudeCodeUpgradeMarker() {
|
|
3968
4226
|
try {
|
|
@@ -3972,7 +4230,7 @@ function stampClaudeCodeUpgradeMarker() {
|
|
|
3972
4230
|
}
|
|
3973
4231
|
function claudeCodeUpgradeThrottled() {
|
|
3974
4232
|
try {
|
|
3975
|
-
const lastCheck = parseInt(
|
|
4233
|
+
const lastCheck = parseInt(readFileSync9(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
3976
4234
|
if (!Number.isFinite(lastCheck)) return false;
|
|
3977
4235
|
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
3978
4236
|
} catch {
|
|
@@ -4025,7 +4283,7 @@ ${r.stderr}`;
|
|
|
4025
4283
|
}
|
|
4026
4284
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
4027
4285
|
function selfUpdateAppliedMarkerPath() {
|
|
4028
|
-
return
|
|
4286
|
+
return join8(homedir4(), ".augmented", ".last-self-update-applied");
|
|
4029
4287
|
}
|
|
4030
4288
|
var selfUpdateUpToDateLogged = false;
|
|
4031
4289
|
var restartAfterUpgrade = false;
|
|
@@ -4048,7 +4306,7 @@ async function checkAndUpdateCli() {
|
|
|
4048
4306
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
4049
4307
|
if (!isBrewFormula && !isNpmGlobal) return;
|
|
4050
4308
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
4051
|
-
const markerPath =
|
|
4309
|
+
const markerPath = join8(homedir4(), ".augmented", ".last-update-check");
|
|
4052
4310
|
try {
|
|
4053
4311
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
4054
4312
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
@@ -4305,9 +4563,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
4305
4563
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
4306
4564
|
}
|
|
4307
4565
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
4308
|
-
const claudeDir =
|
|
4566
|
+
const claudeDir = join8(homedir4(), ".claude");
|
|
4309
4567
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
4310
|
-
const p =
|
|
4568
|
+
const p = join8(claudeDir, filename);
|
|
4311
4569
|
if (existsSync5(p)) {
|
|
4312
4570
|
try {
|
|
4313
4571
|
rmSync2(p, { force: true });
|
|
@@ -4320,9 +4578,86 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
4320
4578
|
delete childEnv.ANTHROPIC_API_KEY;
|
|
4321
4579
|
}
|
|
4322
4580
|
}
|
|
4581
|
+
var evalEmptyMcpConfigPath = null;
|
|
4582
|
+
function ensureEvalEmptyMcpConfig() {
|
|
4583
|
+
if (evalEmptyMcpConfigPath && existsSync5(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
|
|
4584
|
+
const dir = join8(homedir4(), ".augmented");
|
|
4585
|
+
try {
|
|
4586
|
+
mkdirSync4(dir, { recursive: true });
|
|
4587
|
+
} catch {
|
|
4588
|
+
}
|
|
4589
|
+
const p = join8(dir, ".eval-empty-mcp.json");
|
|
4590
|
+
writeFileSync4(p, JSON.stringify({ mcpServers: {} }));
|
|
4591
|
+
evalEmptyMcpConfigPath = p;
|
|
4592
|
+
return p;
|
|
4593
|
+
}
|
|
4594
|
+
async function runEvalClaude(prompt, model) {
|
|
4595
|
+
const childEnv = { ...process.env };
|
|
4596
|
+
await applyClaudeAuthToEnv(childEnv, "conversation-eval");
|
|
4597
|
+
const emptyMcp = ensureEvalEmptyMcpConfig();
|
|
4598
|
+
const args = [
|
|
4599
|
+
"-p",
|
|
4600
|
+
prompt,
|
|
4601
|
+
"--model",
|
|
4602
|
+
model,
|
|
4603
|
+
"--output-format",
|
|
4604
|
+
"text",
|
|
4605
|
+
// Isolation: only the empty MCP config, ignore project/user config + no tools.
|
|
4606
|
+
"--mcp-config",
|
|
4607
|
+
emptyMcp,
|
|
4608
|
+
"--strict-mcp-config",
|
|
4609
|
+
"--permission-mode",
|
|
4610
|
+
"auto",
|
|
4611
|
+
"--allowedTools",
|
|
4612
|
+
""
|
|
4613
|
+
];
|
|
4614
|
+
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
|
|
4615
|
+
cwd: homedir4(),
|
|
4616
|
+
timeout: 12e4,
|
|
4617
|
+
stdin: "ignore",
|
|
4618
|
+
env: childEnv,
|
|
4619
|
+
onSpawn: (pid) => registerClaudeSpawn({ pid, started_at: Date.now(), kind: "other" }),
|
|
4620
|
+
onExit: (pid) => unregisterClaudeSpawn(pid)
|
|
4621
|
+
});
|
|
4622
|
+
return stdout;
|
|
4623
|
+
}
|
|
4624
|
+
var conversationEvalBackend = null;
|
|
4625
|
+
function resolveConversationEvalBackend() {
|
|
4626
|
+
if (conversationEvalBackend) return conversationEvalBackend;
|
|
4627
|
+
const kind = (process.env["AGT_CONV_EVAL_BACKEND"] ?? "claude-p").trim().toLowerCase();
|
|
4628
|
+
if (kind === "local") {
|
|
4629
|
+
const url = process.env["AGT_CONV_EVAL_LOCAL_URL"]?.trim() || DEFAULT_LOCAL_EVAL_URL;
|
|
4630
|
+
const model = process.env["AGT_CONV_EVAL_LOCAL_MODEL"]?.trim() || DEFAULT_LOCAL_EVAL_MODEL;
|
|
4631
|
+
const apiKey = process.env["AGT_CONV_EVAL_LOCAL_API_KEY"]?.trim() || void 0;
|
|
4632
|
+
const safeUrl = (() => {
|
|
4633
|
+
try {
|
|
4634
|
+
const parsed = new URL(url);
|
|
4635
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
4636
|
+
} catch {
|
|
4637
|
+
return "[invalid-url]";
|
|
4638
|
+
}
|
|
4639
|
+
})();
|
|
4640
|
+
log(`[conversation-eval] backend=local url=${safeUrl} model=${model}`);
|
|
4641
|
+
conversationEvalBackend = { model, run: (prompt) => runLocalEvalChat(prompt, { url, model, apiKey }) };
|
|
4642
|
+
return conversationEvalBackend;
|
|
4643
|
+
}
|
|
4644
|
+
if (kind === "" || kind === "claude-p") {
|
|
4645
|
+
const model = process.env["AGT_CONV_EVAL_CLAUDE_MODEL"]?.trim() || DEFAULT_CLAUDE_EVAL_MODEL;
|
|
4646
|
+
conversationEvalBackend = { model, run: (prompt) => runEvalClaude(prompt, model) };
|
|
4647
|
+
return conversationEvalBackend;
|
|
4648
|
+
}
|
|
4649
|
+
log(`[conversation-eval] invalid AGT_CONV_EVAL_BACKEND='${kind}' \u2014 expected 'claude-p' or 'local'; evaluation disabled`);
|
|
4650
|
+
conversationEvalBackend = {
|
|
4651
|
+
model: "invalid-conversation-eval-backend",
|
|
4652
|
+
run: async () => {
|
|
4653
|
+
throw new Error(`Unsupported AGT_CONV_EVAL_BACKEND='${kind}'. Expected 'claude-p' or 'local'.`);
|
|
4654
|
+
}
|
|
4655
|
+
};
|
|
4656
|
+
return conversationEvalBackend;
|
|
4657
|
+
}
|
|
4323
4658
|
function loadGatewayPorts() {
|
|
4324
4659
|
try {
|
|
4325
|
-
return JSON.parse(
|
|
4660
|
+
return JSON.parse(readFileSync9(GATEWAY_PORTS_FILE, "utf-8"));
|
|
4326
4661
|
} catch {
|
|
4327
4662
|
return {};
|
|
4328
4663
|
}
|
|
@@ -4352,10 +4687,10 @@ function freePort(codeName) {
|
|
|
4352
4687
|
}
|
|
4353
4688
|
}
|
|
4354
4689
|
function getStateFile() {
|
|
4355
|
-
return
|
|
4690
|
+
return join8(config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
4356
4691
|
}
|
|
4357
4692
|
function channelHashCacheDir() {
|
|
4358
|
-
return config?.configDir ??
|
|
4693
|
+
return config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
4359
4694
|
}
|
|
4360
4695
|
function loadChannelHashCache2() {
|
|
4361
4696
|
loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -4366,7 +4701,7 @@ function saveChannelHashCache2() {
|
|
|
4366
4701
|
var _channelQuarantineStore = null;
|
|
4367
4702
|
function channelQuarantineStore() {
|
|
4368
4703
|
if (!_channelQuarantineStore) {
|
|
4369
|
-
const dir = config?.configDir ??
|
|
4704
|
+
const dir = config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
4370
4705
|
_channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
|
|
4371
4706
|
}
|
|
4372
4707
|
return _channelQuarantineStore;
|
|
@@ -4421,7 +4756,7 @@ function log(msg) {
|
|
|
4421
4756
|
`;
|
|
4422
4757
|
if (!managerLogPath) {
|
|
4423
4758
|
try {
|
|
4424
|
-
managerLogPath =
|
|
4759
|
+
managerLogPath = join8(homedir4(), ".augmented", "manager.log");
|
|
4425
4760
|
mkdirSync4(dirname3(managerLogPath), { recursive: true });
|
|
4426
4761
|
if (existsSync5(managerLogPath)) {
|
|
4427
4762
|
chmodSync(managerLogPath, 384);
|
|
@@ -4451,7 +4786,7 @@ function sha256(content) {
|
|
|
4451
4786
|
}
|
|
4452
4787
|
function hashFile(filePath) {
|
|
4453
4788
|
try {
|
|
4454
|
-
const content =
|
|
4789
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
4455
4790
|
return sha256(content);
|
|
4456
4791
|
} catch {
|
|
4457
4792
|
return null;
|
|
@@ -4475,13 +4810,13 @@ function parseSkillFrontmatter(content) {
|
|
|
4475
4810
|
return out;
|
|
4476
4811
|
}
|
|
4477
4812
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
4478
|
-
const { readdirSync:
|
|
4479
|
-
const skillsDir =
|
|
4480
|
-
const claudeMdPath =
|
|
4813
|
+
const { readdirSync: readdirSync5, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync5 } = await import("fs");
|
|
4814
|
+
const skillsDir = join8(configDir, codeName, "project", ".claude", "skills");
|
|
4815
|
+
const claudeMdPath = join8(configDir, codeName, "project", "CLAUDE.md");
|
|
4481
4816
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
4482
4817
|
const entries = [];
|
|
4483
|
-
for (const dir of
|
|
4484
|
-
const skillFile =
|
|
4818
|
+
for (const dir of readdirSync5(skillsDir).sort()) {
|
|
4819
|
+
const skillFile = join8(skillsDir, dir, "SKILL.md");
|
|
4485
4820
|
if (!ex(skillFile)) continue;
|
|
4486
4821
|
try {
|
|
4487
4822
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -4529,10 +4864,10 @@ ${SKILLS_INDEX_END}`;
|
|
|
4529
4864
|
}
|
|
4530
4865
|
async function migrateToProfiles() {
|
|
4531
4866
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4532
|
-
const sharedConfigPath =
|
|
4867
|
+
const sharedConfigPath = join8(homeDir, ".openclaw", "openclaw.json");
|
|
4533
4868
|
let sharedConfig;
|
|
4534
4869
|
try {
|
|
4535
|
-
sharedConfig = JSON.parse(
|
|
4870
|
+
sharedConfig = JSON.parse(readFileSync9(sharedConfigPath, "utf-8"));
|
|
4536
4871
|
} catch {
|
|
4537
4872
|
return;
|
|
4538
4873
|
}
|
|
@@ -4545,19 +4880,19 @@ async function migrateToProfiles() {
|
|
|
4545
4880
|
const codeName = agentEntry["id"];
|
|
4546
4881
|
if (!codeName) continue;
|
|
4547
4882
|
if (codeName === "main") continue;
|
|
4548
|
-
const profileDir =
|
|
4549
|
-
if (existsSync5(
|
|
4883
|
+
const profileDir = join8(homeDir, `.openclaw-${codeName}`);
|
|
4884
|
+
if (existsSync5(join8(profileDir, "openclaw.json"))) continue;
|
|
4550
4885
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
4551
4886
|
if (adapter.seedProfileConfig) {
|
|
4552
4887
|
adapter.seedProfileConfig(codeName);
|
|
4553
4888
|
}
|
|
4554
|
-
const sharedAuthDir =
|
|
4555
|
-
const profileAuthDir =
|
|
4556
|
-
const authFile =
|
|
4889
|
+
const sharedAuthDir = join8(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
4890
|
+
const profileAuthDir = join8(profileDir, "agents", codeName, "agent");
|
|
4891
|
+
const authFile = join8(sharedAuthDir, "auth-profiles.json");
|
|
4557
4892
|
if (existsSync5(authFile)) {
|
|
4558
4893
|
mkdirSync4(profileAuthDir, { recursive: true });
|
|
4559
|
-
const authContent =
|
|
4560
|
-
writeFileSync4(
|
|
4894
|
+
const authContent = readFileSync9(authFile, "utf-8");
|
|
4895
|
+
writeFileSync4(join8(profileAuthDir, "auth-profiles.json"), authContent);
|
|
4561
4896
|
}
|
|
4562
4897
|
allocatePort(codeName);
|
|
4563
4898
|
migrated++;
|
|
@@ -4595,7 +4930,7 @@ function readGatewayToken(codeName) {
|
|
|
4595
4930
|
}
|
|
4596
4931
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4597
4932
|
try {
|
|
4598
|
-
const cfg = JSON.parse(
|
|
4933
|
+
const cfg = JSON.parse(readFileSync9(join8(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
4599
4934
|
return cfg?.gateway?.auth?.token;
|
|
4600
4935
|
} catch {
|
|
4601
4936
|
return void 0;
|
|
@@ -4604,18 +4939,18 @@ function readGatewayToken(codeName) {
|
|
|
4604
4939
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
4605
4940
|
function isGatewayHung(codeName) {
|
|
4606
4941
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4607
|
-
const jobsPath =
|
|
4942
|
+
const jobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
4608
4943
|
if (!existsSync5(jobsPath)) return false;
|
|
4609
4944
|
try {
|
|
4610
|
-
const data = JSON.parse(
|
|
4945
|
+
const data = JSON.parse(readFileSync9(jobsPath, "utf-8"));
|
|
4611
4946
|
const jobs = data.jobs ?? data;
|
|
4612
4947
|
if (!Array.isArray(jobs)) return false;
|
|
4613
4948
|
const now = Date.now();
|
|
4614
4949
|
for (const job of jobs) {
|
|
4615
|
-
const
|
|
4616
|
-
if (!
|
|
4617
|
-
const runStartedAt =
|
|
4618
|
-
const isRunning =
|
|
4950
|
+
const state6 = job.state;
|
|
4951
|
+
if (!state6) continue;
|
|
4952
|
+
const runStartedAt = state6.runStartedAtMs;
|
|
4953
|
+
const isRunning = state6.status === "running" || state6.running === true;
|
|
4619
4954
|
if (isRunning && runStartedAt && now - runStartedAt > GATEWAY_HUNG_TIMEOUT_MS) {
|
|
4620
4955
|
return true;
|
|
4621
4956
|
}
|
|
@@ -4640,15 +4975,15 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4640
4975
|
}
|
|
4641
4976
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
4642
4977
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4643
|
-
const cronJobsPath =
|
|
4978
|
+
const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
4644
4979
|
clearStaleCronRunState(cronJobsPath);
|
|
4645
4980
|
} else {
|
|
4646
4981
|
if (status.port) {
|
|
4647
4982
|
try {
|
|
4648
4983
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4649
|
-
const configPath =
|
|
4984
|
+
const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4650
4985
|
if (existsSync5(configPath)) {
|
|
4651
|
-
const cfg = JSON.parse(
|
|
4986
|
+
const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
|
|
4652
4987
|
if (cfg.gateway?.port !== status.port) {
|
|
4653
4988
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4654
4989
|
cfg.gateway.port = status.port;
|
|
@@ -4672,9 +5007,9 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4672
5007
|
gatewaysStartedThisCycle.add(codeName);
|
|
4673
5008
|
try {
|
|
4674
5009
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4675
|
-
const configPath =
|
|
5010
|
+
const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4676
5011
|
if (existsSync5(configPath)) {
|
|
4677
|
-
const cfg = JSON.parse(
|
|
5012
|
+
const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
|
|
4678
5013
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4679
5014
|
cfg.gateway.port = port;
|
|
4680
5015
|
writeFileSync4(configPath, JSON.stringify(cfg, null, 2));
|
|
@@ -4816,7 +5151,7 @@ async function pollCycle() {
|
|
|
4816
5151
|
const now = Date.now();
|
|
4817
5152
|
if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
|
|
4818
5153
|
try {
|
|
4819
|
-
const firstAgent =
|
|
5154
|
+
const firstAgent = state5.agents[0];
|
|
4820
5155
|
const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework("openclaw");
|
|
4821
5156
|
if (versionAdapter.getVersion) {
|
|
4822
5157
|
cachedFrameworkVersion = await versionAdapter.getVersion();
|
|
@@ -4827,7 +5162,7 @@ async function pollCycle() {
|
|
|
4827
5162
|
}
|
|
4828
5163
|
try {
|
|
4829
5164
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
4830
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
5165
|
+
const { collectDiagnostics } = await import("../persistent-session-YNVCVUK3.js");
|
|
4831
5166
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
4832
5167
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
4833
5168
|
let tailscaleHostname;
|
|
@@ -4900,7 +5235,7 @@ async function pollCycle() {
|
|
|
4900
5235
|
const {
|
|
4901
5236
|
collectResponsivenessProbes,
|
|
4902
5237
|
getResponsivenessIntervalMs
|
|
4903
|
-
} = await import("../responsiveness-probe-
|
|
5238
|
+
} = await import("../responsiveness-probe-5ICEBNCM.js");
|
|
4904
5239
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
4905
5240
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
4906
5241
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -4963,7 +5298,7 @@ async function pollCycle() {
|
|
|
4963
5298
|
for (const agent of agents) {
|
|
4964
5299
|
const requested = agent.restart_requested_at ?? null;
|
|
4965
5300
|
if (!requested) continue;
|
|
4966
|
-
const prev =
|
|
5301
|
+
const prev = state5.agents.find((a) => a.agentId === agent.agent_id);
|
|
4967
5302
|
const lastProcessed = prev?.lastRestartProcessedAt ?? null;
|
|
4968
5303
|
if (lastProcessed && Date.parse(lastProcessed) >= Date.parse(requested)) continue;
|
|
4969
5304
|
log(`[restart] Dashboard requested restart for '${agent.code_name}' at ${requested}`);
|
|
@@ -4986,7 +5321,7 @@ async function pollCycle() {
|
|
|
4986
5321
|
await processAgent(agent, agentStates);
|
|
4987
5322
|
} catch (err) {
|
|
4988
5323
|
log(`Error processing agent '${agent.code_name}': ${err.message}`);
|
|
4989
|
-
const existing =
|
|
5324
|
+
const existing = state5.agents.find((a) => a.agentId === agent.agent_id);
|
|
4990
5325
|
if (existing) {
|
|
4991
5326
|
agentStates.push(existing);
|
|
4992
5327
|
} else {
|
|
@@ -5012,12 +5347,12 @@ async function pollCycle() {
|
|
|
5012
5347
|
void maybeReportActivityCache({ api, log });
|
|
5013
5348
|
const restartAckStateChanged = applyRestartAcks({
|
|
5014
5349
|
agentStates,
|
|
5015
|
-
priorAgents:
|
|
5350
|
+
priorAgents: state5.agents,
|
|
5016
5351
|
restartAcks
|
|
5017
5352
|
});
|
|
5018
5353
|
if (restartAckStateChanged) {
|
|
5019
5354
|
try {
|
|
5020
|
-
const ackedState = { ...
|
|
5355
|
+
const ackedState = { ...state5, agents: agentStates };
|
|
5021
5356
|
atomicWriteFileSync(getStateFile(), JSON.stringify(ackedState, null, 2));
|
|
5022
5357
|
} catch (err) {
|
|
5023
5358
|
log(`[restart] failed to persist ack immediately: ${err.message}`);
|
|
@@ -5035,7 +5370,7 @@ async function pollCycle() {
|
|
|
5035
5370
|
} catch {
|
|
5036
5371
|
}
|
|
5037
5372
|
const currentIds = new Set(agents.map((a) => a.agent_id));
|
|
5038
|
-
for (const prev of
|
|
5373
|
+
for (const prev of state5.agents) {
|
|
5039
5374
|
if (!currentIds.has(prev.agentId)) {
|
|
5040
5375
|
log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
|
|
5041
5376
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
@@ -5048,7 +5383,7 @@ async function pollCycle() {
|
|
|
5048
5383
|
}
|
|
5049
5384
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
5050
5385
|
freePort(prev.codeName);
|
|
5051
|
-
const agentDir =
|
|
5386
|
+
const agentDir = join8(adapter.getAgentDir(prev.codeName), "provision");
|
|
5052
5387
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
5053
5388
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
5054
5389
|
}
|
|
@@ -5156,10 +5491,10 @@ async function pollCycle() {
|
|
|
5156
5491
|
}
|
|
5157
5492
|
} catch {
|
|
5158
5493
|
}
|
|
5159
|
-
|
|
5160
|
-
...
|
|
5494
|
+
state5 = {
|
|
5495
|
+
...state5,
|
|
5161
5496
|
lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5162
|
-
pollCount:
|
|
5497
|
+
pollCount: state5.pollCount + 1,
|
|
5163
5498
|
agents: agentStates,
|
|
5164
5499
|
// ENG-5441: serialise trip state on every poll so manager restarts
|
|
5165
5500
|
// never silently clear a tripped breaker. Cheap — only tripped
|
|
@@ -5170,9 +5505,9 @@ async function pollCycle() {
|
|
|
5170
5505
|
log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
|
|
5171
5506
|
consecutivePollFailures = 0;
|
|
5172
5507
|
}
|
|
5173
|
-
send({ type: "state-update", state:
|
|
5508
|
+
send({ type: "state-update", state: state5 });
|
|
5174
5509
|
} catch (err) {
|
|
5175
|
-
|
|
5510
|
+
state5.errorCount++;
|
|
5176
5511
|
const message = err.message;
|
|
5177
5512
|
log(`Poll error: ${message}`);
|
|
5178
5513
|
send({ type: "error", message });
|
|
@@ -5216,10 +5551,17 @@ async function processAgent(agent, agentStates) {
|
|
|
5216
5551
|
agentId: agent.agent_id,
|
|
5217
5552
|
log
|
|
5218
5553
|
});
|
|
5554
|
+
void maybeEvaluateConversations({
|
|
5555
|
+
api,
|
|
5556
|
+
backend: resolveConversationEvalBackend(),
|
|
5557
|
+
codeName: agent.code_name,
|
|
5558
|
+
agentId: agent.agent_id,
|
|
5559
|
+
log
|
|
5560
|
+
});
|
|
5219
5561
|
}
|
|
5220
5562
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5221
5563
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
5222
|
-
let agentDir =
|
|
5564
|
+
let agentDir = join8(adapter.getAgentDir(agent.code_name), "provision");
|
|
5223
5565
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
5224
5566
|
if (previousKnownStatus !== agent.status) {
|
|
5225
5567
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -5340,7 +5682,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5340
5682
|
});
|
|
5341
5683
|
} catch (err) {
|
|
5342
5684
|
log(`Refresh failed for '${agent.code_name}': ${err.message}`);
|
|
5343
|
-
const existing =
|
|
5685
|
+
const existing = state5.agents.find((a) => a.agentId === agent.agent_id);
|
|
5344
5686
|
agentStates.push(existing ?? {
|
|
5345
5687
|
agentId: agent.agent_id,
|
|
5346
5688
|
codeName: agent.code_name,
|
|
@@ -5388,7 +5730,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5388
5730
|
const frameworkId = refreshData.agent.framework ?? "openclaw";
|
|
5389
5731
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
5390
5732
|
const frameworkAdapter = getFramework(frameworkId);
|
|
5391
|
-
agentDir =
|
|
5733
|
+
agentDir = join8(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
5392
5734
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
5393
5735
|
if (frameworkAdapter.migrateSecretStorage && !migratedSecretStorage.has(agent.code_name)) {
|
|
5394
5736
|
try {
|
|
@@ -5404,7 +5746,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5404
5746
|
const charterVersion = refreshData.charter.version;
|
|
5405
5747
|
const toolsVersion = refreshData.tools.version;
|
|
5406
5748
|
const known = agentState.knownVersions.get(agent.agent_id);
|
|
5407
|
-
let lastProvisionAt =
|
|
5749
|
+
let lastProvisionAt = state5.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
|
|
5408
5750
|
const quarantinedChannels = channelQuarantineStore().getQuarantinedKeys(agent.code_name);
|
|
5409
5751
|
const currentChannelIds = setWithout(
|
|
5410
5752
|
launchableChannelIds(refreshData.channel_configs),
|
|
@@ -5431,7 +5773,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5431
5773
|
const changedFiles = [];
|
|
5432
5774
|
mkdirSync4(agentDir, { recursive: true });
|
|
5433
5775
|
for (const artifact of artifacts) {
|
|
5434
|
-
const filePath =
|
|
5776
|
+
const filePath = join8(agentDir, artifact.relativePath);
|
|
5435
5777
|
let existingHash;
|
|
5436
5778
|
let newHash;
|
|
5437
5779
|
let writeContent = artifact.content;
|
|
@@ -5450,8 +5792,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5450
5792
|
};
|
|
5451
5793
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
5452
5794
|
try {
|
|
5453
|
-
const projectClaudeMd =
|
|
5454
|
-
const existing =
|
|
5795
|
+
const projectClaudeMd = join8(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
5796
|
+
const existing = readFileSync9(projectClaudeMd, "utf-8");
|
|
5455
5797
|
existingHash = sha256(stripDynamicSections(existing));
|
|
5456
5798
|
} catch {
|
|
5457
5799
|
existingHash = null;
|
|
@@ -5469,7 +5811,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5469
5811
|
const generatorKeys = Object.keys(generatorServers);
|
|
5470
5812
|
let existingRaw = "";
|
|
5471
5813
|
try {
|
|
5472
|
-
existingRaw =
|
|
5814
|
+
existingRaw = readFileSync9(filePath, "utf-8");
|
|
5473
5815
|
} catch {
|
|
5474
5816
|
}
|
|
5475
5817
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -5491,12 +5833,12 @@ async function processAgent(agent, agentStates) {
|
|
|
5491
5833
|
}
|
|
5492
5834
|
}
|
|
5493
5835
|
if (changedFiles.length > 0) {
|
|
5494
|
-
const isFirst = !existsSync5(
|
|
5836
|
+
const isFirst = !existsSync5(join8(agentDir, "CHARTER.md"));
|
|
5495
5837
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
5496
5838
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
5497
5839
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
5498
5840
|
for (const file of changedFiles) {
|
|
5499
|
-
const filePath =
|
|
5841
|
+
const filePath = join8(agentDir, file.relativePath);
|
|
5500
5842
|
mkdirSync4(dirname3(filePath), { recursive: true });
|
|
5501
5843
|
if (file.relativePath === ".mcp.json") {
|
|
5502
5844
|
safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
|
|
@@ -5505,12 +5847,12 @@ async function processAgent(agent, agentStates) {
|
|
|
5505
5847
|
}
|
|
5506
5848
|
}
|
|
5507
5849
|
try {
|
|
5508
|
-
const provSkillsDir =
|
|
5850
|
+
const provSkillsDir = join8(agentDir, ".claude", "skills");
|
|
5509
5851
|
if (existsSync5(provSkillsDir)) {
|
|
5510
|
-
for (const folder of
|
|
5852
|
+
for (const folder of readdirSync4(provSkillsDir)) {
|
|
5511
5853
|
if (folder.startsWith("knowledge-")) {
|
|
5512
5854
|
try {
|
|
5513
|
-
rmSync2(
|
|
5855
|
+
rmSync2(join8(provSkillsDir, folder), { recursive: true });
|
|
5514
5856
|
} catch {
|
|
5515
5857
|
}
|
|
5516
5858
|
}
|
|
@@ -5523,7 +5865,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5523
5865
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
5524
5866
|
const hashes = /* @__PURE__ */ new Map();
|
|
5525
5867
|
for (const file of trackedFiles2) {
|
|
5526
|
-
const h = hashFile(
|
|
5868
|
+
const h = hashFile(join8(agentDir, file));
|
|
5527
5869
|
if (h) hashes.set(file, h);
|
|
5528
5870
|
}
|
|
5529
5871
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -5591,7 +5933,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5591
5933
|
if (written && existsSync5(agentDir)) {
|
|
5592
5934
|
const driftedFiles = [];
|
|
5593
5935
|
for (const [file, expectedHash] of written) {
|
|
5594
|
-
const localHash = hashFile(
|
|
5936
|
+
const localHash = hashFile(join8(agentDir, file));
|
|
5595
5937
|
if (localHash && localHash !== expectedHash) {
|
|
5596
5938
|
driftedFiles.push(file);
|
|
5597
5939
|
}
|
|
@@ -5602,7 +5944,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5602
5944
|
try {
|
|
5603
5945
|
const localHashes = {};
|
|
5604
5946
|
for (const file of driftedFiles) {
|
|
5605
|
-
localHashes[file] = hashFile(
|
|
5947
|
+
localHashes[file] = hashFile(join8(agentDir, file));
|
|
5606
5948
|
}
|
|
5607
5949
|
await api.post("/host/drift", {
|
|
5608
5950
|
agent_id: agent.agent_id,
|
|
@@ -5865,18 +6207,18 @@ async function processAgent(agent, agentStates) {
|
|
|
5865
6207
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
5866
6208
|
try {
|
|
5867
6209
|
const agentProvisionDir = agentDir;
|
|
5868
|
-
const projectDir =
|
|
6210
|
+
const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
|
|
5869
6211
|
mkdirSync4(agentProvisionDir, { recursive: true });
|
|
5870
6212
|
mkdirSync4(projectDir, { recursive: true });
|
|
5871
|
-
const provisionMcpPath =
|
|
5872
|
-
const projectMcpPath =
|
|
6213
|
+
const provisionMcpPath = join8(agentProvisionDir, ".mcp.json");
|
|
6214
|
+
const projectMcpPath = join8(projectDir, ".mcp.json");
|
|
5873
6215
|
let mcpConfig = { mcpServers: {} };
|
|
5874
6216
|
try {
|
|
5875
|
-
mcpConfig = JSON.parse(
|
|
6217
|
+
mcpConfig = JSON.parse(readFileSync9(provisionMcpPath, "utf-8"));
|
|
5876
6218
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
5877
6219
|
} catch {
|
|
5878
6220
|
}
|
|
5879
|
-
const localDirectChatChannel =
|
|
6221
|
+
const localDirectChatChannel = join8(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
5880
6222
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
5881
6223
|
const directChatTz = (() => {
|
|
5882
6224
|
const tz = directChatTeamSettings?.["timezone"];
|
|
@@ -5906,7 +6248,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5906
6248
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
5907
6249
|
}
|
|
5908
6250
|
}
|
|
5909
|
-
const staleChannelsPath =
|
|
6251
|
+
const staleChannelsPath = join8(projectDir, ".mcp-channels.json");
|
|
5910
6252
|
if (existsSync5(staleChannelsPath)) {
|
|
5911
6253
|
try {
|
|
5912
6254
|
rmSync2(staleChannelsPath, { force: true });
|
|
@@ -5917,7 +6259,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5917
6259
|
log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
|
|
5918
6260
|
}
|
|
5919
6261
|
}
|
|
5920
|
-
let lastSecretsProvisionAt =
|
|
6262
|
+
let lastSecretsProvisionAt = state5.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
|
|
5921
6263
|
let secretsHash = agentState.knownSecretsHashes.get(agent.agent_id) ?? null;
|
|
5922
6264
|
try {
|
|
5923
6265
|
const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
|
|
@@ -5971,7 +6313,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5971
6313
|
}
|
|
5972
6314
|
if (process.env.AGT_CONNECTIVITY_PROBE_ENABLED === "true") {
|
|
5973
6315
|
try {
|
|
5974
|
-
const probeProjectDir =
|
|
6316
|
+
const probeProjectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
|
|
5975
6317
|
await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
|
|
5976
6318
|
} catch (err) {
|
|
5977
6319
|
log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -5981,11 +6323,11 @@ async function processAgent(agent, agentStates) {
|
|
|
5981
6323
|
const intHash = computeIntegrationsHash(integrations);
|
|
5982
6324
|
const prevIntHash = agentState.knownIntegrationHashes.get(agent.agent_id);
|
|
5983
6325
|
if (intHash !== prevIntHash) {
|
|
5984
|
-
const projectDir =
|
|
5985
|
-
const envIntPath =
|
|
6326
|
+
const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
|
|
6327
|
+
const envIntPath = join8(projectDir, ".env.integrations");
|
|
5986
6328
|
let preWriteEnv;
|
|
5987
6329
|
try {
|
|
5988
|
-
preWriteEnv =
|
|
6330
|
+
preWriteEnv = readFileSync9(envIntPath, "utf-8");
|
|
5989
6331
|
} catch {
|
|
5990
6332
|
preWriteEnv = void 0;
|
|
5991
6333
|
}
|
|
@@ -5997,9 +6339,9 @@ async function processAgent(agent, agentStates) {
|
|
|
5997
6339
|
let rotationHandled = true;
|
|
5998
6340
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
5999
6341
|
try {
|
|
6000
|
-
const projectMcpPath =
|
|
6001
|
-
const postWriteEnv =
|
|
6002
|
-
const mcpContent =
|
|
6342
|
+
const projectMcpPath = join8(projectDir, ".mcp.json");
|
|
6343
|
+
const postWriteEnv = readFileSync9(envIntPath, "utf-8");
|
|
6344
|
+
const mcpContent = readFileSync9(projectMcpPath, "utf-8");
|
|
6003
6345
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
6004
6346
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
6005
6347
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -6067,8 +6409,8 @@ async function processAgent(agent, agentStates) {
|
|
|
6067
6409
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
6068
6410
|
if (mcpPath) {
|
|
6069
6411
|
try {
|
|
6070
|
-
const { readFileSync:
|
|
6071
|
-
const mcpConfig = JSON.parse(
|
|
6412
|
+
const { readFileSync: readFileSync10 } = await import("fs");
|
|
6413
|
+
const mcpConfig = JSON.parse(readFileSync10(mcpPath, "utf-8"));
|
|
6072
6414
|
if (mcpConfig.mcpServers) {
|
|
6073
6415
|
const managedPrefixes = [
|
|
6074
6416
|
"composio_",
|
|
@@ -6164,7 +6506,7 @@ async function processAgent(agent, agentStates) {
|
|
|
6164
6506
|
if (agent.status === "active") {
|
|
6165
6507
|
if (frameworkAdapter.installPlugin) {
|
|
6166
6508
|
try {
|
|
6167
|
-
const pluginPath =
|
|
6509
|
+
const pluginPath = join8(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
6168
6510
|
if (existsSync5(pluginPath)) {
|
|
6169
6511
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
6170
6512
|
agtHost: requireHost(),
|
|
@@ -6230,25 +6572,25 @@ async function processAgent(agent, agentStates) {
|
|
|
6230
6572
|
}
|
|
6231
6573
|
}
|
|
6232
6574
|
try {
|
|
6233
|
-
const { readdirSync:
|
|
6575
|
+
const { readdirSync: readdirSync5, rmSync: rmSync3 } = await import("fs");
|
|
6234
6576
|
const { homedir: homedir5 } = await import("os");
|
|
6235
6577
|
const frameworkId2 = frameworkAdapter.id;
|
|
6236
6578
|
const candidateSkillDirs = [
|
|
6237
6579
|
// Claude Code — framework runtime tree
|
|
6238
|
-
|
|
6580
|
+
join8(homedir5(), ".augmented", agent.code_name, "skills"),
|
|
6239
6581
|
// Claude Code — project tree
|
|
6240
|
-
|
|
6582
|
+
join8(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
6241
6583
|
// OpenClaw — framework runtime tree
|
|
6242
|
-
|
|
6584
|
+
join8(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
|
|
6243
6585
|
// Defensive: legacy provision-side path, not currently an
|
|
6244
6586
|
// install target but cheap to sweep.
|
|
6245
|
-
|
|
6587
|
+
join8(agentDir, ".claude", "skills")
|
|
6246
6588
|
];
|
|
6247
6589
|
const existingDirs = candidateSkillDirs.filter((d) => existsSync5(d));
|
|
6248
6590
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
6249
6591
|
for (const dir of existingDirs) {
|
|
6250
6592
|
try {
|
|
6251
|
-
for (const entry of
|
|
6593
|
+
for (const entry of readdirSync5(dir)) {
|
|
6252
6594
|
if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
|
|
6253
6595
|
discoveredEntries.add(entry);
|
|
6254
6596
|
}
|
|
@@ -6258,7 +6600,7 @@ async function processAgent(agent, agentStates) {
|
|
|
6258
6600
|
}
|
|
6259
6601
|
const removeSkillFolder = (entry, reason) => {
|
|
6260
6602
|
for (const dir of existingDirs) {
|
|
6261
|
-
const p =
|
|
6603
|
+
const p = join8(dir, entry);
|
|
6262
6604
|
if (existsSync5(p)) {
|
|
6263
6605
|
rmSync3(p, { recursive: true, force: true });
|
|
6264
6606
|
}
|
|
@@ -6420,8 +6762,8 @@ async function processAgent(agent, agentStates) {
|
|
|
6420
6762
|
const sess = getSessionState(agent.code_name);
|
|
6421
6763
|
let mcpJsonParsed = null;
|
|
6422
6764
|
try {
|
|
6423
|
-
const mcpPath =
|
|
6424
|
-
mcpJsonParsed = JSON.parse(
|
|
6765
|
+
const mcpPath = join8(getProjectDir(agent.code_name), ".mcp.json");
|
|
6766
|
+
mcpJsonParsed = JSON.parse(readFileSync9(mcpPath, "utf-8"));
|
|
6425
6767
|
} catch {
|
|
6426
6768
|
}
|
|
6427
6769
|
reapMissingMcpSessions({
|
|
@@ -6606,10 +6948,10 @@ async function processAgent(agent, agentStates) {
|
|
|
6606
6948
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
6607
6949
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
6608
6950
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
6609
|
-
const jobsPath =
|
|
6951
|
+
const jobsPath = join8(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
6610
6952
|
if (existsSync5(jobsPath)) {
|
|
6611
6953
|
try {
|
|
6612
|
-
const jobsData = JSON.parse(
|
|
6954
|
+
const jobsData = JSON.parse(readFileSync9(jobsPath, "utf-8"));
|
|
6613
6955
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
6614
6956
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
6615
6957
|
);
|
|
@@ -6746,7 +7088,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
6746
7088
|
if (trackedFiles.length > 0 && existsSync5(agentDir)) {
|
|
6747
7089
|
const hashes = /* @__PURE__ */ new Map();
|
|
6748
7090
|
for (const file of trackedFiles) {
|
|
6749
|
-
const h = hashFile(
|
|
7091
|
+
const h = hashFile(join8(agentDir, file));
|
|
6750
7092
|
if (h) hashes.set(file, h);
|
|
6751
7093
|
}
|
|
6752
7094
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -6780,19 +7122,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
6780
7122
|
lastCleanupAt.set(codeName, Date.now());
|
|
6781
7123
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
6782
7124
|
for (const agentDir of ["main", codeName]) {
|
|
6783
|
-
const sessionsDir =
|
|
7125
|
+
const sessionsDir = join8(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
6784
7126
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
6785
7127
|
}
|
|
6786
|
-
const cronRunsDir =
|
|
7128
|
+
const cronRunsDir = join8(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
6787
7129
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
6788
|
-
const cronJobsPath =
|
|
7130
|
+
const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
6789
7131
|
clearStaleCronRunState(cronJobsPath);
|
|
6790
7132
|
}
|
|
6791
7133
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
6792
|
-
const indexPath =
|
|
7134
|
+
const indexPath = join8(sessionsDir, "sessions.json");
|
|
6793
7135
|
if (!existsSync5(indexPath)) return;
|
|
6794
7136
|
try {
|
|
6795
|
-
const raw =
|
|
7137
|
+
const raw = readFileSync9(indexPath, "utf-8");
|
|
6796
7138
|
const index = JSON.parse(raw);
|
|
6797
7139
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
6798
7140
|
key: k,
|
|
@@ -6805,7 +7147,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6805
7147
|
for (const entry of toDelete) {
|
|
6806
7148
|
delete index[entry.key];
|
|
6807
7149
|
if (entry.sessionId) {
|
|
6808
|
-
const sessionFile =
|
|
7150
|
+
const sessionFile = join8(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
6809
7151
|
try {
|
|
6810
7152
|
if (existsSync5(sessionFile)) {
|
|
6811
7153
|
unlinkSync(sessionFile);
|
|
@@ -6827,7 +7169,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6827
7169
|
delete index[parentKey];
|
|
6828
7170
|
if (parentSessionId) {
|
|
6829
7171
|
try {
|
|
6830
|
-
const f =
|
|
7172
|
+
const f = join8(sessionsDir, `${parentSessionId}.jsonl`);
|
|
6831
7173
|
if (existsSync5(f)) {
|
|
6832
7174
|
unlinkSync(f);
|
|
6833
7175
|
deletedFiles++;
|
|
@@ -6848,26 +7190,26 @@ var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
|
6848
7190
|
function clearStaleCronRunState(jobsPath) {
|
|
6849
7191
|
if (!existsSync5(jobsPath)) return;
|
|
6850
7192
|
try {
|
|
6851
|
-
const raw =
|
|
7193
|
+
const raw = readFileSync9(jobsPath, "utf-8");
|
|
6852
7194
|
const data = JSON.parse(raw);
|
|
6853
7195
|
const jobs = data.jobs ?? data;
|
|
6854
7196
|
if (!Array.isArray(jobs)) return;
|
|
6855
7197
|
let changed = false;
|
|
6856
7198
|
const now = Date.now();
|
|
6857
7199
|
for (const job of jobs) {
|
|
6858
|
-
const
|
|
6859
|
-
if (!
|
|
6860
|
-
const runStartedAt =
|
|
6861
|
-
const isRunning =
|
|
7200
|
+
const state6 = job.state;
|
|
7201
|
+
if (!state6) continue;
|
|
7202
|
+
const runStartedAt = state6.runningAtMs ?? state6.runStartedAtMs;
|
|
7203
|
+
const isRunning = state6.running === true || state6.status === "running";
|
|
6862
7204
|
if (isRunning && runStartedAt && now - runStartedAt > STALE_RUN_TIMEOUT_MS) {
|
|
6863
|
-
|
|
6864
|
-
delete
|
|
6865
|
-
delete
|
|
6866
|
-
delete
|
|
7205
|
+
state6.running = false;
|
|
7206
|
+
delete state6.status;
|
|
7207
|
+
delete state6.runStartedAtMs;
|
|
7208
|
+
delete state6.currentRunId;
|
|
6867
7209
|
changed = true;
|
|
6868
7210
|
log(`Cleared stale running state for cron job '${job.name}' (stuck for ${Math.round((now - runStartedAt) / 6e4)}min)`);
|
|
6869
7211
|
} else if (isRunning && !runStartedAt) {
|
|
6870
|
-
|
|
7212
|
+
state6.running = false;
|
|
6871
7213
|
changed = true;
|
|
6872
7214
|
log(`Cleared stale running state for cron job '${job.name}' (no start timestamp)`);
|
|
6873
7215
|
}
|
|
@@ -6883,11 +7225,11 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
|
6883
7225
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
6884
7226
|
let removed = 0;
|
|
6885
7227
|
try {
|
|
6886
|
-
for (const f of
|
|
7228
|
+
for (const f of readdirSync4(dir)) {
|
|
6887
7229
|
if (!f.endsWith(ext)) continue;
|
|
6888
|
-
const fullPath =
|
|
7230
|
+
const fullPath = join8(dir, f);
|
|
6889
7231
|
try {
|
|
6890
|
-
const st =
|
|
7232
|
+
const st = statSync3(fullPath);
|
|
6891
7233
|
if (st.mtimeMs < cutoff) {
|
|
6892
7234
|
unlinkSync(fullPath);
|
|
6893
7235
|
removed++;
|
|
@@ -6905,7 +7247,7 @@ var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
|
6905
7247
|
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
6906
7248
|
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
6907
7249
|
function claudePidFilePath() {
|
|
6908
|
-
return
|
|
7250
|
+
return join8(homedir4(), ".augmented", "manager-claude-pids.json");
|
|
6909
7251
|
}
|
|
6910
7252
|
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
6911
7253
|
function registerClaudeSpawn(record) {
|
|
@@ -6950,16 +7292,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
6950
7292
|
enabled: t.enabled ?? true,
|
|
6951
7293
|
triggered_at: t.triggered_at ?? null
|
|
6952
7294
|
}));
|
|
6953
|
-
const
|
|
6954
|
-
claudeSchedulerStates.set(codeName,
|
|
7295
|
+
const state7 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
7296
|
+
claudeSchedulerStates.set(codeName, state7);
|
|
6955
7297
|
agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
6956
7298
|
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
6957
7299
|
}
|
|
6958
7300
|
if (!claudeSchedulerStates.has(codeName)) {
|
|
6959
7301
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
6960
7302
|
}
|
|
6961
|
-
const
|
|
6962
|
-
const ready = getReadyTasks(
|
|
7303
|
+
const state6 = claudeSchedulerStates.get(codeName);
|
|
7304
|
+
const ready = getReadyTasks(state6, inFlightClaudeTasks);
|
|
6963
7305
|
if (ready.length === 0) return;
|
|
6964
7306
|
for (const task of ready) {
|
|
6965
7307
|
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
@@ -7106,8 +7448,8 @@ async function deliverScheduledCardResult(codeName, agentId, cardId) {
|
|
|
7106
7448
|
markScheduledCardDeliveryComplete(cardId);
|
|
7107
7449
|
return "terminal";
|
|
7108
7450
|
}
|
|
7109
|
-
const
|
|
7110
|
-
const task =
|
|
7451
|
+
const state6 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
7452
|
+
const task = state6.tasks[card.source_ref];
|
|
7111
7453
|
if (!task) {
|
|
7112
7454
|
log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
|
|
7113
7455
|
markScheduledCardDeliveryComplete(cardId);
|
|
@@ -7213,7 +7555,7 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
|
|
|
7213
7555
|
}
|
|
7214
7556
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
7215
7557
|
const projectDir = getProjectDir2(codeName);
|
|
7216
|
-
const mcpConfigPath =
|
|
7558
|
+
const mcpConfigPath = join8(projectDir, ".mcp.json");
|
|
7217
7559
|
let runId = null;
|
|
7218
7560
|
let kanbanItemId = null;
|
|
7219
7561
|
let taskResult;
|
|
@@ -7221,11 +7563,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
7221
7563
|
const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
|
|
7222
7564
|
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
|
|
7223
7565
|
try {
|
|
7224
|
-
const claudeMdPath =
|
|
7566
|
+
const claudeMdPath = join8(projectDir, "CLAUDE.md");
|
|
7225
7567
|
const serverNames = [];
|
|
7226
7568
|
if (existsSync5(mcpConfigPath)) {
|
|
7227
7569
|
try {
|
|
7228
|
-
const d = JSON.parse(
|
|
7570
|
+
const d = JSON.parse(readFileSync9(mcpConfigPath, "utf-8"));
|
|
7229
7571
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
7230
7572
|
} catch {
|
|
7231
7573
|
}
|
|
@@ -7248,10 +7590,10 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
7248
7590
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
7249
7591
|
}
|
|
7250
7592
|
const childEnv = { ...process.env };
|
|
7251
|
-
const envIntPath =
|
|
7593
|
+
const envIntPath = join8(projectDir, ".env.integrations");
|
|
7252
7594
|
if (existsSync5(envIntPath)) {
|
|
7253
7595
|
try {
|
|
7254
|
-
Object.assign(childEnv, parseEnvIntegrations(
|
|
7596
|
+
Object.assign(childEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
|
|
7255
7597
|
} catch {
|
|
7256
7598
|
}
|
|
7257
7599
|
}
|
|
@@ -7423,8 +7765,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
|
|
|
7423
7765
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
7424
7766
|
const codeName = agent.code_name;
|
|
7425
7767
|
const projectDir = getProjectDir(codeName);
|
|
7426
|
-
const mcpConfigPath =
|
|
7427
|
-
const claudeMdPath =
|
|
7768
|
+
const mcpConfigPath = join8(projectDir, ".mcp.json");
|
|
7769
|
+
const claudeMdPath = join8(projectDir, "CLAUDE.md");
|
|
7428
7770
|
if (restartBreaker.isTripped(codeName)) {
|
|
7429
7771
|
const trip = restartBreaker.getTrip(codeName);
|
|
7430
7772
|
return {
|
|
@@ -7651,9 +7993,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash3("sha256").
|
|
|
7651
7993
|
} else if (!claudeSchedulerStates.has(codeName)) {
|
|
7652
7994
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
7653
7995
|
}
|
|
7654
|
-
const
|
|
7655
|
-
if (
|
|
7656
|
-
const ready = getReadyTasks(
|
|
7996
|
+
const state6 = claudeSchedulerStates.get(codeName);
|
|
7997
|
+
if (state6) {
|
|
7998
|
+
const ready = getReadyTasks(state6, inFlightClaudeTasks);
|
|
7657
7999
|
if (ready.length > 0) {
|
|
7658
8000
|
log(`[persistent-session] ${ready.length} ready task(s) for '${codeName}': ${ready.map((t) => `${t.name}(next=${t.nextFireAt ? new Date(t.nextFireAt).toISOString() : "null"})`).join(", ")}`);
|
|
7659
8001
|
}
|
|
@@ -8064,11 +8406,11 @@ ${escapeXml(msg.content)}
|
|
|
8064
8406
|
log(`[direct-chat] One-shot spawn for '${agent.codeName}' (msg=${msg.id}; host in-flight=${directChatSpawnGate.hostInFlight}, queued=${directChatSpawnGate.queuedCount})`);
|
|
8065
8407
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-EM24LTGV.js");
|
|
8066
8408
|
const projDir = ccProjectDir(agent.codeName);
|
|
8067
|
-
const mcpConfigPath =
|
|
8409
|
+
const mcpConfigPath = join8(projDir, ".mcp.json");
|
|
8068
8410
|
const serverNames = [];
|
|
8069
8411
|
if (existsSync5(mcpConfigPath)) {
|
|
8070
8412
|
try {
|
|
8071
|
-
const d = JSON.parse(
|
|
8413
|
+
const d = JSON.parse(readFileSync9(mcpConfigPath, "utf-8"));
|
|
8072
8414
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
8073
8415
|
} catch {
|
|
8074
8416
|
}
|
|
@@ -8087,15 +8429,15 @@ ${escapeXml(msg.content)}
|
|
|
8087
8429
|
"--allowedTools",
|
|
8088
8430
|
allowedTools
|
|
8089
8431
|
];
|
|
8090
|
-
const chatClaudeMd =
|
|
8432
|
+
const chatClaudeMd = join8(projDir, "CLAUDE.md");
|
|
8091
8433
|
if (existsSync5(chatClaudeMd)) {
|
|
8092
8434
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
8093
8435
|
}
|
|
8094
|
-
const envIntPath =
|
|
8436
|
+
const envIntPath = join8(projDir, ".env.integrations");
|
|
8095
8437
|
const childEnv = { ...process.env };
|
|
8096
8438
|
if (existsSync5(envIntPath)) {
|
|
8097
8439
|
try {
|
|
8098
|
-
Object.assign(childEnv, parseEnvIntegrations(
|
|
8440
|
+
Object.assign(childEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
|
|
8099
8441
|
} catch {
|
|
8100
8442
|
}
|
|
8101
8443
|
}
|
|
@@ -8473,12 +8815,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
8473
8815
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
8474
8816
|
try {
|
|
8475
8817
|
const candidates = [
|
|
8476
|
-
|
|
8477
|
-
|
|
8818
|
+
join8(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
8819
|
+
join8(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
8478
8820
|
];
|
|
8479
8821
|
for (const candidate of candidates) {
|
|
8480
8822
|
if (existsSync5(candidate)) {
|
|
8481
|
-
const content =
|
|
8823
|
+
const content = readFileSync9(candidate, "utf-8");
|
|
8482
8824
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
8483
8825
|
builtInSkillCache.set(skillId, files);
|
|
8484
8826
|
return files;
|
|
@@ -9121,7 +9463,7 @@ async function processClaudePairSessions(agents) {
|
|
|
9121
9463
|
killPairSession,
|
|
9122
9464
|
pairTmuxSession,
|
|
9123
9465
|
finalizeClaudePairOnboarding
|
|
9124
|
-
} = await import("../claude-pair-runtime-
|
|
9466
|
+
} = await import("../claude-pair-runtime-DY2LN3ED.js");
|
|
9125
9467
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
9126
9468
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
9127
9469
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -9431,8 +9773,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
9431
9773
|
};
|
|
9432
9774
|
}
|
|
9433
9775
|
async function syncMemories(agent, configDir, log2) {
|
|
9434
|
-
const projectDir =
|
|
9435
|
-
const memoryDir =
|
|
9776
|
+
const projectDir = join8(configDir, agent.code_name, "project");
|
|
9777
|
+
const memoryDir = join8(projectDir, "memory");
|
|
9436
9778
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
9437
9779
|
if (isFreshSync) {
|
|
9438
9780
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -9447,10 +9789,10 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
9447
9789
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
9448
9790
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
9449
9791
|
const changedMemories = [];
|
|
9450
|
-
for (const file of
|
|
9792
|
+
for (const file of readdirSync4(memoryDir)) {
|
|
9451
9793
|
if (!file.endsWith(".md")) continue;
|
|
9452
9794
|
try {
|
|
9453
|
-
const raw =
|
|
9795
|
+
const raw = readFileSync9(join8(memoryDir, file), "utf-8");
|
|
9454
9796
|
const fileHash = createHash3("sha256").update(raw).digest("hex").slice(0, 16);
|
|
9455
9797
|
currentHashes.set(file, fileHash);
|
|
9456
9798
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -9475,7 +9817,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
9475
9817
|
} catch (err) {
|
|
9476
9818
|
for (const mem of changedMemories) {
|
|
9477
9819
|
for (const [file] of currentHashes) {
|
|
9478
|
-
const parsed = parseMemoryFile(
|
|
9820
|
+
const parsed = parseMemoryFile(readFileSync9(join8(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
9479
9821
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
9480
9822
|
}
|
|
9481
9823
|
}
|
|
@@ -9488,7 +9830,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
9488
9830
|
}
|
|
9489
9831
|
}
|
|
9490
9832
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
9491
|
-
const localFiles = existsSync5(memoryDir) ?
|
|
9833
|
+
const localFiles = existsSync5(memoryDir) ? readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
9492
9834
|
const localListHash = createHash3("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
9493
9835
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
9494
9836
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
@@ -9510,7 +9852,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
|
9510
9852
|
const mem = dbMemories.memories[i];
|
|
9511
9853
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
9512
9854
|
const slug = rawSlug || `memory-${i}`;
|
|
9513
|
-
const filePath =
|
|
9855
|
+
const filePath = join8(memoryDir, `${slug}.md`);
|
|
9514
9856
|
const desired = `---
|
|
9515
9857
|
name: ${JSON.stringify(mem.name)}
|
|
9516
9858
|
type: ${mem.type}
|
|
@@ -9522,7 +9864,7 @@ ${mem.content}
|
|
|
9522
9864
|
if (existsSync5(filePath)) {
|
|
9523
9865
|
let existing = "";
|
|
9524
9866
|
try {
|
|
9525
|
-
existing =
|
|
9867
|
+
existing = readFileSync9(filePath, "utf-8");
|
|
9526
9868
|
} catch {
|
|
9527
9869
|
}
|
|
9528
9870
|
if (existing === desired) continue;
|
|
@@ -9534,7 +9876,7 @@ ${mem.content}
|
|
|
9534
9876
|
}
|
|
9535
9877
|
}
|
|
9536
9878
|
if (written > 0 || overwritten > 0) {
|
|
9537
|
-
const updatedFiles =
|
|
9879
|
+
const updatedFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
9538
9880
|
lastLocalFileHash.set(agent.agent_id, createHash3("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
9539
9881
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
9540
9882
|
}
|
|
@@ -9736,11 +10078,11 @@ function startManager(opts) {
|
|
|
9736
10078
|
try {
|
|
9737
10079
|
const stateFile = getStateFile();
|
|
9738
10080
|
if (existsSync5(stateFile)) {
|
|
9739
|
-
const raw =
|
|
10081
|
+
const raw = readFileSync9(stateFile, "utf-8");
|
|
9740
10082
|
const parsed = JSON.parse(raw);
|
|
9741
10083
|
if (Array.isArray(parsed.agents)) {
|
|
9742
|
-
|
|
9743
|
-
log(`[startup] rehydrated ${
|
|
10084
|
+
state5.agents = parsed.agents;
|
|
10085
|
+
log(`[startup] rehydrated ${state5.agents.length} agent state(s) from ${stateFile}`);
|
|
9744
10086
|
}
|
|
9745
10087
|
if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
|
|
9746
10088
|
restartBreaker.hydrate(parsed.circuitBreakerTrips);
|
|
@@ -9752,7 +10094,7 @@ function startManager(opts) {
|
|
|
9752
10094
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
9753
10095
|
}
|
|
9754
10096
|
log(
|
|
9755
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
10097
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join8(homedir4(), ".augmented", "manager.log")}`
|
|
9756
10098
|
);
|
|
9757
10099
|
deployMcpAssets();
|
|
9758
10100
|
reapOrphanChannelMcps({ log });
|
|
@@ -9773,7 +10115,7 @@ async function reapOrphanedClaudePids() {
|
|
|
9773
10115
|
const looksLikeClaude = (pid) => {
|
|
9774
10116
|
if (process.platform !== "linux") return true;
|
|
9775
10117
|
try {
|
|
9776
|
-
const comm =
|
|
10118
|
+
const comm = readFileSync9(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
9777
10119
|
return comm.includes("claude");
|
|
9778
10120
|
} catch {
|
|
9779
10121
|
return false;
|
|
@@ -9883,14 +10225,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
9883
10225
|
}
|
|
9884
10226
|
}
|
|
9885
10227
|
function deployMcpAssets() {
|
|
9886
|
-
const targetDir =
|
|
10228
|
+
const targetDir = join8(homedir4(), ".augmented", "_mcp");
|
|
9887
10229
|
mkdirSync4(targetDir, { recursive: true });
|
|
9888
10230
|
const moduleDir = dirname3(fileURLToPath(import.meta.url));
|
|
9889
10231
|
let mcpSourceDir = "";
|
|
9890
10232
|
let dir = moduleDir;
|
|
9891
10233
|
for (let i = 0; i < 6; i++) {
|
|
9892
|
-
const candidate =
|
|
9893
|
-
if (existsSync5(
|
|
10234
|
+
const candidate = join8(dir, "dist", "mcp");
|
|
10235
|
+
if (existsSync5(join8(candidate, "index.js"))) {
|
|
9894
10236
|
mcpSourceDir = candidate;
|
|
9895
10237
|
break;
|
|
9896
10238
|
}
|
|
@@ -9906,7 +10248,7 @@ function deployMcpAssets() {
|
|
|
9906
10248
|
const fileHash = (p) => {
|
|
9907
10249
|
try {
|
|
9908
10250
|
if (!existsSync5(p)) return null;
|
|
9909
|
-
return createHash3("sha256").update(
|
|
10251
|
+
return createHash3("sha256").update(readFileSync9(p)).digest("hex");
|
|
9910
10252
|
} catch {
|
|
9911
10253
|
return null;
|
|
9912
10254
|
}
|
|
@@ -9927,8 +10269,8 @@ function deployMcpAssets() {
|
|
|
9927
10269
|
"teams-channel.js"
|
|
9928
10270
|
// ENG-6019
|
|
9929
10271
|
]) {
|
|
9930
|
-
const src =
|
|
9931
|
-
const dst =
|
|
10272
|
+
const src = join8(mcpSourceDir, file);
|
|
10273
|
+
const dst = join8(targetDir, file);
|
|
9932
10274
|
if (!existsSync5(src)) continue;
|
|
9933
10275
|
const before = fileHash(dst);
|
|
9934
10276
|
try {
|
|
@@ -9946,16 +10288,16 @@ function deployMcpAssets() {
|
|
|
9946
10288
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
9947
10289
|
restartRunningChannelMcps(changedBasenames);
|
|
9948
10290
|
}
|
|
9949
|
-
const localMcpPath =
|
|
10291
|
+
const localMcpPath = join8(targetDir, "index.js");
|
|
9950
10292
|
try {
|
|
9951
|
-
const agentsDir =
|
|
10293
|
+
const agentsDir = join8(homedir4(), ".augmented", "agents");
|
|
9952
10294
|
if (existsSync5(agentsDir)) {
|
|
9953
|
-
for (const entry of
|
|
10295
|
+
for (const entry of readdirSync4(agentsDir, { withFileTypes: true })) {
|
|
9954
10296
|
if (!entry.isDirectory()) continue;
|
|
9955
10297
|
for (const subdir of ["provision", "project"]) {
|
|
9956
|
-
const mcpJsonPath =
|
|
10298
|
+
const mcpJsonPath = join8(agentsDir, entry.name, subdir, ".mcp.json");
|
|
9957
10299
|
try {
|
|
9958
|
-
const raw =
|
|
10300
|
+
const raw = readFileSync9(mcpJsonPath, "utf-8");
|
|
9959
10301
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
9960
10302
|
const mcpConfig = JSON.parse(raw);
|
|
9961
10303
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|