@integrity-labs/agt-cli 0.27.85 → 0.27.87
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 +3 -3
- package/dist/{chunk-76NYF2QN.js → chunk-2E42P2IO.js} +1 -1
- package/dist/{chunk-4LHN3FAL.js → chunk-7EKFVCGY.js} +6 -1
- package/dist/{chunk-4LHN3FAL.js.map → chunk-7EKFVCGY.js.map} +1 -1
- package/dist/{claude-pair-runtime-DY2LN3ED.js → claude-pair-runtime-WTGNQXCL.js} +2 -2
- package/dist/lib/manager-worker.js +581 -233
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/{persistent-session-YNVCVUK3.js → persistent-session-IADHTYFL.js} +2 -2
- package/dist/{responsiveness-probe-5ICEBNCM.js → responsiveness-probe-3IY27CNE.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-76NYF2QN.js.map → chunk-2E42P2IO.js.map} +0 -0
- /package/dist/{claude-pair-runtime-DY2LN3ED.js.map → claude-pair-runtime-WTGNQXCL.js.map} +0 -0
- /package/dist/{persistent-session-YNVCVUK3.js.map → persistent-session-IADHTYFL.js.map} +0 -0
- /package/dist/{responsiveness-probe-5ICEBNCM.js.map → responsiveness-probe-3IY27CNE.js.map} +0 -0
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
provisionStopHook,
|
|
17
17
|
requireHost,
|
|
18
18
|
safeWriteJsonAtomic
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-2E42P2IO.js";
|
|
20
20
|
import {
|
|
21
21
|
getProjectDir as getProjectDir2,
|
|
22
22
|
getReadyTasks,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
import {
|
|
28
28
|
buildAllowedTools,
|
|
29
29
|
checkChannelInputs,
|
|
30
|
+
creditWatchdogGiveUpCount,
|
|
30
31
|
formatMissingVar,
|
|
31
32
|
getLastFailureContext,
|
|
32
33
|
getProjectDir,
|
|
@@ -53,7 +54,7 @@ import {
|
|
|
53
54
|
stopPersistentSession,
|
|
54
55
|
takeWatchdogGiveUpCount,
|
|
55
56
|
takeZombieDetection
|
|
56
|
-
} from "../chunk-
|
|
57
|
+
} from "../chunk-7EKFVCGY.js";
|
|
57
58
|
import {
|
|
58
59
|
KANBAN_CHECK_COMMAND,
|
|
59
60
|
appendDmFooter,
|
|
@@ -84,10 +85,10 @@ import {
|
|
|
84
85
|
|
|
85
86
|
// src/lib/manager-worker.ts
|
|
86
87
|
import { createHash as createHash3 } from "crypto";
|
|
87
|
-
import { readFileSync as
|
|
88
|
+
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
89
|
import https from "https";
|
|
89
90
|
import { execFileSync as syncExecFile } from "child_process";
|
|
90
|
-
import { join as
|
|
91
|
+
import { join as join8, dirname as dirname3 } from "path";
|
|
91
92
|
import { homedir as homedir4 } from "os";
|
|
92
93
|
import { fileURLToPath } from "url";
|
|
93
94
|
|
|
@@ -473,14 +474,14 @@ function reapMissingMcpSessions(args) {
|
|
|
473
474
|
if (!missingRaw.includes(key)) liveKeys.add(key);
|
|
474
475
|
}
|
|
475
476
|
for (const key of liveKeys) {
|
|
476
|
-
const
|
|
477
|
-
if (
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
477
|
+
const state6 = presenceReaperState.get(stateKey(codeName, key));
|
|
478
|
+
if (state6) {
|
|
479
|
+
state6.attempts = 0;
|
|
480
|
+
state6.lastSeenLiveAt = now();
|
|
481
|
+
state6.lastAttemptedSessionStartedAt = null;
|
|
482
|
+
state6.gaveUpLogged = false;
|
|
483
|
+
state6.firstMissingAt = null;
|
|
484
|
+
state6.quarantineLogged = false;
|
|
484
485
|
}
|
|
485
486
|
}
|
|
486
487
|
if (missing.length === 0) {
|
|
@@ -498,7 +499,7 @@ function reapMissingMcpSessions(args) {
|
|
|
498
499
|
const wouldQuarantine = [];
|
|
499
500
|
for (const key of missing) {
|
|
500
501
|
const sk = stateKey(codeName, key);
|
|
501
|
-
const
|
|
502
|
+
const state6 = presenceReaperState.get(sk) ?? {
|
|
502
503
|
attempts: 0,
|
|
503
504
|
lastSeenLiveAt: null,
|
|
504
505
|
lastAttemptedSessionStartedAt: null,
|
|
@@ -506,19 +507,19 @@ function reapMissingMcpSessions(args) {
|
|
|
506
507
|
firstMissingAt: null,
|
|
507
508
|
quarantineLogged: false
|
|
508
509
|
};
|
|
509
|
-
if (
|
|
510
|
-
if (
|
|
511
|
-
|
|
512
|
-
|
|
510
|
+
if (state6.firstMissingAt === null) state6.firstMissingAt = nowMs;
|
|
511
|
+
if (state6.lastAttemptedSessionStartedAt !== sessionStartedAt) {
|
|
512
|
+
state6.attempts += 1;
|
|
513
|
+
state6.lastAttemptedSessionStartedAt = sessionStartedAt;
|
|
513
514
|
}
|
|
514
|
-
presenceReaperState.set(sk,
|
|
515
|
-
if (
|
|
515
|
+
presenceReaperState.set(sk, state6);
|
|
516
|
+
if (state6.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
|
|
516
517
|
givenUp.push(key);
|
|
517
|
-
if (!
|
|
518
|
+
if (!state6.gaveUpLogged) {
|
|
518
519
|
log2(
|
|
519
520
|
`[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
521
|
);
|
|
521
|
-
|
|
522
|
+
state6.gaveUpLogged = true;
|
|
522
523
|
if (onGiveUp) {
|
|
523
524
|
try {
|
|
524
525
|
onGiveUp(codeName, key);
|
|
@@ -529,10 +530,10 @@ function reapMissingMcpSessions(args) {
|
|
|
529
530
|
}
|
|
530
531
|
}
|
|
531
532
|
}
|
|
532
|
-
if (quarantineMode !== "off" && !
|
|
533
|
-
const dwellElapsed = quarantineDwellMs <= 0 ||
|
|
533
|
+
if (quarantineMode !== "off" && !state6.quarantineLogged && classifyKey(key) === "optional") {
|
|
534
|
+
const dwellElapsed = quarantineDwellMs <= 0 || state6.firstMissingAt !== null && nowMs - state6.firstMissingAt >= quarantineDwellMs;
|
|
534
535
|
if (dwellElapsed) {
|
|
535
|
-
|
|
536
|
+
state6.quarantineLogged = true;
|
|
536
537
|
if (quarantineMode === "enforce") {
|
|
537
538
|
quarantined.push(key);
|
|
538
539
|
log2(
|
|
@@ -1647,14 +1648,272 @@ async function maybeReportTokenUsage(args) {
|
|
|
1647
1648
|
state2.set(codeName, next);
|
|
1648
1649
|
}
|
|
1649
1650
|
|
|
1651
|
+
// src/lib/conversation-evaluator.ts
|
|
1652
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
1653
|
+
import { join as join3 } from "path";
|
|
1654
|
+
var MIN_CHECK_INTERVAL_MS3 = 5 * 6e4;
|
|
1655
|
+
var TRANSCRIPT_MTIME_WINDOW_MS2 = 3 * 24 * 60 * 60 * 1e3;
|
|
1656
|
+
var WINDOW_PAD_MS = 5 * 6e4;
|
|
1657
|
+
var MAX_TURN_CHARS = 1500;
|
|
1658
|
+
var MAX_TRANSCRIPT_CHARS = 6e3;
|
|
1659
|
+
var DEFAULT_CLAUDE_EVAL_MODEL = "claude-haiku-4-5-20251001";
|
|
1660
|
+
var DEFAULT_LOCAL_EVAL_URL = "http://localhost:11434/v1/chat/completions";
|
|
1661
|
+
var DEFAULT_LOCAL_EVAL_MODEL = "gemma4:12b";
|
|
1662
|
+
var EVAL_TIMEOUT_MS = 12e4;
|
|
1663
|
+
async function runLocalEvalChat(prompt, opts) {
|
|
1664
|
+
const endpoint = new URL(opts.url);
|
|
1665
|
+
const hostname = endpoint.hostname.toLowerCase();
|
|
1666
|
+
const isLoopback = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
1667
|
+
if (!isLoopback) {
|
|
1668
|
+
throw new Error(`local eval url must be loopback-only, got host '${endpoint.hostname}'`);
|
|
1669
|
+
}
|
|
1670
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
1671
|
+
const res = await doFetch(opts.url, {
|
|
1672
|
+
method: "POST",
|
|
1673
|
+
headers: {
|
|
1674
|
+
"Content-Type": "application/json",
|
|
1675
|
+
...opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : {}
|
|
1676
|
+
},
|
|
1677
|
+
body: JSON.stringify({
|
|
1678
|
+
model: opts.model,
|
|
1679
|
+
messages: [{ role: "user", content: prompt }],
|
|
1680
|
+
temperature: 0,
|
|
1681
|
+
max_tokens: 512,
|
|
1682
|
+
stream: false
|
|
1683
|
+
}),
|
|
1684
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? EVAL_TIMEOUT_MS)
|
|
1685
|
+
});
|
|
1686
|
+
if (!res.ok) {
|
|
1687
|
+
throw new Error(`local eval endpoint ${opts.url} returned ${res.status}`);
|
|
1688
|
+
}
|
|
1689
|
+
const data = await res.json();
|
|
1690
|
+
const text = data.choices?.[0]?.message?.content;
|
|
1691
|
+
if (typeof text !== "string" || !text.trim()) {
|
|
1692
|
+
throw new Error("local eval endpoint returned no message content");
|
|
1693
|
+
}
|
|
1694
|
+
return text;
|
|
1695
|
+
}
|
|
1696
|
+
var state3 = /* @__PURE__ */ new Map();
|
|
1697
|
+
function channelRefTokens(channelRef) {
|
|
1698
|
+
return channelRef.split(":").slice(1).filter((p) => p && p !== "dm");
|
|
1699
|
+
}
|
|
1700
|
+
function contentToText(content) {
|
|
1701
|
+
if (typeof content === "string") return content;
|
|
1702
|
+
if (Array.isArray(content)) {
|
|
1703
|
+
return content.map((b) => {
|
|
1704
|
+
if (typeof b === "string") return b;
|
|
1705
|
+
if (b && typeof b === "object" && b.type === "text") {
|
|
1706
|
+
return String(b.text ?? "");
|
|
1707
|
+
}
|
|
1708
|
+
return "";
|
|
1709
|
+
}).filter(Boolean).join(" ");
|
|
1710
|
+
}
|
|
1711
|
+
return "";
|
|
1712
|
+
}
|
|
1713
|
+
function parseTranscriptTurns(jsonl) {
|
|
1714
|
+
const turns = [];
|
|
1715
|
+
for (const line of jsonl.split("\n")) {
|
|
1716
|
+
const trimmed = line.trim();
|
|
1717
|
+
if (!trimmed) continue;
|
|
1718
|
+
let obj;
|
|
1719
|
+
try {
|
|
1720
|
+
obj = JSON.parse(trimmed);
|
|
1721
|
+
} catch {
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
const role = obj.type === "user" || obj.type === "assistant" ? obj.type : void 0;
|
|
1725
|
+
if (!role) continue;
|
|
1726
|
+
const text = contentToText(obj.message?.content).trim();
|
|
1727
|
+
if (!text) continue;
|
|
1728
|
+
const ts = obj.timestamp ? Date.parse(obj.timestamp) : NaN;
|
|
1729
|
+
turns.push({ role, text, ts: Number.isFinite(ts) ? ts : 0 });
|
|
1730
|
+
}
|
|
1731
|
+
return turns;
|
|
1732
|
+
}
|
|
1733
|
+
function stripChannelTag(text) {
|
|
1734
|
+
return text.replace(/<channel\b[^>]*\/?>/i, "").replace(/<\/channel>/i, "").trim();
|
|
1735
|
+
}
|
|
1736
|
+
function reconstructConversation(allTurns, tokens, windowStartMs, windowEndMs) {
|
|
1737
|
+
if (tokens.length === 0) return [];
|
|
1738
|
+
const sorted = [...allTurns].sort((a, b) => a.ts - b.ts);
|
|
1739
|
+
const out = [];
|
|
1740
|
+
let inConversation = false;
|
|
1741
|
+
for (const turn of sorted) {
|
|
1742
|
+
if (turn.ts && (turn.ts < windowStartMs || turn.ts > windowEndMs)) continue;
|
|
1743
|
+
if (turn.role === "user") {
|
|
1744
|
+
inConversation = tokens.every((t) => turn.text.includes(t));
|
|
1745
|
+
if (inConversation) {
|
|
1746
|
+
out.push({ ...turn, text: stripChannelTag(turn.text) });
|
|
1747
|
+
}
|
|
1748
|
+
} else if (inConversation) {
|
|
1749
|
+
out.push(turn);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
return out;
|
|
1753
|
+
}
|
|
1754
|
+
function renderTranscript(turns) {
|
|
1755
|
+
const lines = [];
|
|
1756
|
+
let total = 0;
|
|
1757
|
+
for (const turn of turns) {
|
|
1758
|
+
const label = turn.role === "user" ? "User" : "Agent";
|
|
1759
|
+
const body = turn.text.length > MAX_TURN_CHARS ? `${turn.text.slice(0, MAX_TURN_CHARS)}\u2026` : turn.text;
|
|
1760
|
+
const line = `${label}: ${body}`;
|
|
1761
|
+
if (total + line.length > MAX_TRANSCRIPT_CHARS) break;
|
|
1762
|
+
lines.push(line);
|
|
1763
|
+
total += line.length;
|
|
1764
|
+
}
|
|
1765
|
+
return lines.join("\n\n");
|
|
1766
|
+
}
|
|
1767
|
+
function buildEvalPrompt(channel, transcript) {
|
|
1768
|
+
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.
|
|
1769
|
+
|
|
1770
|
+
Conversation transcript:
|
|
1771
|
+
"""
|
|
1772
|
+
${transcript}
|
|
1773
|
+
"""
|
|
1774
|
+
|
|
1775
|
+
Score the agent's success:
|
|
1776
|
+
- score: integer 0-100 (0 = ignored/unhelpful/wrong, 100 = fully resolved the user's need)
|
|
1777
|
+
- verdict: "success" (need clearly met), "partial" (some help but incomplete/ambiguous), or "failure" (did not help / made it worse)
|
|
1778
|
+
- summary: ONE short sentence (max 140 chars) explaining the score. Do NOT quote sensitive user content.
|
|
1779
|
+
|
|
1780
|
+
Respond with ONLY a JSON object, no other text:
|
|
1781
|
+
{"score": 0-100, "verdict": "success|partial|failure", "summary": "..."}`;
|
|
1782
|
+
}
|
|
1783
|
+
function parseVerdict(raw) {
|
|
1784
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
1785
|
+
if (!match) return null;
|
|
1786
|
+
let obj;
|
|
1787
|
+
try {
|
|
1788
|
+
obj = JSON.parse(match[0]);
|
|
1789
|
+
} catch {
|
|
1790
|
+
return null;
|
|
1791
|
+
}
|
|
1792
|
+
const score = typeof obj.score === "number" ? obj.score : Number(obj.score);
|
|
1793
|
+
if (!Number.isFinite(score) || score < 0 || score > 100) return null;
|
|
1794
|
+
if (obj.verdict !== "success" && obj.verdict !== "partial" && obj.verdict !== "failure") return null;
|
|
1795
|
+
const summary = typeof obj.summary === "string" ? obj.summary.slice(0, 140) : "";
|
|
1796
|
+
return { score: Math.round(score), verdict: obj.verdict, summary };
|
|
1797
|
+
}
|
|
1798
|
+
async function maybeEvaluateConversations(args) {
|
|
1799
|
+
const { api: api2, backend, codeName, agentId, log: log2 } = args;
|
|
1800
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
1801
|
+
const nowMs = now.getTime();
|
|
1802
|
+
const existing = state3.get(codeName);
|
|
1803
|
+
if (existing && nowMs - existing.lastCheckedAt < MIN_CHECK_INTERVAL_MS3) {
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
state3.set(codeName, { lastCheckedAt: nowMs });
|
|
1807
|
+
let pending;
|
|
1808
|
+
try {
|
|
1809
|
+
const resp = await api2.get(
|
|
1810
|
+
`/host/conversations/pending-evaluation?agent_id=${encodeURIComponent(agentId)}`
|
|
1811
|
+
);
|
|
1812
|
+
pending = resp?.conversations ?? [];
|
|
1813
|
+
} catch (err) {
|
|
1814
|
+
log2(`[conversation-eval] ${codeName}: pending fetch failed: ${err.message}`);
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
if (pending.length === 0) return;
|
|
1818
|
+
const dir = args.transcriptDir ?? sessionTranscriptDir(getProjectDir(codeName));
|
|
1819
|
+
const allTurns = readRecentTurns(dir, nowMs);
|
|
1820
|
+
if (allTurns.length === 0) {
|
|
1821
|
+
for (const conv of pending) {
|
|
1822
|
+
await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
|
|
1823
|
+
}
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
for (const conv of pending) {
|
|
1827
|
+
const tokens = channelRefTokens(conv.channel_ref);
|
|
1828
|
+
const windowStart = Date.parse(conv.started_at) - WINDOW_PAD_MS;
|
|
1829
|
+
const windowEnd = Date.parse(conv.last_message_at) + WINDOW_PAD_MS;
|
|
1830
|
+
const turns = reconstructConversation(allTurns, tokens, windowStart, windowEnd);
|
|
1831
|
+
if (turns.length === 0) {
|
|
1832
|
+
await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
|
|
1833
|
+
continue;
|
|
1834
|
+
}
|
|
1835
|
+
const transcript = renderTranscript(turns);
|
|
1836
|
+
if (!transcript.trim()) {
|
|
1837
|
+
await reportSkip(api2, agentId, conv.conversation_id, log2, codeName);
|
|
1838
|
+
continue;
|
|
1839
|
+
}
|
|
1840
|
+
let verdict;
|
|
1841
|
+
try {
|
|
1842
|
+
const out = await backend.run(buildEvalPrompt(conv.channel, transcript));
|
|
1843
|
+
verdict = parseVerdict(out);
|
|
1844
|
+
} catch (err) {
|
|
1845
|
+
log2(`[conversation-eval] ${codeName}: scoring failed: ${err.message}`);
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
if (!verdict) {
|
|
1849
|
+
log2(`[conversation-eval] ${codeName}: unparseable verdict for ${conv.conversation_id.slice(0, 8)}`);
|
|
1850
|
+
continue;
|
|
1851
|
+
}
|
|
1852
|
+
try {
|
|
1853
|
+
await api2.post("/host/conversations/evaluation", {
|
|
1854
|
+
agent_id: agentId,
|
|
1855
|
+
conversation_id: conv.conversation_id,
|
|
1856
|
+
score: verdict.score,
|
|
1857
|
+
verdict: verdict.verdict,
|
|
1858
|
+
summary: verdict.summary,
|
|
1859
|
+
model: backend.model
|
|
1860
|
+
});
|
|
1861
|
+
log2(
|
|
1862
|
+
`[conversation-eval] ${codeName}: ${conv.conversation_id.slice(0, 8)} \u2192 ${verdict.verdict} (${verdict.score})`
|
|
1863
|
+
);
|
|
1864
|
+
} catch (err) {
|
|
1865
|
+
log2(`[conversation-eval] ${codeName}: report failed: ${err.message}`);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
async function reportSkip(api2, agentId, conversationId, log2, codeName) {
|
|
1870
|
+
try {
|
|
1871
|
+
await api2.post("/host/conversations/evaluation", {
|
|
1872
|
+
agent_id: agentId,
|
|
1873
|
+
conversation_id: conversationId,
|
|
1874
|
+
skipped: true
|
|
1875
|
+
});
|
|
1876
|
+
} catch (err) {
|
|
1877
|
+
log2(`[conversation-eval] ${codeName}: skip report failed: ${err.message}`);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
function readRecentTurns(dir, nowMs) {
|
|
1881
|
+
let entries;
|
|
1882
|
+
try {
|
|
1883
|
+
entries = readdirSync2(dir);
|
|
1884
|
+
} catch {
|
|
1885
|
+
return [];
|
|
1886
|
+
}
|
|
1887
|
+
const turns = [];
|
|
1888
|
+
for (const name of entries) {
|
|
1889
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
1890
|
+
const full = join3(dir, name);
|
|
1891
|
+
let mtimeMs;
|
|
1892
|
+
try {
|
|
1893
|
+
mtimeMs = statSync2(full).mtimeMs;
|
|
1894
|
+
} catch {
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
if (nowMs - mtimeMs > TRANSCRIPT_MTIME_WINDOW_MS2) continue;
|
|
1898
|
+
let content;
|
|
1899
|
+
try {
|
|
1900
|
+
content = readFileSync5(full, "utf8");
|
|
1901
|
+
} catch {
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1904
|
+
turns.push(...parseTranscriptTurns(content));
|
|
1905
|
+
}
|
|
1906
|
+
return turns;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1650
1909
|
// src/lib/activity-cache-monitor.ts
|
|
1651
|
-
import { existsSync as existsSync2, readFileSync as
|
|
1910
|
+
import { existsSync as existsSync2, readFileSync as readFileSync6 } from "fs";
|
|
1652
1911
|
import { homedir } from "os";
|
|
1653
|
-
import { join as
|
|
1654
|
-
var
|
|
1655
|
-
var STATS_CACHE_PATH =
|
|
1912
|
+
import { join as join4 } from "path";
|
|
1913
|
+
var MIN_CHECK_INTERVAL_MS4 = 6e4;
|
|
1914
|
+
var STATS_CACHE_PATH = join4(homedir(), ".claude", "stats-cache.json");
|
|
1656
1915
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
1657
|
-
var
|
|
1916
|
+
var state4 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
1658
1917
|
function selectNewDailyRows(raw, lastObservedDate) {
|
|
1659
1918
|
let parsed;
|
|
1660
1919
|
try {
|
|
@@ -1693,24 +1952,24 @@ async function maybeReportActivityCache(args) {
|
|
|
1693
1952
|
const { api: api2, log: log2 } = args;
|
|
1694
1953
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
1695
1954
|
const nowMs = now.getTime();
|
|
1696
|
-
if (nowMs -
|
|
1697
|
-
|
|
1955
|
+
if (nowMs - state4.lastCheckedAt < MIN_CHECK_INTERVAL_MS4) return;
|
|
1956
|
+
state4.lastCheckedAt = nowMs;
|
|
1698
1957
|
if (!existsSync2(STATS_CACHE_PATH)) {
|
|
1699
1958
|
return;
|
|
1700
1959
|
}
|
|
1701
1960
|
let raw;
|
|
1702
1961
|
try {
|
|
1703
|
-
raw =
|
|
1962
|
+
raw = readFileSync6(STATS_CACHE_PATH, "utf-8");
|
|
1704
1963
|
} catch (err) {
|
|
1705
1964
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
1706
1965
|
return;
|
|
1707
1966
|
}
|
|
1708
|
-
const rows = selectNewDailyRows(raw,
|
|
1967
|
+
const rows = selectNewDailyRows(raw, state4.lastObservedDate);
|
|
1709
1968
|
if (rows.length === 0) return;
|
|
1710
1969
|
for (const row of rows) {
|
|
1711
1970
|
try {
|
|
1712
1971
|
await api2.post("/host/activity-observations", row);
|
|
1713
|
-
|
|
1972
|
+
state4.lastObservedDate = row.date;
|
|
1714
1973
|
} catch (err) {
|
|
1715
1974
|
log2(
|
|
1716
1975
|
`[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
|
|
@@ -2150,7 +2409,7 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
2150
2409
|
// src/lib/claude-auth-detect.ts
|
|
2151
2410
|
import { readFile, readdir } from "fs/promises";
|
|
2152
2411
|
import { homedir as homedir2, platform } from "os";
|
|
2153
|
-
import { join as
|
|
2412
|
+
import { join as join5 } from "path";
|
|
2154
2413
|
import { execFile as execFile2 } from "child_process";
|
|
2155
2414
|
import { promisify } from "util";
|
|
2156
2415
|
var execFileAsync = promisify(execFile2);
|
|
@@ -2165,8 +2424,8 @@ async function detectClaudeAuth() {
|
|
|
2165
2424
|
}
|
|
2166
2425
|
async function findClaudeCredentialsPaths() {
|
|
2167
2426
|
const candidates = [
|
|
2168
|
-
|
|
2169
|
-
|
|
2427
|
+
join5(homedir2(), ".claude", ".credentials.json"),
|
|
2428
|
+
join5(homedir2(), ".claude", "credentials.json")
|
|
2170
2429
|
];
|
|
2171
2430
|
const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
|
|
2172
2431
|
if (isLinuxRoot) {
|
|
@@ -2174,8 +2433,8 @@ async function findClaudeCredentialsPaths() {
|
|
|
2174
2433
|
const entries = await readdir("/home", { withFileTypes: true });
|
|
2175
2434
|
for (const entry of entries) {
|
|
2176
2435
|
if (!entry.isDirectory()) continue;
|
|
2177
|
-
candidates.push(
|
|
2178
|
-
candidates.push(
|
|
2436
|
+
candidates.push(join5("/home", entry.name, ".claude", ".credentials.json"));
|
|
2437
|
+
candidates.push(join5("/home", entry.name, ".claude", "credentials.json"));
|
|
2179
2438
|
}
|
|
2180
2439
|
} catch {
|
|
2181
2440
|
}
|
|
@@ -2267,18 +2526,18 @@ function normalize(value) {
|
|
|
2267
2526
|
}
|
|
2268
2527
|
|
|
2269
2528
|
// src/lib/channel-hash-cache.ts
|
|
2270
|
-
import { existsSync as existsSync3, readFileSync as
|
|
2271
|
-
import { join as
|
|
2529
|
+
import { existsSync as existsSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "fs";
|
|
2530
|
+
import { join as join6 } from "path";
|
|
2272
2531
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
2273
2532
|
function getChannelHashCacheFile(configDir) {
|
|
2274
|
-
return
|
|
2533
|
+
return join6(configDir, CACHE_FILENAME);
|
|
2275
2534
|
}
|
|
2276
2535
|
function loadChannelHashCache(target, configDir) {
|
|
2277
2536
|
const path = getChannelHashCacheFile(configDir);
|
|
2278
2537
|
if (!existsSync3(path)) return;
|
|
2279
2538
|
let parsed;
|
|
2280
2539
|
try {
|
|
2281
|
-
parsed = JSON.parse(
|
|
2540
|
+
parsed = JSON.parse(readFileSync7(path, "utf-8"));
|
|
2282
2541
|
} catch {
|
|
2283
2542
|
return;
|
|
2284
2543
|
}
|
|
@@ -2732,24 +2991,24 @@ function clearAgentState(agentId, codeName) {
|
|
|
2732
2991
|
}
|
|
2733
2992
|
|
|
2734
2993
|
// src/lib/restart-flags.ts
|
|
2735
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as
|
|
2994
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readFileSync as readFileSync8, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2736
2995
|
import { homedir as homedir3 } from "os";
|
|
2737
|
-
import { join as
|
|
2996
|
+
import { join as join7 } from "path";
|
|
2738
2997
|
import { randomUUID } from "crypto";
|
|
2739
2998
|
function restartFlagsDir() {
|
|
2740
|
-
return
|
|
2999
|
+
return join7(homedir3(), ".augmented", "restart-flags");
|
|
2741
3000
|
}
|
|
2742
3001
|
function flagPath(codeName) {
|
|
2743
|
-
return
|
|
3002
|
+
return join7(restartFlagsDir(), `${codeName}.flag`);
|
|
2744
3003
|
}
|
|
2745
3004
|
function readRestartFlags() {
|
|
2746
3005
|
const dir = restartFlagsDir();
|
|
2747
3006
|
if (!existsSync4(dir)) return [];
|
|
2748
3007
|
const out = [];
|
|
2749
|
-
for (const entry of
|
|
3008
|
+
for (const entry of readdirSync3(dir)) {
|
|
2750
3009
|
if (!entry.endsWith(".flag")) continue;
|
|
2751
3010
|
try {
|
|
2752
|
-
const raw =
|
|
3011
|
+
const raw = readFileSync8(join7(dir, entry), "utf8");
|
|
2753
3012
|
const parsed = JSON.parse(raw);
|
|
2754
3013
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
2755
3014
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -3296,8 +3555,8 @@ function applyRestartAcks(args) {
|
|
|
3296
3555
|
var GATEWAY_PORT_BASE = 18800;
|
|
3297
3556
|
var GATEWAY_PORT_STEP = 10;
|
|
3298
3557
|
var GATEWAY_PORT_MAX = 18899;
|
|
3299
|
-
var AUGMENTED_DIR =
|
|
3300
|
-
var GATEWAY_PORTS_FILE =
|
|
3558
|
+
var AUGMENTED_DIR = join8(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
3559
|
+
var GATEWAY_PORTS_FILE = join8(AUGMENTED_DIR, "gateway-ports.json");
|
|
3301
3560
|
var CHANNEL_SWEEP_INTERVAL_MS = (() => {
|
|
3302
3561
|
const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
|
|
3303
3562
|
if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
|
|
@@ -3404,8 +3663,8 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
|
|
|
3404
3663
|
function shouldSkipRevokedCleanup(previousKnownStatus) {
|
|
3405
3664
|
return previousKnownStatus === "revoked";
|
|
3406
3665
|
}
|
|
3407
|
-
function hasRevokedResiduals(
|
|
3408
|
-
return
|
|
3666
|
+
function hasRevokedResiduals(state6) {
|
|
3667
|
+
return state6.gatewayRunning || state6.portAllocated || state6.provisionDirExists;
|
|
3409
3668
|
}
|
|
3410
3669
|
var pendingSessionRestarts = /* @__PURE__ */ new Map();
|
|
3411
3670
|
var restartBreaker = new RestartBreaker();
|
|
@@ -3482,7 +3741,7 @@ function inboundAgeSecondsFor(codeName) {
|
|
|
3482
3741
|
}
|
|
3483
3742
|
function paneLogAgeSecondsFor(codeName) {
|
|
3484
3743
|
try {
|
|
3485
|
-
const mtimeMs =
|
|
3744
|
+
const mtimeMs = statSync3(paneLogPath(codeName)).mtimeMs;
|
|
3486
3745
|
return Math.max(0, Math.floor((Date.now() - mtimeMs) / 1e3));
|
|
3487
3746
|
} catch (err) {
|
|
3488
3747
|
if (err?.code === "ENOENT") return null;
|
|
@@ -3502,7 +3761,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
|
3502
3761
|
var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
3503
3762
|
function projectMcpHash(_codeName, projectDir) {
|
|
3504
3763
|
try {
|
|
3505
|
-
const raw =
|
|
3764
|
+
const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
|
|
3506
3765
|
return createHash3("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
3507
3766
|
} catch {
|
|
3508
3767
|
return null;
|
|
@@ -3510,7 +3769,7 @@ function projectMcpHash(_codeName, projectDir) {
|
|
|
3510
3769
|
}
|
|
3511
3770
|
function projectMcpKeys(_codeName, projectDir) {
|
|
3512
3771
|
try {
|
|
3513
|
-
const raw =
|
|
3772
|
+
const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
|
|
3514
3773
|
const parsed = JSON.parse(raw);
|
|
3515
3774
|
const servers = parsed.mcpServers;
|
|
3516
3775
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -3521,7 +3780,7 @@ function projectMcpKeys(_codeName, projectDir) {
|
|
|
3521
3780
|
}
|
|
3522
3781
|
function readMcpHttpServerConfig(projectDir, serverKey) {
|
|
3523
3782
|
try {
|
|
3524
|
-
const raw =
|
|
3783
|
+
const raw = readFileSync9(join8(projectDir, ".mcp.json"), "utf-8");
|
|
3525
3784
|
const servers = JSON.parse(raw).mcpServers ?? {};
|
|
3526
3785
|
const entry = servers[serverKey];
|
|
3527
3786
|
if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
|
|
@@ -3674,7 +3933,7 @@ var taskDisplayInfo = /* @__PURE__ */ new Map();
|
|
|
3674
3933
|
var agentChannelTokens = /* @__PURE__ */ new Map();
|
|
3675
3934
|
var activeChannels = /* @__PURE__ */ new Map();
|
|
3676
3935
|
var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
|
|
3677
|
-
var
|
|
3936
|
+
var state5 = {
|
|
3678
3937
|
pid: process.pid,
|
|
3679
3938
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3680
3939
|
lastPollAt: null,
|
|
@@ -3713,7 +3972,7 @@ var cachedMaintenanceWindow = null;
|
|
|
3713
3972
|
var lastVersionCheckAt = 0;
|
|
3714
3973
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3715
3974
|
var lastResponsivenessProbeAt = 0;
|
|
3716
|
-
var agtCliVersion = true ? "0.27.
|
|
3975
|
+
var agtCliVersion = true ? "0.27.87" : "dev";
|
|
3717
3976
|
function resolveBrewPath(execFileSync4) {
|
|
3718
3977
|
try {
|
|
3719
3978
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -3890,7 +4149,7 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
3890
4149
|
try {
|
|
3891
4150
|
let settings = {};
|
|
3892
4151
|
if (existsSync5(path)) {
|
|
3893
|
-
const raw =
|
|
4152
|
+
const raw = readFileSync9(path, "utf-8").trim();
|
|
3894
4153
|
if (raw) {
|
|
3895
4154
|
let parsed;
|
|
3896
4155
|
try {
|
|
@@ -3962,7 +4221,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
3962
4221
|
var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3963
4222
|
var claudeCodeUpgradeInFlight = false;
|
|
3964
4223
|
function claudeCodeUpgradeMarkerPath() {
|
|
3965
|
-
return
|
|
4224
|
+
return join8(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
|
|
3966
4225
|
}
|
|
3967
4226
|
function stampClaudeCodeUpgradeMarker() {
|
|
3968
4227
|
try {
|
|
@@ -3972,7 +4231,7 @@ function stampClaudeCodeUpgradeMarker() {
|
|
|
3972
4231
|
}
|
|
3973
4232
|
function claudeCodeUpgradeThrottled() {
|
|
3974
4233
|
try {
|
|
3975
|
-
const lastCheck = parseInt(
|
|
4234
|
+
const lastCheck = parseInt(readFileSync9(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
3976
4235
|
if (!Number.isFinite(lastCheck)) return false;
|
|
3977
4236
|
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
3978
4237
|
} catch {
|
|
@@ -4025,7 +4284,7 @@ ${r.stderr}`;
|
|
|
4025
4284
|
}
|
|
4026
4285
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
4027
4286
|
function selfUpdateAppliedMarkerPath() {
|
|
4028
|
-
return
|
|
4287
|
+
return join8(homedir4(), ".augmented", ".last-self-update-applied");
|
|
4029
4288
|
}
|
|
4030
4289
|
var selfUpdateUpToDateLogged = false;
|
|
4031
4290
|
var restartAfterUpgrade = false;
|
|
@@ -4048,7 +4307,7 @@ async function checkAndUpdateCli() {
|
|
|
4048
4307
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
4049
4308
|
if (!isBrewFormula && !isNpmGlobal) return;
|
|
4050
4309
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
4051
|
-
const markerPath =
|
|
4310
|
+
const markerPath = join8(homedir4(), ".augmented", ".last-update-check");
|
|
4052
4311
|
try {
|
|
4053
4312
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
4054
4313
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
@@ -4305,9 +4564,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
4305
4564
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
4306
4565
|
}
|
|
4307
4566
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
4308
|
-
const claudeDir =
|
|
4567
|
+
const claudeDir = join8(homedir4(), ".claude");
|
|
4309
4568
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
4310
|
-
const p =
|
|
4569
|
+
const p = join8(claudeDir, filename);
|
|
4311
4570
|
if (existsSync5(p)) {
|
|
4312
4571
|
try {
|
|
4313
4572
|
rmSync2(p, { force: true });
|
|
@@ -4320,9 +4579,86 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
4320
4579
|
delete childEnv.ANTHROPIC_API_KEY;
|
|
4321
4580
|
}
|
|
4322
4581
|
}
|
|
4582
|
+
var evalEmptyMcpConfigPath = null;
|
|
4583
|
+
function ensureEvalEmptyMcpConfig() {
|
|
4584
|
+
if (evalEmptyMcpConfigPath && existsSync5(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
|
|
4585
|
+
const dir = join8(homedir4(), ".augmented");
|
|
4586
|
+
try {
|
|
4587
|
+
mkdirSync4(dir, { recursive: true });
|
|
4588
|
+
} catch {
|
|
4589
|
+
}
|
|
4590
|
+
const p = join8(dir, ".eval-empty-mcp.json");
|
|
4591
|
+
writeFileSync4(p, JSON.stringify({ mcpServers: {} }));
|
|
4592
|
+
evalEmptyMcpConfigPath = p;
|
|
4593
|
+
return p;
|
|
4594
|
+
}
|
|
4595
|
+
async function runEvalClaude(prompt, model) {
|
|
4596
|
+
const childEnv = { ...process.env };
|
|
4597
|
+
await applyClaudeAuthToEnv(childEnv, "conversation-eval");
|
|
4598
|
+
const emptyMcp = ensureEvalEmptyMcpConfig();
|
|
4599
|
+
const args = [
|
|
4600
|
+
"-p",
|
|
4601
|
+
prompt,
|
|
4602
|
+
"--model",
|
|
4603
|
+
model,
|
|
4604
|
+
"--output-format",
|
|
4605
|
+
"text",
|
|
4606
|
+
// Isolation: only the empty MCP config, ignore project/user config + no tools.
|
|
4607
|
+
"--mcp-config",
|
|
4608
|
+
emptyMcp,
|
|
4609
|
+
"--strict-mcp-config",
|
|
4610
|
+
"--permission-mode",
|
|
4611
|
+
"auto",
|
|
4612
|
+
"--allowedTools",
|
|
4613
|
+
""
|
|
4614
|
+
];
|
|
4615
|
+
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
|
|
4616
|
+
cwd: homedir4(),
|
|
4617
|
+
timeout: 12e4,
|
|
4618
|
+
stdin: "ignore",
|
|
4619
|
+
env: childEnv,
|
|
4620
|
+
onSpawn: (pid) => registerClaudeSpawn({ pid, started_at: Date.now(), kind: "other" }),
|
|
4621
|
+
onExit: (pid) => unregisterClaudeSpawn(pid)
|
|
4622
|
+
});
|
|
4623
|
+
return stdout;
|
|
4624
|
+
}
|
|
4625
|
+
var conversationEvalBackend = null;
|
|
4626
|
+
function resolveConversationEvalBackend() {
|
|
4627
|
+
if (conversationEvalBackend) return conversationEvalBackend;
|
|
4628
|
+
const kind = (process.env["AGT_CONV_EVAL_BACKEND"] ?? "claude-p").trim().toLowerCase();
|
|
4629
|
+
if (kind === "local") {
|
|
4630
|
+
const url = process.env["AGT_CONV_EVAL_LOCAL_URL"]?.trim() || DEFAULT_LOCAL_EVAL_URL;
|
|
4631
|
+
const model = process.env["AGT_CONV_EVAL_LOCAL_MODEL"]?.trim() || DEFAULT_LOCAL_EVAL_MODEL;
|
|
4632
|
+
const apiKey = process.env["AGT_CONV_EVAL_LOCAL_API_KEY"]?.trim() || void 0;
|
|
4633
|
+
const safeUrl = (() => {
|
|
4634
|
+
try {
|
|
4635
|
+
const parsed = new URL(url);
|
|
4636
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
4637
|
+
} catch {
|
|
4638
|
+
return "[invalid-url]";
|
|
4639
|
+
}
|
|
4640
|
+
})();
|
|
4641
|
+
log(`[conversation-eval] backend=local url=${safeUrl} model=${model}`);
|
|
4642
|
+
conversationEvalBackend = { model, run: (prompt) => runLocalEvalChat(prompt, { url, model, apiKey }) };
|
|
4643
|
+
return conversationEvalBackend;
|
|
4644
|
+
}
|
|
4645
|
+
if (kind === "" || kind === "claude-p") {
|
|
4646
|
+
const model = process.env["AGT_CONV_EVAL_CLAUDE_MODEL"]?.trim() || DEFAULT_CLAUDE_EVAL_MODEL;
|
|
4647
|
+
conversationEvalBackend = { model, run: (prompt) => runEvalClaude(prompt, model) };
|
|
4648
|
+
return conversationEvalBackend;
|
|
4649
|
+
}
|
|
4650
|
+
log(`[conversation-eval] invalid AGT_CONV_EVAL_BACKEND='${kind}' \u2014 expected 'claude-p' or 'local'; evaluation disabled`);
|
|
4651
|
+
conversationEvalBackend = {
|
|
4652
|
+
model: "invalid-conversation-eval-backend",
|
|
4653
|
+
run: async () => {
|
|
4654
|
+
throw new Error(`Unsupported AGT_CONV_EVAL_BACKEND='${kind}'. Expected 'claude-p' or 'local'.`);
|
|
4655
|
+
}
|
|
4656
|
+
};
|
|
4657
|
+
return conversationEvalBackend;
|
|
4658
|
+
}
|
|
4323
4659
|
function loadGatewayPorts() {
|
|
4324
4660
|
try {
|
|
4325
|
-
return JSON.parse(
|
|
4661
|
+
return JSON.parse(readFileSync9(GATEWAY_PORTS_FILE, "utf-8"));
|
|
4326
4662
|
} catch {
|
|
4327
4663
|
return {};
|
|
4328
4664
|
}
|
|
@@ -4352,10 +4688,10 @@ function freePort(codeName) {
|
|
|
4352
4688
|
}
|
|
4353
4689
|
}
|
|
4354
4690
|
function getStateFile() {
|
|
4355
|
-
return
|
|
4691
|
+
return join8(config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
4356
4692
|
}
|
|
4357
4693
|
function channelHashCacheDir() {
|
|
4358
|
-
return config?.configDir ??
|
|
4694
|
+
return config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
4359
4695
|
}
|
|
4360
4696
|
function loadChannelHashCache2() {
|
|
4361
4697
|
loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -4366,7 +4702,7 @@ function saveChannelHashCache2() {
|
|
|
4366
4702
|
var _channelQuarantineStore = null;
|
|
4367
4703
|
function channelQuarantineStore() {
|
|
4368
4704
|
if (!_channelQuarantineStore) {
|
|
4369
|
-
const dir = config?.configDir ??
|
|
4705
|
+
const dir = config?.configDir ?? join8(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
4370
4706
|
_channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
|
|
4371
4707
|
}
|
|
4372
4708
|
return _channelQuarantineStore;
|
|
@@ -4421,7 +4757,7 @@ function log(msg) {
|
|
|
4421
4757
|
`;
|
|
4422
4758
|
if (!managerLogPath) {
|
|
4423
4759
|
try {
|
|
4424
|
-
managerLogPath =
|
|
4760
|
+
managerLogPath = join8(homedir4(), ".augmented", "manager.log");
|
|
4425
4761
|
mkdirSync4(dirname3(managerLogPath), { recursive: true });
|
|
4426
4762
|
if (existsSync5(managerLogPath)) {
|
|
4427
4763
|
chmodSync(managerLogPath, 384);
|
|
@@ -4451,7 +4787,7 @@ function sha256(content) {
|
|
|
4451
4787
|
}
|
|
4452
4788
|
function hashFile(filePath) {
|
|
4453
4789
|
try {
|
|
4454
|
-
const content =
|
|
4790
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
4455
4791
|
return sha256(content);
|
|
4456
4792
|
} catch {
|
|
4457
4793
|
return null;
|
|
@@ -4475,13 +4811,13 @@ function parseSkillFrontmatter(content) {
|
|
|
4475
4811
|
return out;
|
|
4476
4812
|
}
|
|
4477
4813
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
4478
|
-
const { readdirSync:
|
|
4479
|
-
const skillsDir =
|
|
4480
|
-
const claudeMdPath =
|
|
4814
|
+
const { readdirSync: readdirSync5, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync5 } = await import("fs");
|
|
4815
|
+
const skillsDir = join8(configDir, codeName, "project", ".claude", "skills");
|
|
4816
|
+
const claudeMdPath = join8(configDir, codeName, "project", "CLAUDE.md");
|
|
4481
4817
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
4482
4818
|
const entries = [];
|
|
4483
|
-
for (const dir of
|
|
4484
|
-
const skillFile =
|
|
4819
|
+
for (const dir of readdirSync5(skillsDir).sort()) {
|
|
4820
|
+
const skillFile = join8(skillsDir, dir, "SKILL.md");
|
|
4485
4821
|
if (!ex(skillFile)) continue;
|
|
4486
4822
|
try {
|
|
4487
4823
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -4529,10 +4865,10 @@ ${SKILLS_INDEX_END}`;
|
|
|
4529
4865
|
}
|
|
4530
4866
|
async function migrateToProfiles() {
|
|
4531
4867
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4532
|
-
const sharedConfigPath =
|
|
4868
|
+
const sharedConfigPath = join8(homeDir, ".openclaw", "openclaw.json");
|
|
4533
4869
|
let sharedConfig;
|
|
4534
4870
|
try {
|
|
4535
|
-
sharedConfig = JSON.parse(
|
|
4871
|
+
sharedConfig = JSON.parse(readFileSync9(sharedConfigPath, "utf-8"));
|
|
4536
4872
|
} catch {
|
|
4537
4873
|
return;
|
|
4538
4874
|
}
|
|
@@ -4545,19 +4881,19 @@ async function migrateToProfiles() {
|
|
|
4545
4881
|
const codeName = agentEntry["id"];
|
|
4546
4882
|
if (!codeName) continue;
|
|
4547
4883
|
if (codeName === "main") continue;
|
|
4548
|
-
const profileDir =
|
|
4549
|
-
if (existsSync5(
|
|
4884
|
+
const profileDir = join8(homeDir, `.openclaw-${codeName}`);
|
|
4885
|
+
if (existsSync5(join8(profileDir, "openclaw.json"))) continue;
|
|
4550
4886
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
4551
4887
|
if (adapter.seedProfileConfig) {
|
|
4552
4888
|
adapter.seedProfileConfig(codeName);
|
|
4553
4889
|
}
|
|
4554
|
-
const sharedAuthDir =
|
|
4555
|
-
const profileAuthDir =
|
|
4556
|
-
const authFile =
|
|
4890
|
+
const sharedAuthDir = join8(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
4891
|
+
const profileAuthDir = join8(profileDir, "agents", codeName, "agent");
|
|
4892
|
+
const authFile = join8(sharedAuthDir, "auth-profiles.json");
|
|
4557
4893
|
if (existsSync5(authFile)) {
|
|
4558
4894
|
mkdirSync4(profileAuthDir, { recursive: true });
|
|
4559
|
-
const authContent =
|
|
4560
|
-
writeFileSync4(
|
|
4895
|
+
const authContent = readFileSync9(authFile, "utf-8");
|
|
4896
|
+
writeFileSync4(join8(profileAuthDir, "auth-profiles.json"), authContent);
|
|
4561
4897
|
}
|
|
4562
4898
|
allocatePort(codeName);
|
|
4563
4899
|
migrated++;
|
|
@@ -4595,7 +4931,7 @@ function readGatewayToken(codeName) {
|
|
|
4595
4931
|
}
|
|
4596
4932
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4597
4933
|
try {
|
|
4598
|
-
const cfg = JSON.parse(
|
|
4934
|
+
const cfg = JSON.parse(readFileSync9(join8(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
4599
4935
|
return cfg?.gateway?.auth?.token;
|
|
4600
4936
|
} catch {
|
|
4601
4937
|
return void 0;
|
|
@@ -4604,18 +4940,18 @@ function readGatewayToken(codeName) {
|
|
|
4604
4940
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
4605
4941
|
function isGatewayHung(codeName) {
|
|
4606
4942
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4607
|
-
const jobsPath =
|
|
4943
|
+
const jobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
4608
4944
|
if (!existsSync5(jobsPath)) return false;
|
|
4609
4945
|
try {
|
|
4610
|
-
const data = JSON.parse(
|
|
4946
|
+
const data = JSON.parse(readFileSync9(jobsPath, "utf-8"));
|
|
4611
4947
|
const jobs = data.jobs ?? data;
|
|
4612
4948
|
if (!Array.isArray(jobs)) return false;
|
|
4613
4949
|
const now = Date.now();
|
|
4614
4950
|
for (const job of jobs) {
|
|
4615
|
-
const
|
|
4616
|
-
if (!
|
|
4617
|
-
const runStartedAt =
|
|
4618
|
-
const isRunning =
|
|
4951
|
+
const state6 = job.state;
|
|
4952
|
+
if (!state6) continue;
|
|
4953
|
+
const runStartedAt = state6.runStartedAtMs;
|
|
4954
|
+
const isRunning = state6.status === "running" || state6.running === true;
|
|
4619
4955
|
if (isRunning && runStartedAt && now - runStartedAt > GATEWAY_HUNG_TIMEOUT_MS) {
|
|
4620
4956
|
return true;
|
|
4621
4957
|
}
|
|
@@ -4640,15 +4976,15 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4640
4976
|
}
|
|
4641
4977
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
4642
4978
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4643
|
-
const cronJobsPath =
|
|
4979
|
+
const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
4644
4980
|
clearStaleCronRunState(cronJobsPath);
|
|
4645
4981
|
} else {
|
|
4646
4982
|
if (status.port) {
|
|
4647
4983
|
try {
|
|
4648
4984
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4649
|
-
const configPath =
|
|
4985
|
+
const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4650
4986
|
if (existsSync5(configPath)) {
|
|
4651
|
-
const cfg = JSON.parse(
|
|
4987
|
+
const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
|
|
4652
4988
|
if (cfg.gateway?.port !== status.port) {
|
|
4653
4989
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4654
4990
|
cfg.gateway.port = status.port;
|
|
@@ -4672,9 +5008,9 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4672
5008
|
gatewaysStartedThisCycle.add(codeName);
|
|
4673
5009
|
try {
|
|
4674
5010
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4675
|
-
const configPath =
|
|
5011
|
+
const configPath = join8(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4676
5012
|
if (existsSync5(configPath)) {
|
|
4677
|
-
const cfg = JSON.parse(
|
|
5013
|
+
const cfg = JSON.parse(readFileSync9(configPath, "utf-8"));
|
|
4678
5014
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4679
5015
|
cfg.gateway.port = port;
|
|
4680
5016
|
writeFileSync4(configPath, JSON.stringify(cfg, null, 2));
|
|
@@ -4816,7 +5152,7 @@ async function pollCycle() {
|
|
|
4816
5152
|
const now = Date.now();
|
|
4817
5153
|
if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
|
|
4818
5154
|
try {
|
|
4819
|
-
const firstAgent =
|
|
5155
|
+
const firstAgent = state5.agents[0];
|
|
4820
5156
|
const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework("openclaw");
|
|
4821
5157
|
if (versionAdapter.getVersion) {
|
|
4822
5158
|
cachedFrameworkVersion = await versionAdapter.getVersion();
|
|
@@ -4827,7 +5163,7 @@ async function pollCycle() {
|
|
|
4827
5163
|
}
|
|
4828
5164
|
try {
|
|
4829
5165
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
4830
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
5166
|
+
const { collectDiagnostics } = await import("../persistent-session-IADHTYFL.js");
|
|
4831
5167
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
4832
5168
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
4833
5169
|
let tailscaleHostname;
|
|
@@ -4900,18 +5236,23 @@ async function pollCycle() {
|
|
|
4900
5236
|
const {
|
|
4901
5237
|
collectResponsivenessProbes,
|
|
4902
5238
|
getResponsivenessIntervalMs
|
|
4903
|
-
} = await import("../responsiveness-probe-
|
|
5239
|
+
} = await import("../responsiveness-probe-3IY27CNE.js");
|
|
4904
5240
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
4905
5241
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
4906
5242
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
4907
5243
|
if (probeCodeNames.length > 0) {
|
|
4908
|
-
const
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
5244
|
+
const drainedGiveUps = /* @__PURE__ */ new Map();
|
|
5245
|
+
const probes = collectResponsivenessProbes(probeCodeNames).map((p) => {
|
|
5246
|
+
const giveUps = takeWatchdogGiveUpCount(p.code_name);
|
|
5247
|
+
if (giveUps > 0) drainedGiveUps.set(p.code_name, giveUps);
|
|
5248
|
+
return { ...p, input_stuck_give_ups: giveUps };
|
|
5249
|
+
});
|
|
4912
5250
|
if (probes.length > 0) {
|
|
4913
5251
|
void api.post("/host/responsiveness-probe", { host_id: hostId, probes }).catch((err) => {
|
|
4914
|
-
|
|
5252
|
+
for (const [codeName, count] of drainedGiveUps) {
|
|
5253
|
+
creditWatchdogGiveUpCount(codeName, count);
|
|
5254
|
+
}
|
|
5255
|
+
log(`[responsiveness-probe] post failed (give-up counts re-credited): ${err.message}`);
|
|
4915
5256
|
});
|
|
4916
5257
|
}
|
|
4917
5258
|
}
|
|
@@ -4963,7 +5304,7 @@ async function pollCycle() {
|
|
|
4963
5304
|
for (const agent of agents) {
|
|
4964
5305
|
const requested = agent.restart_requested_at ?? null;
|
|
4965
5306
|
if (!requested) continue;
|
|
4966
|
-
const prev =
|
|
5307
|
+
const prev = state5.agents.find((a) => a.agentId === agent.agent_id);
|
|
4967
5308
|
const lastProcessed = prev?.lastRestartProcessedAt ?? null;
|
|
4968
5309
|
if (lastProcessed && Date.parse(lastProcessed) >= Date.parse(requested)) continue;
|
|
4969
5310
|
log(`[restart] Dashboard requested restart for '${agent.code_name}' at ${requested}`);
|
|
@@ -4986,7 +5327,7 @@ async function pollCycle() {
|
|
|
4986
5327
|
await processAgent(agent, agentStates);
|
|
4987
5328
|
} catch (err) {
|
|
4988
5329
|
log(`Error processing agent '${agent.code_name}': ${err.message}`);
|
|
4989
|
-
const existing =
|
|
5330
|
+
const existing = state5.agents.find((a) => a.agentId === agent.agent_id);
|
|
4990
5331
|
if (existing) {
|
|
4991
5332
|
agentStates.push(existing);
|
|
4992
5333
|
} else {
|
|
@@ -5012,12 +5353,12 @@ async function pollCycle() {
|
|
|
5012
5353
|
void maybeReportActivityCache({ api, log });
|
|
5013
5354
|
const restartAckStateChanged = applyRestartAcks({
|
|
5014
5355
|
agentStates,
|
|
5015
|
-
priorAgents:
|
|
5356
|
+
priorAgents: state5.agents,
|
|
5016
5357
|
restartAcks
|
|
5017
5358
|
});
|
|
5018
5359
|
if (restartAckStateChanged) {
|
|
5019
5360
|
try {
|
|
5020
|
-
const ackedState = { ...
|
|
5361
|
+
const ackedState = { ...state5, agents: agentStates };
|
|
5021
5362
|
atomicWriteFileSync(getStateFile(), JSON.stringify(ackedState, null, 2));
|
|
5022
5363
|
} catch (err) {
|
|
5023
5364
|
log(`[restart] failed to persist ack immediately: ${err.message}`);
|
|
@@ -5035,7 +5376,7 @@ async function pollCycle() {
|
|
|
5035
5376
|
} catch {
|
|
5036
5377
|
}
|
|
5037
5378
|
const currentIds = new Set(agents.map((a) => a.agent_id));
|
|
5038
|
-
for (const prev of
|
|
5379
|
+
for (const prev of state5.agents) {
|
|
5039
5380
|
if (!currentIds.has(prev.agentId)) {
|
|
5040
5381
|
log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
|
|
5041
5382
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
@@ -5048,7 +5389,7 @@ async function pollCycle() {
|
|
|
5048
5389
|
}
|
|
5049
5390
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
5050
5391
|
freePort(prev.codeName);
|
|
5051
|
-
const agentDir =
|
|
5392
|
+
const agentDir = join8(adapter.getAgentDir(prev.codeName), "provision");
|
|
5052
5393
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
5053
5394
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
5054
5395
|
}
|
|
@@ -5156,10 +5497,10 @@ async function pollCycle() {
|
|
|
5156
5497
|
}
|
|
5157
5498
|
} catch {
|
|
5158
5499
|
}
|
|
5159
|
-
|
|
5160
|
-
...
|
|
5500
|
+
state5 = {
|
|
5501
|
+
...state5,
|
|
5161
5502
|
lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5162
|
-
pollCount:
|
|
5503
|
+
pollCount: state5.pollCount + 1,
|
|
5163
5504
|
agents: agentStates,
|
|
5164
5505
|
// ENG-5441: serialise trip state on every poll so manager restarts
|
|
5165
5506
|
// never silently clear a tripped breaker. Cheap — only tripped
|
|
@@ -5170,9 +5511,9 @@ async function pollCycle() {
|
|
|
5170
5511
|
log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
|
|
5171
5512
|
consecutivePollFailures = 0;
|
|
5172
5513
|
}
|
|
5173
|
-
send({ type: "state-update", state:
|
|
5514
|
+
send({ type: "state-update", state: state5 });
|
|
5174
5515
|
} catch (err) {
|
|
5175
|
-
|
|
5516
|
+
state5.errorCount++;
|
|
5176
5517
|
const message = err.message;
|
|
5177
5518
|
log(`Poll error: ${message}`);
|
|
5178
5519
|
send({ type: "error", message });
|
|
@@ -5216,10 +5557,17 @@ async function processAgent(agent, agentStates) {
|
|
|
5216
5557
|
agentId: agent.agent_id,
|
|
5217
5558
|
log
|
|
5218
5559
|
});
|
|
5560
|
+
void maybeEvaluateConversations({
|
|
5561
|
+
api,
|
|
5562
|
+
backend: resolveConversationEvalBackend(),
|
|
5563
|
+
codeName: agent.code_name,
|
|
5564
|
+
agentId: agent.agent_id,
|
|
5565
|
+
log
|
|
5566
|
+
});
|
|
5219
5567
|
}
|
|
5220
5568
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5221
5569
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
5222
|
-
let agentDir =
|
|
5570
|
+
let agentDir = join8(adapter.getAgentDir(agent.code_name), "provision");
|
|
5223
5571
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
5224
5572
|
if (previousKnownStatus !== agent.status) {
|
|
5225
5573
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -5340,7 +5688,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5340
5688
|
});
|
|
5341
5689
|
} catch (err) {
|
|
5342
5690
|
log(`Refresh failed for '${agent.code_name}': ${err.message}`);
|
|
5343
|
-
const existing =
|
|
5691
|
+
const existing = state5.agents.find((a) => a.agentId === agent.agent_id);
|
|
5344
5692
|
agentStates.push(existing ?? {
|
|
5345
5693
|
agentId: agent.agent_id,
|
|
5346
5694
|
codeName: agent.code_name,
|
|
@@ -5388,7 +5736,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5388
5736
|
const frameworkId = refreshData.agent.framework ?? "openclaw";
|
|
5389
5737
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
5390
5738
|
const frameworkAdapter = getFramework(frameworkId);
|
|
5391
|
-
agentDir =
|
|
5739
|
+
agentDir = join8(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
5392
5740
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
5393
5741
|
if (frameworkAdapter.migrateSecretStorage && !migratedSecretStorage.has(agent.code_name)) {
|
|
5394
5742
|
try {
|
|
@@ -5404,7 +5752,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5404
5752
|
const charterVersion = refreshData.charter.version;
|
|
5405
5753
|
const toolsVersion = refreshData.tools.version;
|
|
5406
5754
|
const known = agentState.knownVersions.get(agent.agent_id);
|
|
5407
|
-
let lastProvisionAt =
|
|
5755
|
+
let lastProvisionAt = state5.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
|
|
5408
5756
|
const quarantinedChannels = channelQuarantineStore().getQuarantinedKeys(agent.code_name);
|
|
5409
5757
|
const currentChannelIds = setWithout(
|
|
5410
5758
|
launchableChannelIds(refreshData.channel_configs),
|
|
@@ -5431,7 +5779,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5431
5779
|
const changedFiles = [];
|
|
5432
5780
|
mkdirSync4(agentDir, { recursive: true });
|
|
5433
5781
|
for (const artifact of artifacts) {
|
|
5434
|
-
const filePath =
|
|
5782
|
+
const filePath = join8(agentDir, artifact.relativePath);
|
|
5435
5783
|
let existingHash;
|
|
5436
5784
|
let newHash;
|
|
5437
5785
|
let writeContent = artifact.content;
|
|
@@ -5450,8 +5798,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5450
5798
|
};
|
|
5451
5799
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
5452
5800
|
try {
|
|
5453
|
-
const projectClaudeMd =
|
|
5454
|
-
const existing =
|
|
5801
|
+
const projectClaudeMd = join8(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
5802
|
+
const existing = readFileSync9(projectClaudeMd, "utf-8");
|
|
5455
5803
|
existingHash = sha256(stripDynamicSections(existing));
|
|
5456
5804
|
} catch {
|
|
5457
5805
|
existingHash = null;
|
|
@@ -5469,7 +5817,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5469
5817
|
const generatorKeys = Object.keys(generatorServers);
|
|
5470
5818
|
let existingRaw = "";
|
|
5471
5819
|
try {
|
|
5472
|
-
existingRaw =
|
|
5820
|
+
existingRaw = readFileSync9(filePath, "utf-8");
|
|
5473
5821
|
} catch {
|
|
5474
5822
|
}
|
|
5475
5823
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -5491,12 +5839,12 @@ async function processAgent(agent, agentStates) {
|
|
|
5491
5839
|
}
|
|
5492
5840
|
}
|
|
5493
5841
|
if (changedFiles.length > 0) {
|
|
5494
|
-
const isFirst = !existsSync5(
|
|
5842
|
+
const isFirst = !existsSync5(join8(agentDir, "CHARTER.md"));
|
|
5495
5843
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
5496
5844
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
5497
5845
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
5498
5846
|
for (const file of changedFiles) {
|
|
5499
|
-
const filePath =
|
|
5847
|
+
const filePath = join8(agentDir, file.relativePath);
|
|
5500
5848
|
mkdirSync4(dirname3(filePath), { recursive: true });
|
|
5501
5849
|
if (file.relativePath === ".mcp.json") {
|
|
5502
5850
|
safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
|
|
@@ -5505,12 +5853,12 @@ async function processAgent(agent, agentStates) {
|
|
|
5505
5853
|
}
|
|
5506
5854
|
}
|
|
5507
5855
|
try {
|
|
5508
|
-
const provSkillsDir =
|
|
5856
|
+
const provSkillsDir = join8(agentDir, ".claude", "skills");
|
|
5509
5857
|
if (existsSync5(provSkillsDir)) {
|
|
5510
|
-
for (const folder of
|
|
5858
|
+
for (const folder of readdirSync4(provSkillsDir)) {
|
|
5511
5859
|
if (folder.startsWith("knowledge-")) {
|
|
5512
5860
|
try {
|
|
5513
|
-
rmSync2(
|
|
5861
|
+
rmSync2(join8(provSkillsDir, folder), { recursive: true });
|
|
5514
5862
|
} catch {
|
|
5515
5863
|
}
|
|
5516
5864
|
}
|
|
@@ -5523,7 +5871,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5523
5871
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
5524
5872
|
const hashes = /* @__PURE__ */ new Map();
|
|
5525
5873
|
for (const file of trackedFiles2) {
|
|
5526
|
-
const h = hashFile(
|
|
5874
|
+
const h = hashFile(join8(agentDir, file));
|
|
5527
5875
|
if (h) hashes.set(file, h);
|
|
5528
5876
|
}
|
|
5529
5877
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -5591,7 +5939,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5591
5939
|
if (written && existsSync5(agentDir)) {
|
|
5592
5940
|
const driftedFiles = [];
|
|
5593
5941
|
for (const [file, expectedHash] of written) {
|
|
5594
|
-
const localHash = hashFile(
|
|
5942
|
+
const localHash = hashFile(join8(agentDir, file));
|
|
5595
5943
|
if (localHash && localHash !== expectedHash) {
|
|
5596
5944
|
driftedFiles.push(file);
|
|
5597
5945
|
}
|
|
@@ -5602,7 +5950,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5602
5950
|
try {
|
|
5603
5951
|
const localHashes = {};
|
|
5604
5952
|
for (const file of driftedFiles) {
|
|
5605
|
-
localHashes[file] = hashFile(
|
|
5953
|
+
localHashes[file] = hashFile(join8(agentDir, file));
|
|
5606
5954
|
}
|
|
5607
5955
|
await api.post("/host/drift", {
|
|
5608
5956
|
agent_id: agent.agent_id,
|
|
@@ -5865,18 +6213,18 @@ async function processAgent(agent, agentStates) {
|
|
|
5865
6213
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
5866
6214
|
try {
|
|
5867
6215
|
const agentProvisionDir = agentDir;
|
|
5868
|
-
const projectDir =
|
|
6216
|
+
const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
|
|
5869
6217
|
mkdirSync4(agentProvisionDir, { recursive: true });
|
|
5870
6218
|
mkdirSync4(projectDir, { recursive: true });
|
|
5871
|
-
const provisionMcpPath =
|
|
5872
|
-
const projectMcpPath =
|
|
6219
|
+
const provisionMcpPath = join8(agentProvisionDir, ".mcp.json");
|
|
6220
|
+
const projectMcpPath = join8(projectDir, ".mcp.json");
|
|
5873
6221
|
let mcpConfig = { mcpServers: {} };
|
|
5874
6222
|
try {
|
|
5875
|
-
mcpConfig = JSON.parse(
|
|
6223
|
+
mcpConfig = JSON.parse(readFileSync9(provisionMcpPath, "utf-8"));
|
|
5876
6224
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
5877
6225
|
} catch {
|
|
5878
6226
|
}
|
|
5879
|
-
const localDirectChatChannel =
|
|
6227
|
+
const localDirectChatChannel = join8(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
5880
6228
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
5881
6229
|
const directChatTz = (() => {
|
|
5882
6230
|
const tz = directChatTeamSettings?.["timezone"];
|
|
@@ -5906,7 +6254,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5906
6254
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
5907
6255
|
}
|
|
5908
6256
|
}
|
|
5909
|
-
const staleChannelsPath =
|
|
6257
|
+
const staleChannelsPath = join8(projectDir, ".mcp-channels.json");
|
|
5910
6258
|
if (existsSync5(staleChannelsPath)) {
|
|
5911
6259
|
try {
|
|
5912
6260
|
rmSync2(staleChannelsPath, { force: true });
|
|
@@ -5917,7 +6265,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5917
6265
|
log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
|
|
5918
6266
|
}
|
|
5919
6267
|
}
|
|
5920
|
-
let lastSecretsProvisionAt =
|
|
6268
|
+
let lastSecretsProvisionAt = state5.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
|
|
5921
6269
|
let secretsHash = agentState.knownSecretsHashes.get(agent.agent_id) ?? null;
|
|
5922
6270
|
try {
|
|
5923
6271
|
const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
|
|
@@ -5971,7 +6319,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5971
6319
|
}
|
|
5972
6320
|
if (process.env.AGT_CONNECTIVITY_PROBE_ENABLED === "true") {
|
|
5973
6321
|
try {
|
|
5974
|
-
const probeProjectDir =
|
|
6322
|
+
const probeProjectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
|
|
5975
6323
|
await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
|
|
5976
6324
|
} catch (err) {
|
|
5977
6325
|
log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -5981,11 +6329,11 @@ async function processAgent(agent, agentStates) {
|
|
|
5981
6329
|
const intHash = computeIntegrationsHash(integrations);
|
|
5982
6330
|
const prevIntHash = agentState.knownIntegrationHashes.get(agent.agent_id);
|
|
5983
6331
|
if (intHash !== prevIntHash) {
|
|
5984
|
-
const projectDir =
|
|
5985
|
-
const envIntPath =
|
|
6332
|
+
const projectDir = join8(homedir4(), ".augmented", agent.code_name, "project");
|
|
6333
|
+
const envIntPath = join8(projectDir, ".env.integrations");
|
|
5986
6334
|
let preWriteEnv;
|
|
5987
6335
|
try {
|
|
5988
|
-
preWriteEnv =
|
|
6336
|
+
preWriteEnv = readFileSync9(envIntPath, "utf-8");
|
|
5989
6337
|
} catch {
|
|
5990
6338
|
preWriteEnv = void 0;
|
|
5991
6339
|
}
|
|
@@ -5997,9 +6345,9 @@ async function processAgent(agent, agentStates) {
|
|
|
5997
6345
|
let rotationHandled = true;
|
|
5998
6346
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
5999
6347
|
try {
|
|
6000
|
-
const projectMcpPath =
|
|
6001
|
-
const postWriteEnv =
|
|
6002
|
-
const mcpContent =
|
|
6348
|
+
const projectMcpPath = join8(projectDir, ".mcp.json");
|
|
6349
|
+
const postWriteEnv = readFileSync9(envIntPath, "utf-8");
|
|
6350
|
+
const mcpContent = readFileSync9(projectMcpPath, "utf-8");
|
|
6003
6351
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
6004
6352
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
6005
6353
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -6067,8 +6415,8 @@ async function processAgent(agent, agentStates) {
|
|
|
6067
6415
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
6068
6416
|
if (mcpPath) {
|
|
6069
6417
|
try {
|
|
6070
|
-
const { readFileSync:
|
|
6071
|
-
const mcpConfig = JSON.parse(
|
|
6418
|
+
const { readFileSync: readFileSync10 } = await import("fs");
|
|
6419
|
+
const mcpConfig = JSON.parse(readFileSync10(mcpPath, "utf-8"));
|
|
6072
6420
|
if (mcpConfig.mcpServers) {
|
|
6073
6421
|
const managedPrefixes = [
|
|
6074
6422
|
"composio_",
|
|
@@ -6164,7 +6512,7 @@ async function processAgent(agent, agentStates) {
|
|
|
6164
6512
|
if (agent.status === "active") {
|
|
6165
6513
|
if (frameworkAdapter.installPlugin) {
|
|
6166
6514
|
try {
|
|
6167
|
-
const pluginPath =
|
|
6515
|
+
const pluginPath = join8(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
6168
6516
|
if (existsSync5(pluginPath)) {
|
|
6169
6517
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
6170
6518
|
agtHost: requireHost(),
|
|
@@ -6230,25 +6578,25 @@ async function processAgent(agent, agentStates) {
|
|
|
6230
6578
|
}
|
|
6231
6579
|
}
|
|
6232
6580
|
try {
|
|
6233
|
-
const { readdirSync:
|
|
6581
|
+
const { readdirSync: readdirSync5, rmSync: rmSync3 } = await import("fs");
|
|
6234
6582
|
const { homedir: homedir5 } = await import("os");
|
|
6235
6583
|
const frameworkId2 = frameworkAdapter.id;
|
|
6236
6584
|
const candidateSkillDirs = [
|
|
6237
6585
|
// Claude Code — framework runtime tree
|
|
6238
|
-
|
|
6586
|
+
join8(homedir5(), ".augmented", agent.code_name, "skills"),
|
|
6239
6587
|
// Claude Code — project tree
|
|
6240
|
-
|
|
6588
|
+
join8(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
6241
6589
|
// OpenClaw — framework runtime tree
|
|
6242
|
-
|
|
6590
|
+
join8(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
|
|
6243
6591
|
// Defensive: legacy provision-side path, not currently an
|
|
6244
6592
|
// install target but cheap to sweep.
|
|
6245
|
-
|
|
6593
|
+
join8(agentDir, ".claude", "skills")
|
|
6246
6594
|
];
|
|
6247
6595
|
const existingDirs = candidateSkillDirs.filter((d) => existsSync5(d));
|
|
6248
6596
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
6249
6597
|
for (const dir of existingDirs) {
|
|
6250
6598
|
try {
|
|
6251
|
-
for (const entry of
|
|
6599
|
+
for (const entry of readdirSync5(dir)) {
|
|
6252
6600
|
if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
|
|
6253
6601
|
discoveredEntries.add(entry);
|
|
6254
6602
|
}
|
|
@@ -6258,7 +6606,7 @@ async function processAgent(agent, agentStates) {
|
|
|
6258
6606
|
}
|
|
6259
6607
|
const removeSkillFolder = (entry, reason) => {
|
|
6260
6608
|
for (const dir of existingDirs) {
|
|
6261
|
-
const p =
|
|
6609
|
+
const p = join8(dir, entry);
|
|
6262
6610
|
if (existsSync5(p)) {
|
|
6263
6611
|
rmSync3(p, { recursive: true, force: true });
|
|
6264
6612
|
}
|
|
@@ -6420,8 +6768,8 @@ async function processAgent(agent, agentStates) {
|
|
|
6420
6768
|
const sess = getSessionState(agent.code_name);
|
|
6421
6769
|
let mcpJsonParsed = null;
|
|
6422
6770
|
try {
|
|
6423
|
-
const mcpPath =
|
|
6424
|
-
mcpJsonParsed = JSON.parse(
|
|
6771
|
+
const mcpPath = join8(getProjectDir(agent.code_name), ".mcp.json");
|
|
6772
|
+
mcpJsonParsed = JSON.parse(readFileSync9(mcpPath, "utf-8"));
|
|
6425
6773
|
} catch {
|
|
6426
6774
|
}
|
|
6427
6775
|
reapMissingMcpSessions({
|
|
@@ -6606,10 +6954,10 @@ async function processAgent(agent, agentStates) {
|
|
|
6606
6954
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
6607
6955
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
6608
6956
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
6609
|
-
const jobsPath =
|
|
6957
|
+
const jobsPath = join8(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
6610
6958
|
if (existsSync5(jobsPath)) {
|
|
6611
6959
|
try {
|
|
6612
|
-
const jobsData = JSON.parse(
|
|
6960
|
+
const jobsData = JSON.parse(readFileSync9(jobsPath, "utf-8"));
|
|
6613
6961
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
6614
6962
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
6615
6963
|
);
|
|
@@ -6746,7 +7094,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
6746
7094
|
if (trackedFiles.length > 0 && existsSync5(agentDir)) {
|
|
6747
7095
|
const hashes = /* @__PURE__ */ new Map();
|
|
6748
7096
|
for (const file of trackedFiles) {
|
|
6749
|
-
const h = hashFile(
|
|
7097
|
+
const h = hashFile(join8(agentDir, file));
|
|
6750
7098
|
if (h) hashes.set(file, h);
|
|
6751
7099
|
}
|
|
6752
7100
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -6780,19 +7128,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
6780
7128
|
lastCleanupAt.set(codeName, Date.now());
|
|
6781
7129
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
6782
7130
|
for (const agentDir of ["main", codeName]) {
|
|
6783
|
-
const sessionsDir =
|
|
7131
|
+
const sessionsDir = join8(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
6784
7132
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
6785
7133
|
}
|
|
6786
|
-
const cronRunsDir =
|
|
7134
|
+
const cronRunsDir = join8(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
6787
7135
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
6788
|
-
const cronJobsPath =
|
|
7136
|
+
const cronJobsPath = join8(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
6789
7137
|
clearStaleCronRunState(cronJobsPath);
|
|
6790
7138
|
}
|
|
6791
7139
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
6792
|
-
const indexPath =
|
|
7140
|
+
const indexPath = join8(sessionsDir, "sessions.json");
|
|
6793
7141
|
if (!existsSync5(indexPath)) return;
|
|
6794
7142
|
try {
|
|
6795
|
-
const raw =
|
|
7143
|
+
const raw = readFileSync9(indexPath, "utf-8");
|
|
6796
7144
|
const index = JSON.parse(raw);
|
|
6797
7145
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
6798
7146
|
key: k,
|
|
@@ -6805,7 +7153,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6805
7153
|
for (const entry of toDelete) {
|
|
6806
7154
|
delete index[entry.key];
|
|
6807
7155
|
if (entry.sessionId) {
|
|
6808
|
-
const sessionFile =
|
|
7156
|
+
const sessionFile = join8(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
6809
7157
|
try {
|
|
6810
7158
|
if (existsSync5(sessionFile)) {
|
|
6811
7159
|
unlinkSync(sessionFile);
|
|
@@ -6827,7 +7175,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6827
7175
|
delete index[parentKey];
|
|
6828
7176
|
if (parentSessionId) {
|
|
6829
7177
|
try {
|
|
6830
|
-
const f =
|
|
7178
|
+
const f = join8(sessionsDir, `${parentSessionId}.jsonl`);
|
|
6831
7179
|
if (existsSync5(f)) {
|
|
6832
7180
|
unlinkSync(f);
|
|
6833
7181
|
deletedFiles++;
|
|
@@ -6848,26 +7196,26 @@ var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
|
6848
7196
|
function clearStaleCronRunState(jobsPath) {
|
|
6849
7197
|
if (!existsSync5(jobsPath)) return;
|
|
6850
7198
|
try {
|
|
6851
|
-
const raw =
|
|
7199
|
+
const raw = readFileSync9(jobsPath, "utf-8");
|
|
6852
7200
|
const data = JSON.parse(raw);
|
|
6853
7201
|
const jobs = data.jobs ?? data;
|
|
6854
7202
|
if (!Array.isArray(jobs)) return;
|
|
6855
7203
|
let changed = false;
|
|
6856
7204
|
const now = Date.now();
|
|
6857
7205
|
for (const job of jobs) {
|
|
6858
|
-
const
|
|
6859
|
-
if (!
|
|
6860
|
-
const runStartedAt =
|
|
6861
|
-
const isRunning =
|
|
7206
|
+
const state6 = job.state;
|
|
7207
|
+
if (!state6) continue;
|
|
7208
|
+
const runStartedAt = state6.runningAtMs ?? state6.runStartedAtMs;
|
|
7209
|
+
const isRunning = state6.running === true || state6.status === "running";
|
|
6862
7210
|
if (isRunning && runStartedAt && now - runStartedAt > STALE_RUN_TIMEOUT_MS) {
|
|
6863
|
-
|
|
6864
|
-
delete
|
|
6865
|
-
delete
|
|
6866
|
-
delete
|
|
7211
|
+
state6.running = false;
|
|
7212
|
+
delete state6.status;
|
|
7213
|
+
delete state6.runStartedAtMs;
|
|
7214
|
+
delete state6.currentRunId;
|
|
6867
7215
|
changed = true;
|
|
6868
7216
|
log(`Cleared stale running state for cron job '${job.name}' (stuck for ${Math.round((now - runStartedAt) / 6e4)}min)`);
|
|
6869
7217
|
} else if (isRunning && !runStartedAt) {
|
|
6870
|
-
|
|
7218
|
+
state6.running = false;
|
|
6871
7219
|
changed = true;
|
|
6872
7220
|
log(`Cleared stale running state for cron job '${job.name}' (no start timestamp)`);
|
|
6873
7221
|
}
|
|
@@ -6883,11 +7231,11 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
|
6883
7231
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
6884
7232
|
let removed = 0;
|
|
6885
7233
|
try {
|
|
6886
|
-
for (const f of
|
|
7234
|
+
for (const f of readdirSync4(dir)) {
|
|
6887
7235
|
if (!f.endsWith(ext)) continue;
|
|
6888
|
-
const fullPath =
|
|
7236
|
+
const fullPath = join8(dir, f);
|
|
6889
7237
|
try {
|
|
6890
|
-
const st =
|
|
7238
|
+
const st = statSync3(fullPath);
|
|
6891
7239
|
if (st.mtimeMs < cutoff) {
|
|
6892
7240
|
unlinkSync(fullPath);
|
|
6893
7241
|
removed++;
|
|
@@ -6905,7 +7253,7 @@ var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
|
6905
7253
|
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
6906
7254
|
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
6907
7255
|
function claudePidFilePath() {
|
|
6908
|
-
return
|
|
7256
|
+
return join8(homedir4(), ".augmented", "manager-claude-pids.json");
|
|
6909
7257
|
}
|
|
6910
7258
|
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
6911
7259
|
function registerClaudeSpawn(record) {
|
|
@@ -6950,16 +7298,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
6950
7298
|
enabled: t.enabled ?? true,
|
|
6951
7299
|
triggered_at: t.triggered_at ?? null
|
|
6952
7300
|
}));
|
|
6953
|
-
const
|
|
6954
|
-
claudeSchedulerStates.set(codeName,
|
|
7301
|
+
const state7 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
7302
|
+
claudeSchedulerStates.set(codeName, state7);
|
|
6955
7303
|
agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
6956
7304
|
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
6957
7305
|
}
|
|
6958
7306
|
if (!claudeSchedulerStates.has(codeName)) {
|
|
6959
7307
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
6960
7308
|
}
|
|
6961
|
-
const
|
|
6962
|
-
const ready = getReadyTasks(
|
|
7309
|
+
const state6 = claudeSchedulerStates.get(codeName);
|
|
7310
|
+
const ready = getReadyTasks(state6, inFlightClaudeTasks);
|
|
6963
7311
|
if (ready.length === 0) return;
|
|
6964
7312
|
for (const task of ready) {
|
|
6965
7313
|
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
@@ -7106,8 +7454,8 @@ async function deliverScheduledCardResult(codeName, agentId, cardId) {
|
|
|
7106
7454
|
markScheduledCardDeliveryComplete(cardId);
|
|
7107
7455
|
return "terminal";
|
|
7108
7456
|
}
|
|
7109
|
-
const
|
|
7110
|
-
const task =
|
|
7457
|
+
const state6 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
7458
|
+
const task = state6.tasks[card.source_ref];
|
|
7111
7459
|
if (!task) {
|
|
7112
7460
|
log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
|
|
7113
7461
|
markScheduledCardDeliveryComplete(cardId);
|
|
@@ -7213,7 +7561,7 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
|
|
|
7213
7561
|
}
|
|
7214
7562
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
7215
7563
|
const projectDir = getProjectDir2(codeName);
|
|
7216
|
-
const mcpConfigPath =
|
|
7564
|
+
const mcpConfigPath = join8(projectDir, ".mcp.json");
|
|
7217
7565
|
let runId = null;
|
|
7218
7566
|
let kanbanItemId = null;
|
|
7219
7567
|
let taskResult;
|
|
@@ -7221,11 +7569,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
7221
7569
|
const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
|
|
7222
7570
|
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
|
|
7223
7571
|
try {
|
|
7224
|
-
const claudeMdPath =
|
|
7572
|
+
const claudeMdPath = join8(projectDir, "CLAUDE.md");
|
|
7225
7573
|
const serverNames = [];
|
|
7226
7574
|
if (existsSync5(mcpConfigPath)) {
|
|
7227
7575
|
try {
|
|
7228
|
-
const d = JSON.parse(
|
|
7576
|
+
const d = JSON.parse(readFileSync9(mcpConfigPath, "utf-8"));
|
|
7229
7577
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
7230
7578
|
} catch {
|
|
7231
7579
|
}
|
|
@@ -7248,10 +7596,10 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
7248
7596
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
7249
7597
|
}
|
|
7250
7598
|
const childEnv = { ...process.env };
|
|
7251
|
-
const envIntPath =
|
|
7599
|
+
const envIntPath = join8(projectDir, ".env.integrations");
|
|
7252
7600
|
if (existsSync5(envIntPath)) {
|
|
7253
7601
|
try {
|
|
7254
|
-
Object.assign(childEnv, parseEnvIntegrations(
|
|
7602
|
+
Object.assign(childEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
|
|
7255
7603
|
} catch {
|
|
7256
7604
|
}
|
|
7257
7605
|
}
|
|
@@ -7423,8 +7771,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
|
|
|
7423
7771
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
7424
7772
|
const codeName = agent.code_name;
|
|
7425
7773
|
const projectDir = getProjectDir(codeName);
|
|
7426
|
-
const mcpConfigPath =
|
|
7427
|
-
const claudeMdPath =
|
|
7774
|
+
const mcpConfigPath = join8(projectDir, ".mcp.json");
|
|
7775
|
+
const claudeMdPath = join8(projectDir, "CLAUDE.md");
|
|
7428
7776
|
if (restartBreaker.isTripped(codeName)) {
|
|
7429
7777
|
const trip = restartBreaker.getTrip(codeName);
|
|
7430
7778
|
return {
|
|
@@ -7651,9 +7999,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash3("sha256").
|
|
|
7651
7999
|
} else if (!claudeSchedulerStates.has(codeName)) {
|
|
7652
8000
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
7653
8001
|
}
|
|
7654
|
-
const
|
|
7655
|
-
if (
|
|
7656
|
-
const ready = getReadyTasks(
|
|
8002
|
+
const state6 = claudeSchedulerStates.get(codeName);
|
|
8003
|
+
if (state6) {
|
|
8004
|
+
const ready = getReadyTasks(state6, inFlightClaudeTasks);
|
|
7657
8005
|
if (ready.length > 0) {
|
|
7658
8006
|
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
8007
|
}
|
|
@@ -8064,11 +8412,11 @@ ${escapeXml(msg.content)}
|
|
|
8064
8412
|
log(`[direct-chat] One-shot spawn for '${agent.codeName}' (msg=${msg.id}; host in-flight=${directChatSpawnGate.hostInFlight}, queued=${directChatSpawnGate.queuedCount})`);
|
|
8065
8413
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-EM24LTGV.js");
|
|
8066
8414
|
const projDir = ccProjectDir(agent.codeName);
|
|
8067
|
-
const mcpConfigPath =
|
|
8415
|
+
const mcpConfigPath = join8(projDir, ".mcp.json");
|
|
8068
8416
|
const serverNames = [];
|
|
8069
8417
|
if (existsSync5(mcpConfigPath)) {
|
|
8070
8418
|
try {
|
|
8071
|
-
const d = JSON.parse(
|
|
8419
|
+
const d = JSON.parse(readFileSync9(mcpConfigPath, "utf-8"));
|
|
8072
8420
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
8073
8421
|
} catch {
|
|
8074
8422
|
}
|
|
@@ -8087,15 +8435,15 @@ ${escapeXml(msg.content)}
|
|
|
8087
8435
|
"--allowedTools",
|
|
8088
8436
|
allowedTools
|
|
8089
8437
|
];
|
|
8090
|
-
const chatClaudeMd =
|
|
8438
|
+
const chatClaudeMd = join8(projDir, "CLAUDE.md");
|
|
8091
8439
|
if (existsSync5(chatClaudeMd)) {
|
|
8092
8440
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
8093
8441
|
}
|
|
8094
|
-
const envIntPath =
|
|
8442
|
+
const envIntPath = join8(projDir, ".env.integrations");
|
|
8095
8443
|
const childEnv = { ...process.env };
|
|
8096
8444
|
if (existsSync5(envIntPath)) {
|
|
8097
8445
|
try {
|
|
8098
|
-
Object.assign(childEnv, parseEnvIntegrations(
|
|
8446
|
+
Object.assign(childEnv, parseEnvIntegrations(readFileSync9(envIntPath, "utf-8")));
|
|
8099
8447
|
} catch {
|
|
8100
8448
|
}
|
|
8101
8449
|
}
|
|
@@ -8473,12 +8821,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
8473
8821
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
8474
8822
|
try {
|
|
8475
8823
|
const candidates = [
|
|
8476
|
-
|
|
8477
|
-
|
|
8824
|
+
join8(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
8825
|
+
join8(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
8478
8826
|
];
|
|
8479
8827
|
for (const candidate of candidates) {
|
|
8480
8828
|
if (existsSync5(candidate)) {
|
|
8481
|
-
const content =
|
|
8829
|
+
const content = readFileSync9(candidate, "utf-8");
|
|
8482
8830
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
8483
8831
|
builtInSkillCache.set(skillId, files);
|
|
8484
8832
|
return files;
|
|
@@ -9121,7 +9469,7 @@ async function processClaudePairSessions(agents) {
|
|
|
9121
9469
|
killPairSession,
|
|
9122
9470
|
pairTmuxSession,
|
|
9123
9471
|
finalizeClaudePairOnboarding
|
|
9124
|
-
} = await import("../claude-pair-runtime-
|
|
9472
|
+
} = await import("../claude-pair-runtime-WTGNQXCL.js");
|
|
9125
9473
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
9126
9474
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
9127
9475
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -9431,8 +9779,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
9431
9779
|
};
|
|
9432
9780
|
}
|
|
9433
9781
|
async function syncMemories(agent, configDir, log2) {
|
|
9434
|
-
const projectDir =
|
|
9435
|
-
const memoryDir =
|
|
9782
|
+
const projectDir = join8(configDir, agent.code_name, "project");
|
|
9783
|
+
const memoryDir = join8(projectDir, "memory");
|
|
9436
9784
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
9437
9785
|
if (isFreshSync) {
|
|
9438
9786
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -9447,10 +9795,10 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
9447
9795
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
9448
9796
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
9449
9797
|
const changedMemories = [];
|
|
9450
|
-
for (const file of
|
|
9798
|
+
for (const file of readdirSync4(memoryDir)) {
|
|
9451
9799
|
if (!file.endsWith(".md")) continue;
|
|
9452
9800
|
try {
|
|
9453
|
-
const raw =
|
|
9801
|
+
const raw = readFileSync9(join8(memoryDir, file), "utf-8");
|
|
9454
9802
|
const fileHash = createHash3("sha256").update(raw).digest("hex").slice(0, 16);
|
|
9455
9803
|
currentHashes.set(file, fileHash);
|
|
9456
9804
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -9475,7 +9823,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
9475
9823
|
} catch (err) {
|
|
9476
9824
|
for (const mem of changedMemories) {
|
|
9477
9825
|
for (const [file] of currentHashes) {
|
|
9478
|
-
const parsed = parseMemoryFile(
|
|
9826
|
+
const parsed = parseMemoryFile(readFileSync9(join8(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
9479
9827
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
9480
9828
|
}
|
|
9481
9829
|
}
|
|
@@ -9488,7 +9836,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
9488
9836
|
}
|
|
9489
9837
|
}
|
|
9490
9838
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
9491
|
-
const localFiles = existsSync5(memoryDir) ?
|
|
9839
|
+
const localFiles = existsSync5(memoryDir) ? readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
9492
9840
|
const localListHash = createHash3("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
9493
9841
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
9494
9842
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
@@ -9510,7 +9858,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
|
9510
9858
|
const mem = dbMemories.memories[i];
|
|
9511
9859
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
9512
9860
|
const slug = rawSlug || `memory-${i}`;
|
|
9513
|
-
const filePath =
|
|
9861
|
+
const filePath = join8(memoryDir, `${slug}.md`);
|
|
9514
9862
|
const desired = `---
|
|
9515
9863
|
name: ${JSON.stringify(mem.name)}
|
|
9516
9864
|
type: ${mem.type}
|
|
@@ -9522,7 +9870,7 @@ ${mem.content}
|
|
|
9522
9870
|
if (existsSync5(filePath)) {
|
|
9523
9871
|
let existing = "";
|
|
9524
9872
|
try {
|
|
9525
|
-
existing =
|
|
9873
|
+
existing = readFileSync9(filePath, "utf-8");
|
|
9526
9874
|
} catch {
|
|
9527
9875
|
}
|
|
9528
9876
|
if (existing === desired) continue;
|
|
@@ -9534,7 +9882,7 @@ ${mem.content}
|
|
|
9534
9882
|
}
|
|
9535
9883
|
}
|
|
9536
9884
|
if (written > 0 || overwritten > 0) {
|
|
9537
|
-
const updatedFiles =
|
|
9885
|
+
const updatedFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
9538
9886
|
lastLocalFileHash.set(agent.agent_id, createHash3("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
9539
9887
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
9540
9888
|
}
|
|
@@ -9736,11 +10084,11 @@ function startManager(opts) {
|
|
|
9736
10084
|
try {
|
|
9737
10085
|
const stateFile = getStateFile();
|
|
9738
10086
|
if (existsSync5(stateFile)) {
|
|
9739
|
-
const raw =
|
|
10087
|
+
const raw = readFileSync9(stateFile, "utf-8");
|
|
9740
10088
|
const parsed = JSON.parse(raw);
|
|
9741
10089
|
if (Array.isArray(parsed.agents)) {
|
|
9742
|
-
|
|
9743
|
-
log(`[startup] rehydrated ${
|
|
10090
|
+
state5.agents = parsed.agents;
|
|
10091
|
+
log(`[startup] rehydrated ${state5.agents.length} agent state(s) from ${stateFile}`);
|
|
9744
10092
|
}
|
|
9745
10093
|
if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
|
|
9746
10094
|
restartBreaker.hydrate(parsed.circuitBreakerTrips);
|
|
@@ -9752,7 +10100,7 @@ function startManager(opts) {
|
|
|
9752
10100
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
9753
10101
|
}
|
|
9754
10102
|
log(
|
|
9755
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
10103
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join8(homedir4(), ".augmented", "manager.log")}`
|
|
9756
10104
|
);
|
|
9757
10105
|
deployMcpAssets();
|
|
9758
10106
|
reapOrphanChannelMcps({ log });
|
|
@@ -9773,7 +10121,7 @@ async function reapOrphanedClaudePids() {
|
|
|
9773
10121
|
const looksLikeClaude = (pid) => {
|
|
9774
10122
|
if (process.platform !== "linux") return true;
|
|
9775
10123
|
try {
|
|
9776
|
-
const comm =
|
|
10124
|
+
const comm = readFileSync9(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
9777
10125
|
return comm.includes("claude");
|
|
9778
10126
|
} catch {
|
|
9779
10127
|
return false;
|
|
@@ -9883,14 +10231,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
9883
10231
|
}
|
|
9884
10232
|
}
|
|
9885
10233
|
function deployMcpAssets() {
|
|
9886
|
-
const targetDir =
|
|
10234
|
+
const targetDir = join8(homedir4(), ".augmented", "_mcp");
|
|
9887
10235
|
mkdirSync4(targetDir, { recursive: true });
|
|
9888
10236
|
const moduleDir = dirname3(fileURLToPath(import.meta.url));
|
|
9889
10237
|
let mcpSourceDir = "";
|
|
9890
10238
|
let dir = moduleDir;
|
|
9891
10239
|
for (let i = 0; i < 6; i++) {
|
|
9892
|
-
const candidate =
|
|
9893
|
-
if (existsSync5(
|
|
10240
|
+
const candidate = join8(dir, "dist", "mcp");
|
|
10241
|
+
if (existsSync5(join8(candidate, "index.js"))) {
|
|
9894
10242
|
mcpSourceDir = candidate;
|
|
9895
10243
|
break;
|
|
9896
10244
|
}
|
|
@@ -9906,7 +10254,7 @@ function deployMcpAssets() {
|
|
|
9906
10254
|
const fileHash = (p) => {
|
|
9907
10255
|
try {
|
|
9908
10256
|
if (!existsSync5(p)) return null;
|
|
9909
|
-
return createHash3("sha256").update(
|
|
10257
|
+
return createHash3("sha256").update(readFileSync9(p)).digest("hex");
|
|
9910
10258
|
} catch {
|
|
9911
10259
|
return null;
|
|
9912
10260
|
}
|
|
@@ -9927,8 +10275,8 @@ function deployMcpAssets() {
|
|
|
9927
10275
|
"teams-channel.js"
|
|
9928
10276
|
// ENG-6019
|
|
9929
10277
|
]) {
|
|
9930
|
-
const src =
|
|
9931
|
-
const dst =
|
|
10278
|
+
const src = join8(mcpSourceDir, file);
|
|
10279
|
+
const dst = join8(targetDir, file);
|
|
9932
10280
|
if (!existsSync5(src)) continue;
|
|
9933
10281
|
const before = fileHash(dst);
|
|
9934
10282
|
try {
|
|
@@ -9946,16 +10294,16 @@ function deployMcpAssets() {
|
|
|
9946
10294
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
9947
10295
|
restartRunningChannelMcps(changedBasenames);
|
|
9948
10296
|
}
|
|
9949
|
-
const localMcpPath =
|
|
10297
|
+
const localMcpPath = join8(targetDir, "index.js");
|
|
9950
10298
|
try {
|
|
9951
|
-
const agentsDir =
|
|
10299
|
+
const agentsDir = join8(homedir4(), ".augmented", "agents");
|
|
9952
10300
|
if (existsSync5(agentsDir)) {
|
|
9953
|
-
for (const entry of
|
|
10301
|
+
for (const entry of readdirSync4(agentsDir, { withFileTypes: true })) {
|
|
9954
10302
|
if (!entry.isDirectory()) continue;
|
|
9955
10303
|
for (const subdir of ["provision", "project"]) {
|
|
9956
|
-
const mcpJsonPath =
|
|
10304
|
+
const mcpJsonPath = join8(agentsDir, entry.name, subdir, ".mcp.json");
|
|
9957
10305
|
try {
|
|
9958
|
-
const raw =
|
|
10306
|
+
const raw = readFileSync9(mcpJsonPath, "utf-8");
|
|
9959
10307
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
9960
10308
|
const mcpConfig = JSON.parse(raw);
|
|
9961
10309
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|