@integrity-labs/agt-cli 0.23.1 → 0.24.0
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-2TOCO5D2.js → chunk-HSIESZMZ.js} +23 -1
- package/dist/{chunk-2TOCO5D2.js.map → chunk-HSIESZMZ.js.map} +1 -1
- package/dist/{chunk-Q4QD3TAC.js → chunk-WZTRMJM4.js} +205 -159
- package/dist/chunk-WZTRMJM4.js.map +1 -0
- package/dist/{chunk-TCCBS3PD.js → chunk-ZKQGDH3T.js} +2 -2
- package/dist/{claude-pair-runtime-KUWBZW33.js → claude-pair-runtime-XCFWTH6T.js} +2 -2
- package/dist/lib/manager-worker.js +551 -251
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/index.js +71 -0
- package/dist/mcp/slack-channel.js +32 -246
- package/dist/mcp/telegram-channel.js +30 -232
- package/dist/pane-mcp-banner-scraper-JA437JIB.js +22 -0
- package/dist/pane-mcp-banner-scraper-JA437JIB.js.map +1 -0
- package/dist/{persistent-session-XEA4SPAN.js → persistent-session-L4YIJJML.js} +3 -3
- package/dist/{responsiveness-probe-VWPOCABI.js → responsiveness-probe-YRLESO54.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-Q4QD3TAC.js.map +0 -1
- /package/dist/{chunk-TCCBS3PD.js.map → chunk-ZKQGDH3T.js.map} +0 -0
- /package/dist/{claude-pair-runtime-KUWBZW33.js.map → claude-pair-runtime-XCFWTH6T.js.map} +0 -0
- /package/dist/{persistent-session-XEA4SPAN.js.map → persistent-session-L4YIJJML.js.map} +0 -0
- /package/dist/{responsiveness-probe-VWPOCABI.js.map → responsiveness-probe-YRLESO54.js.map} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SUPERVISOR_RESTART_EXIT_CODE,
|
|
3
3
|
api,
|
|
4
|
+
estimateActiveTasksTokens,
|
|
4
5
|
exchangeApiKey,
|
|
5
6
|
extractCommandNotFound,
|
|
6
7
|
getApiKey,
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
provisionOrientHook,
|
|
12
13
|
provisionStopHook,
|
|
13
14
|
requireHost
|
|
14
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-WZTRMJM4.js";
|
|
15
16
|
import {
|
|
16
17
|
findTaskByTemplate,
|
|
17
18
|
getProjectDir as getProjectDir2,
|
|
@@ -46,7 +47,7 @@ import {
|
|
|
46
47
|
stopAllSessionsAndWait,
|
|
47
48
|
stopPersistentSession,
|
|
48
49
|
takeZombieDetection
|
|
49
|
-
} from "../chunk-
|
|
50
|
+
} from "../chunk-ZKQGDH3T.js";
|
|
50
51
|
import {
|
|
51
52
|
KANBAN_CHECK_COMMAND,
|
|
52
53
|
appendDmFooter,
|
|
@@ -62,15 +63,15 @@ import {
|
|
|
62
63
|
resolveChannels,
|
|
63
64
|
resolveDmTarget,
|
|
64
65
|
wrapScheduledTaskPrompt
|
|
65
|
-
} from "../chunk-
|
|
66
|
+
} from "../chunk-HSIESZMZ.js";
|
|
66
67
|
|
|
67
68
|
// src/lib/manager-worker.ts
|
|
68
69
|
import { createHash as createHash2 } from "crypto";
|
|
69
|
-
import { readFileSync as
|
|
70
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, appendFileSync, mkdirSync as mkdirSync2, chmodSync, existsSync as existsSync4, rmSync as rmSync2, readdirSync as readdirSync2, statSync, unlinkSync, copyFileSync } from "fs";
|
|
70
71
|
import https from "https";
|
|
71
72
|
import { execFileSync as syncExecFile } from "child_process";
|
|
72
|
-
import { join as
|
|
73
|
-
import { homedir as
|
|
73
|
+
import { join as join5, dirname } from "path";
|
|
74
|
+
import { homedir as homedir4 } from "os";
|
|
74
75
|
import { fileURLToPath } from "url";
|
|
75
76
|
|
|
76
77
|
// src/lib/mcp-config-drift.ts
|
|
@@ -434,12 +435,12 @@ function reapMissingMcpSessions(args) {
|
|
|
434
435
|
if (!missingRaw.includes(key)) liveKeys.add(key);
|
|
435
436
|
}
|
|
436
437
|
for (const key of liveKeys) {
|
|
437
|
-
const
|
|
438
|
-
if (
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
438
|
+
const state4 = presenceReaperState.get(stateKey(codeName, key));
|
|
439
|
+
if (state4) {
|
|
440
|
+
state4.attempts = 0;
|
|
441
|
+
state4.lastSeenLiveAt = now();
|
|
442
|
+
state4.lastAttemptedSessionStartedAt = null;
|
|
443
|
+
state4.gaveUpLogged = false;
|
|
443
444
|
}
|
|
444
445
|
}
|
|
445
446
|
if (missing.length === 0) {
|
|
@@ -454,24 +455,24 @@ function reapMissingMcpSessions(args) {
|
|
|
454
455
|
const active = [];
|
|
455
456
|
for (const key of missing) {
|
|
456
457
|
const sk = stateKey(codeName, key);
|
|
457
|
-
const
|
|
458
|
+
const state4 = presenceReaperState.get(sk) ?? {
|
|
458
459
|
attempts: 0,
|
|
459
460
|
lastSeenLiveAt: null,
|
|
460
461
|
lastAttemptedSessionStartedAt: null,
|
|
461
462
|
gaveUpLogged: false
|
|
462
463
|
};
|
|
463
|
-
if (
|
|
464
|
-
|
|
465
|
-
|
|
464
|
+
if (state4.lastAttemptedSessionStartedAt !== sessionStartedAt) {
|
|
465
|
+
state4.attempts += 1;
|
|
466
|
+
state4.lastAttemptedSessionStartedAt = sessionStartedAt;
|
|
466
467
|
}
|
|
467
|
-
presenceReaperState.set(sk,
|
|
468
|
-
if (
|
|
468
|
+
presenceReaperState.set(sk, state4);
|
|
469
|
+
if (state4.attempts > MAX_PRESENCE_RESTART_ATTEMPTS) {
|
|
469
470
|
givenUp.push(key);
|
|
470
|
-
if (!
|
|
471
|
+
if (!state4.gaveUpLogged) {
|
|
471
472
|
log2(
|
|
472
473
|
`[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)`
|
|
473
474
|
);
|
|
474
|
-
|
|
475
|
+
state4.gaveUpLogged = true;
|
|
475
476
|
if (onGiveUp) {
|
|
476
477
|
try {
|
|
477
478
|
onGiveUp(codeName, key);
|
|
@@ -552,6 +553,108 @@ function decideChannelRestart(input) {
|
|
|
552
553
|
return { restart: true, added, removed };
|
|
553
554
|
}
|
|
554
555
|
|
|
556
|
+
// src/lib/restart-breaker.ts
|
|
557
|
+
var DEFAULT_MAX = 2;
|
|
558
|
+
var DEFAULT_WINDOW_MS = 6e5;
|
|
559
|
+
function readEnvNumber(name, fallback) {
|
|
560
|
+
const raw = process.env[name];
|
|
561
|
+
if (!raw) return fallback;
|
|
562
|
+
const parsed = Number(raw);
|
|
563
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
564
|
+
}
|
|
565
|
+
var RestartBreaker = class {
|
|
566
|
+
max;
|
|
567
|
+
windowMs;
|
|
568
|
+
now;
|
|
569
|
+
events = /* @__PURE__ */ new Map();
|
|
570
|
+
trips = /* @__PURE__ */ new Map();
|
|
571
|
+
constructor(opts = {}) {
|
|
572
|
+
this.max = opts.max ?? readEnvNumber("AGT_RESTART_BREAKER_MAX", DEFAULT_MAX);
|
|
573
|
+
this.windowMs = opts.windowMs ?? readEnvNumber("AGT_RESTART_BREAKER_WINDOW_MS", DEFAULT_WINDOW_MS);
|
|
574
|
+
this.now = opts.now ?? Date.now;
|
|
575
|
+
if (!Number.isFinite(this.max) || this.max < 1) {
|
|
576
|
+
throw new Error("restart-breaker max must be a finite number >= 1");
|
|
577
|
+
}
|
|
578
|
+
if (!Number.isFinite(this.windowMs) || this.windowMs < 1e3) {
|
|
579
|
+
throw new Error("restart-breaker windowMs must be a finite number >= 1000");
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
/** True if this agent's breaker is currently tripped (manager must skip spawn). */
|
|
583
|
+
isTripped(codeName) {
|
|
584
|
+
return this.trips.has(codeName);
|
|
585
|
+
}
|
|
586
|
+
getTrip(codeName) {
|
|
587
|
+
return this.trips.get(codeName);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Record a restart event. If recording this event puts the count
|
|
591
|
+
* inside the window above `max`, the breaker trips and the call site
|
|
592
|
+
* should NOT respawn.
|
|
593
|
+
*
|
|
594
|
+
* Idempotent on already-tripped breakers: returns the existing trip
|
|
595
|
+
* without double-counting events. Callers may still record the reason
|
|
596
|
+
* via the decision-log for forensics.
|
|
597
|
+
*/
|
|
598
|
+
record(codeName, reason) {
|
|
599
|
+
const existing = this.trips.get(codeName);
|
|
600
|
+
if (existing) {
|
|
601
|
+
return { tripped: false, trip: existing, windowCount: existing.eventsAtTrip.length };
|
|
602
|
+
}
|
|
603
|
+
const at = this.now();
|
|
604
|
+
const cutoff = at - this.windowMs;
|
|
605
|
+
const prior = (this.events.get(codeName) ?? []).filter((e) => e.at >= cutoff);
|
|
606
|
+
prior.push({ reason, at });
|
|
607
|
+
this.events.set(codeName, prior);
|
|
608
|
+
if (prior.length > this.max) {
|
|
609
|
+
const trip = {
|
|
610
|
+
trippedAt: at,
|
|
611
|
+
eventsAtTrip: [...prior],
|
|
612
|
+
statusMessage: formatStatusMessage(prior, this.windowMs)
|
|
613
|
+
};
|
|
614
|
+
this.trips.set(codeName, trip);
|
|
615
|
+
this.events.delete(codeName);
|
|
616
|
+
return { tripped: true, trip, windowCount: prior.length };
|
|
617
|
+
}
|
|
618
|
+
return { tripped: false, windowCount: prior.length };
|
|
619
|
+
}
|
|
620
|
+
/** Operator-initiated reset: drops the trip + the events log for this agent. */
|
|
621
|
+
clear(codeName) {
|
|
622
|
+
this.trips.delete(codeName);
|
|
623
|
+
this.events.delete(codeName);
|
|
624
|
+
}
|
|
625
|
+
/** Snapshot tripped agents for `manager-state.json`. */
|
|
626
|
+
serialize() {
|
|
627
|
+
return Object.fromEntries(this.trips.entries());
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Rehydrate trip state from `manager-state.json`. Called once at
|
|
631
|
+
* worker startup. Window history is intentionally NOT persisted — only
|
|
632
|
+
* tripped state. A tripped breaker survives manager restart; an
|
|
633
|
+
* un-tripped one starts a fresh window in the new worker.
|
|
634
|
+
*/
|
|
635
|
+
hydrate(saved) {
|
|
636
|
+
if (!saved) return;
|
|
637
|
+
for (const [codeName, trip] of Object.entries(saved)) {
|
|
638
|
+
if (trip && typeof trip.trippedAt === "number" && Array.isArray(trip.eventsAtTrip)) {
|
|
639
|
+
this.trips.set(codeName, trip);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
/** Test helper — current in-window event count for `codeName`. */
|
|
644
|
+
windowCount(codeName) {
|
|
645
|
+
const cutoff = this.now() - this.windowMs;
|
|
646
|
+
return (this.events.get(codeName) ?? []).filter((e) => e.at >= cutoff).length;
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
function formatStatusMessage(events, windowMs) {
|
|
650
|
+
const last = events[events.length - 1];
|
|
651
|
+
const windowLabel = windowMs < 6e4 ? `${Math.round(windowMs / 1e3)}s` : `${(windowMs / 6e4).toFixed(1).replace(/\.0$/, "")}min`;
|
|
652
|
+
const reasonCounts = /* @__PURE__ */ new Map();
|
|
653
|
+
for (const e of events) reasonCounts.set(e.reason, (reasonCounts.get(e.reason) ?? 0) + 1);
|
|
654
|
+
const breakdown = Array.from(reasonCounts.entries()).map(([r, n]) => `${r}=${n}`).join(", ");
|
|
655
|
+
return `Circuit breaker tripped: ${events.length} restarts in ${windowLabel} (${breakdown}); most recent=${last.reason} at ${new Date(last.at).toISOString()}`;
|
|
656
|
+
}
|
|
657
|
+
|
|
555
658
|
// src/lib/usage-banner-monitor.ts
|
|
556
659
|
var MIN_CHECK_INTERVAL_MS = 6e4;
|
|
557
660
|
var PANE_TAIL_LINES_FOR_BANNER = 200;
|
|
@@ -607,6 +710,79 @@ async function maybeReportUsageBanner(args) {
|
|
|
607
710
|
}
|
|
608
711
|
}
|
|
609
712
|
|
|
713
|
+
// src/lib/activity-cache-monitor.ts
|
|
714
|
+
import { existsSync, readFileSync } from "fs";
|
|
715
|
+
import { homedir } from "os";
|
|
716
|
+
import { join } from "path";
|
|
717
|
+
var MIN_CHECK_INTERVAL_MS2 = 6e4;
|
|
718
|
+
var STATS_CACHE_PATH = join(homedir(), ".claude", "stats-cache.json");
|
|
719
|
+
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
720
|
+
var state2 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
721
|
+
function selectNewDailyRows(raw, lastObservedDate) {
|
|
722
|
+
let parsed;
|
|
723
|
+
try {
|
|
724
|
+
parsed = JSON.parse(raw);
|
|
725
|
+
} catch {
|
|
726
|
+
return [];
|
|
727
|
+
}
|
|
728
|
+
const days = Array.isArray(parsed.dailyActivity) ? parsed.dailyActivity : [];
|
|
729
|
+
const nonNegInt = (v) => {
|
|
730
|
+
if (typeof v !== "number" || !Number.isFinite(v)) return null;
|
|
731
|
+
const n = Math.trunc(v);
|
|
732
|
+
return n >= 0 ? n : null;
|
|
733
|
+
};
|
|
734
|
+
const result = [];
|
|
735
|
+
for (const d of days) {
|
|
736
|
+
if (typeof d.date !== "string" || !ISO_DATE_RE.test(d.date)) continue;
|
|
737
|
+
if (lastObservedDate !== null && d.date <= lastObservedDate) continue;
|
|
738
|
+
const m = nonNegInt(d.messageCount);
|
|
739
|
+
const s = nonNegInt(d.sessionCount);
|
|
740
|
+
const t = nonNegInt(d.toolCallCount);
|
|
741
|
+
if (m === null || s === null || t === null) continue;
|
|
742
|
+
const breakdownForDay = parsed.dailyModelTokens?.[d.date];
|
|
743
|
+
const model_breakdown = breakdownForDay && typeof breakdownForDay === "object" && !Array.isArray(breakdownForDay) ? breakdownForDay : null;
|
|
744
|
+
result.push({
|
|
745
|
+
date: d.date,
|
|
746
|
+
message_count: m,
|
|
747
|
+
session_count: s,
|
|
748
|
+
tool_call_count: t,
|
|
749
|
+
model_breakdown
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
result.sort((a, b) => a.date.localeCompare(b.date));
|
|
753
|
+
return result;
|
|
754
|
+
}
|
|
755
|
+
async function maybeReportActivityCache(args) {
|
|
756
|
+
const { api: api2, log: log2 } = args;
|
|
757
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
758
|
+
const nowMs = now.getTime();
|
|
759
|
+
if (nowMs - state2.lastCheckedAt < MIN_CHECK_INTERVAL_MS2) return;
|
|
760
|
+
state2.lastCheckedAt = nowMs;
|
|
761
|
+
if (!existsSync(STATS_CACHE_PATH)) {
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
let raw;
|
|
765
|
+
try {
|
|
766
|
+
raw = readFileSync(STATS_CACHE_PATH, "utf-8");
|
|
767
|
+
} catch (err) {
|
|
768
|
+
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const rows = selectNewDailyRows(raw, state2.lastObservedDate);
|
|
772
|
+
if (rows.length === 0) return;
|
|
773
|
+
for (const row of rows) {
|
|
774
|
+
try {
|
|
775
|
+
await api2.post("/host/activity-observations", row);
|
|
776
|
+
state2.lastObservedDate = row.date;
|
|
777
|
+
} catch (err) {
|
|
778
|
+
log2(
|
|
779
|
+
`[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
|
|
780
|
+
);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
610
786
|
// src/lib/poll-backoff.ts
|
|
611
787
|
var POLL_BACKOFF_BASE_MS = 1e3;
|
|
612
788
|
var POLL_BACKOFF_MAX_MS = 6e4;
|
|
@@ -1036,8 +1212,8 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
1036
1212
|
|
|
1037
1213
|
// src/lib/claude-auth-detect.ts
|
|
1038
1214
|
import { readFile, readdir } from "fs/promises";
|
|
1039
|
-
import { homedir, platform } from "os";
|
|
1040
|
-
import { join } from "path";
|
|
1215
|
+
import { homedir as homedir2, platform } from "os";
|
|
1216
|
+
import { join as join2 } from "path";
|
|
1041
1217
|
import { execFile } from "child_process";
|
|
1042
1218
|
import { promisify } from "util";
|
|
1043
1219
|
var execFileAsync = promisify(execFile);
|
|
@@ -1052,8 +1228,8 @@ async function detectClaudeAuth() {
|
|
|
1052
1228
|
}
|
|
1053
1229
|
async function findClaudeCredentialsPaths() {
|
|
1054
1230
|
const candidates = [
|
|
1055
|
-
|
|
1056
|
-
|
|
1231
|
+
join2(homedir2(), ".claude", ".credentials.json"),
|
|
1232
|
+
join2(homedir2(), ".claude", "credentials.json")
|
|
1057
1233
|
];
|
|
1058
1234
|
const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
|
|
1059
1235
|
if (isLinuxRoot) {
|
|
@@ -1061,8 +1237,8 @@ async function findClaudeCredentialsPaths() {
|
|
|
1061
1237
|
const entries = await readdir("/home", { withFileTypes: true });
|
|
1062
1238
|
for (const entry of entries) {
|
|
1063
1239
|
if (!entry.isDirectory()) continue;
|
|
1064
|
-
candidates.push(
|
|
1065
|
-
candidates.push(
|
|
1240
|
+
candidates.push(join2("/home", entry.name, ".claude", ".credentials.json"));
|
|
1241
|
+
candidates.push(join2("/home", entry.name, ".claude", "credentials.json"));
|
|
1066
1242
|
}
|
|
1067
1243
|
} catch {
|
|
1068
1244
|
}
|
|
@@ -1154,18 +1330,18 @@ function normalize(value) {
|
|
|
1154
1330
|
}
|
|
1155
1331
|
|
|
1156
1332
|
// src/lib/channel-hash-cache.ts
|
|
1157
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
1158
|
-
import { join as
|
|
1333
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1334
|
+
import { join as join3 } from "path";
|
|
1159
1335
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
1160
1336
|
function getChannelHashCacheFile(configDir) {
|
|
1161
|
-
return
|
|
1337
|
+
return join3(configDir, CACHE_FILENAME);
|
|
1162
1338
|
}
|
|
1163
1339
|
function loadChannelHashCache(target, configDir) {
|
|
1164
1340
|
const path = getChannelHashCacheFile(configDir);
|
|
1165
|
-
if (!
|
|
1341
|
+
if (!existsSync2(path)) return;
|
|
1166
1342
|
let parsed;
|
|
1167
1343
|
try {
|
|
1168
|
-
parsed = JSON.parse(
|
|
1344
|
+
parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
1169
1345
|
} catch {
|
|
1170
1346
|
return;
|
|
1171
1347
|
}
|
|
@@ -1638,24 +1814,24 @@ function withScheduleLinkFooter(opts) {
|
|
|
1638
1814
|
}
|
|
1639
1815
|
|
|
1640
1816
|
// src/lib/restart-flags.ts
|
|
1641
|
-
import { existsSync as
|
|
1642
|
-
import { homedir as
|
|
1643
|
-
import { join as
|
|
1817
|
+
import { existsSync as existsSync3, mkdirSync, readdirSync, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1818
|
+
import { homedir as homedir3 } from "os";
|
|
1819
|
+
import { join as join4 } from "path";
|
|
1644
1820
|
import { randomUUID } from "crypto";
|
|
1645
1821
|
function restartFlagsDir() {
|
|
1646
|
-
return
|
|
1822
|
+
return join4(homedir3(), ".augmented", "restart-flags");
|
|
1647
1823
|
}
|
|
1648
1824
|
function flagPath(codeName) {
|
|
1649
|
-
return
|
|
1825
|
+
return join4(restartFlagsDir(), `${codeName}.flag`);
|
|
1650
1826
|
}
|
|
1651
1827
|
function readRestartFlags() {
|
|
1652
1828
|
const dir = restartFlagsDir();
|
|
1653
|
-
if (!
|
|
1829
|
+
if (!existsSync3(dir)) return [];
|
|
1654
1830
|
const out = [];
|
|
1655
1831
|
for (const entry of readdirSync(dir)) {
|
|
1656
1832
|
if (!entry.endsWith(".flag")) continue;
|
|
1657
1833
|
try {
|
|
1658
|
-
const raw =
|
|
1834
|
+
const raw = readFileSync3(join4(dir, entry), "utf8");
|
|
1659
1835
|
const parsed = JSON.parse(raw);
|
|
1660
1836
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
1661
1837
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -1673,7 +1849,7 @@ function readRestartFlags() {
|
|
|
1673
1849
|
}
|
|
1674
1850
|
function deleteRestartFlag(codeName) {
|
|
1675
1851
|
const path = flagPath(codeName);
|
|
1676
|
-
if (
|
|
1852
|
+
if (existsSync3(path)) {
|
|
1677
1853
|
rmSync(path, { force: true });
|
|
1678
1854
|
}
|
|
1679
1855
|
}
|
|
@@ -2171,8 +2347,8 @@ function applyRestartAcks(args) {
|
|
|
2171
2347
|
var GATEWAY_PORT_BASE = 18800;
|
|
2172
2348
|
var GATEWAY_PORT_STEP = 10;
|
|
2173
2349
|
var GATEWAY_PORT_MAX = 18899;
|
|
2174
|
-
var AUGMENTED_DIR =
|
|
2175
|
-
var GATEWAY_PORTS_FILE =
|
|
2350
|
+
var AUGMENTED_DIR = join5(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
2351
|
+
var GATEWAY_PORTS_FILE = join5(AUGMENTED_DIR, "gateway-ports.json");
|
|
2176
2352
|
var CHANNEL_SWEEP_INTERVAL_MS = (() => {
|
|
2177
2353
|
const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
|
|
2178
2354
|
if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
|
|
@@ -2279,14 +2455,45 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
|
|
|
2279
2455
|
function shouldSkipRevokedCleanup(previousKnownStatus) {
|
|
2280
2456
|
return previousKnownStatus === "revoked";
|
|
2281
2457
|
}
|
|
2282
|
-
function hasRevokedResiduals(
|
|
2283
|
-
return
|
|
2458
|
+
function hasRevokedResiduals(state4) {
|
|
2459
|
+
return state4.gatewayRunning || state4.portAllocated || state4.provisionDirExists;
|
|
2284
2460
|
}
|
|
2285
2461
|
var knownVersions = /* @__PURE__ */ new Map();
|
|
2286
2462
|
var knownStatuses = /* @__PURE__ */ new Map();
|
|
2287
2463
|
var knownChannels = /* @__PURE__ */ new Map();
|
|
2288
2464
|
var pendingSessionRestarts = /* @__PURE__ */ new Map();
|
|
2289
|
-
|
|
2465
|
+
var restartBreaker = new RestartBreaker();
|
|
2466
|
+
var reportedTrips = /* @__PURE__ */ new Map();
|
|
2467
|
+
function recordRestartForBreaker(codeName, reason) {
|
|
2468
|
+
const result = restartBreaker.record(codeName, reason);
|
|
2469
|
+
if (!result.tripped || !result.trip) return;
|
|
2470
|
+
const trip = result.trip;
|
|
2471
|
+
log(
|
|
2472
|
+
`[persistent-session-decision] agent=${codeName} decision=circuit-tripped detail=${JSON.stringify(trip.statusMessage)} (ENG-5441)`
|
|
2473
|
+
);
|
|
2474
|
+
maybeReportCurrentTrip(codeName);
|
|
2475
|
+
}
|
|
2476
|
+
async function reportCircuitTripped(codeName, trip) {
|
|
2477
|
+
if (reportedTrips.get(codeName) === trip.trippedAt) return;
|
|
2478
|
+
const agentId = codeNameToAgentId.get(codeName);
|
|
2479
|
+
if (!agentId) throw new Error(`no agent_id known yet for '${codeName}' \u2014 will retry next poll`);
|
|
2480
|
+
await api.post("/host/circuit-breaker/trip", {
|
|
2481
|
+
agent_id: agentId,
|
|
2482
|
+
status_message: trip.statusMessage,
|
|
2483
|
+
tripped_at: new Date(trip.trippedAt).toISOString(),
|
|
2484
|
+
events: trip.eventsAtTrip.map((e) => ({ reason: e.reason, at: new Date(e.at).toISOString() }))
|
|
2485
|
+
});
|
|
2486
|
+
reportedTrips.set(codeName, trip.trippedAt);
|
|
2487
|
+
}
|
|
2488
|
+
function maybeReportCurrentTrip(codeName) {
|
|
2489
|
+
const trip = restartBreaker.getTrip(codeName);
|
|
2490
|
+
if (!trip) return;
|
|
2491
|
+
if (reportedTrips.get(codeName) === trip.trippedAt) return;
|
|
2492
|
+
reportCircuitTripped(codeName, trip).catch((err) => {
|
|
2493
|
+
log(`[circuit-breaker] Failed to report trip for '${codeName}' to API: ${err.message}`);
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
function scheduleSessionRestart(codeName, delayMs, reason, breakerReason = "hot-reload-mcp") {
|
|
2290
2497
|
const existing = pendingSessionRestarts.get(codeName);
|
|
2291
2498
|
if (existing) {
|
|
2292
2499
|
clearTimeout(existing);
|
|
@@ -2296,6 +2503,7 @@ function scheduleSessionRestart(codeName, delayMs, reason) {
|
|
|
2296
2503
|
pendingSessionRestarts.delete(codeName);
|
|
2297
2504
|
stopPersistentSession(codeName, log);
|
|
2298
2505
|
runningMcpHashes.delete(codeName);
|
|
2506
|
+
recordRestartForBreaker(codeName, breakerReason);
|
|
2299
2507
|
log(`[hot-reload] Session stopped for '${codeName}' \u2014 will respawn with ${reason}`);
|
|
2300
2508
|
}, delayMs);
|
|
2301
2509
|
timer.unref?.();
|
|
@@ -2314,7 +2522,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
|
2314
2522
|
var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
2315
2523
|
function projectMcpHash(_codeName, projectDir) {
|
|
2316
2524
|
try {
|
|
2317
|
-
const raw =
|
|
2525
|
+
const raw = readFileSync4(join5(projectDir, ".mcp.json"), "utf-8");
|
|
2318
2526
|
return createHash2("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
2319
2527
|
} catch {
|
|
2320
2528
|
return null;
|
|
@@ -2322,7 +2530,7 @@ function projectMcpHash(_codeName, projectDir) {
|
|
|
2322
2530
|
}
|
|
2323
2531
|
function projectMcpKeys(_codeName, projectDir) {
|
|
2324
2532
|
try {
|
|
2325
|
-
const raw =
|
|
2533
|
+
const raw = readFileSync4(join5(projectDir, ".mcp.json"), "utf-8");
|
|
2326
2534
|
const parsed = JSON.parse(raw);
|
|
2327
2535
|
const servers = parsed.mcpServers;
|
|
2328
2536
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -2331,11 +2539,14 @@ function projectMcpKeys(_codeName, projectDir) {
|
|
|
2331
2539
|
return null;
|
|
2332
2540
|
}
|
|
2333
2541
|
}
|
|
2334
|
-
function stopPersistentSessionAndForgetMcpBaseline(codeName) {
|
|
2542
|
+
function stopPersistentSessionAndForgetMcpBaseline(codeName, breakerReason) {
|
|
2335
2543
|
cancelPendingSessionRestart(codeName);
|
|
2336
2544
|
stopPersistentSession(codeName, log);
|
|
2337
2545
|
runningMcpHashes.delete(codeName);
|
|
2338
2546
|
runningMcpServerKeys.delete(codeName);
|
|
2547
|
+
if (breakerReason) {
|
|
2548
|
+
recordRestartForBreaker(codeName, breakerReason);
|
|
2549
|
+
}
|
|
2339
2550
|
}
|
|
2340
2551
|
function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
2341
2552
|
const currentHash = projectMcpHash(codeName, projectDir);
|
|
@@ -2416,7 +2627,7 @@ var codeNameToAgentId = /* @__PURE__ */ new Map();
|
|
|
2416
2627
|
var agentChannelTokens = /* @__PURE__ */ new Map();
|
|
2417
2628
|
var activeChannels = /* @__PURE__ */ new Map();
|
|
2418
2629
|
var gatewaysStartedThisCycle = /* @__PURE__ */ new Set();
|
|
2419
|
-
var
|
|
2630
|
+
var state3 = {
|
|
2420
2631
|
pid: process.pid,
|
|
2421
2632
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2422
2633
|
lastPollAt: null,
|
|
@@ -2450,6 +2661,7 @@ function clearAgentCaches(agentId, codeName) {
|
|
|
2450
2661
|
lastHarvestAt.delete(codeName);
|
|
2451
2662
|
claudeSchedulerStates.delete(codeName);
|
|
2452
2663
|
claudeTaskConcurrency.delete(codeName);
|
|
2664
|
+
lastMcpFailedBannerCount.delete(codeName);
|
|
2453
2665
|
memoryFileHashes.delete(agentId);
|
|
2454
2666
|
lastDownloadHash.delete(agentId);
|
|
2455
2667
|
lastLocalFileHash.delete(agentId);
|
|
@@ -2472,7 +2684,7 @@ var cachedFrameworkVersion = null;
|
|
|
2472
2684
|
var lastVersionCheckAt = 0;
|
|
2473
2685
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2474
2686
|
var lastResponsivenessProbeAt = 0;
|
|
2475
|
-
var agtCliVersion = true ? "0.
|
|
2687
|
+
var agtCliVersion = true ? "0.24.0" : "dev";
|
|
2476
2688
|
function resolveBrewPath(execFileSync4) {
|
|
2477
2689
|
try {
|
|
2478
2690
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -2485,7 +2697,7 @@ function resolveBrewPath(execFileSync4) {
|
|
|
2485
2697
|
"/usr/local/bin/brew"
|
|
2486
2698
|
];
|
|
2487
2699
|
for (const path of fallbacks) {
|
|
2488
|
-
if (
|
|
2700
|
+
if (existsSync4(path)) return path;
|
|
2489
2701
|
}
|
|
2490
2702
|
return null;
|
|
2491
2703
|
}
|
|
@@ -2495,7 +2707,7 @@ function claudeBinaryInstalled(execFileSync4) {
|
|
|
2495
2707
|
"/opt/homebrew/bin/claude",
|
|
2496
2708
|
"/usr/local/bin/claude"
|
|
2497
2709
|
];
|
|
2498
|
-
if (canonical.some((path) =>
|
|
2710
|
+
if (canonical.some((path) => existsSync4(path))) return true;
|
|
2499
2711
|
try {
|
|
2500
2712
|
execFileSync4("which", ["claude"], { timeout: 5e3 });
|
|
2501
2713
|
return true;
|
|
@@ -2677,7 +2889,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
2677
2889
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
2678
2890
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
2679
2891
|
}
|
|
2680
|
-
if (
|
|
2892
|
+
if (existsSync4("/home/linuxbrew/.linuxbrew/bin/claude")) {
|
|
2681
2893
|
log("Claude Code installed successfully");
|
|
2682
2894
|
} else {
|
|
2683
2895
|
log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
|
|
@@ -2689,7 +2901,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
2689
2901
|
var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2690
2902
|
var claudeCodeUpgradeInFlight = false;
|
|
2691
2903
|
function claudeCodeUpgradeMarkerPath() {
|
|
2692
|
-
return
|
|
2904
|
+
return join5(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
|
|
2693
2905
|
}
|
|
2694
2906
|
function stampClaudeCodeUpgradeMarker() {
|
|
2695
2907
|
try {
|
|
@@ -2699,7 +2911,7 @@ function stampClaudeCodeUpgradeMarker() {
|
|
|
2699
2911
|
}
|
|
2700
2912
|
function claudeCodeUpgradeThrottled() {
|
|
2701
2913
|
try {
|
|
2702
|
-
const lastCheck = parseInt(
|
|
2914
|
+
const lastCheck = parseInt(readFileSync4(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
2703
2915
|
if (!Number.isFinite(lastCheck)) return false;
|
|
2704
2916
|
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
2705
2917
|
} catch {
|
|
@@ -2769,7 +2981,7 @@ async function checkAndUpdateCli() {
|
|
|
2769
2981
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
2770
2982
|
if (!isBrewFormula && !isNpmGlobal) return;
|
|
2771
2983
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
2772
|
-
const markerPath =
|
|
2984
|
+
const markerPath = join5(homedir4(), ".augmented", ".last-update-check");
|
|
2773
2985
|
try {
|
|
2774
2986
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
2775
2987
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
@@ -2834,17 +3046,18 @@ async function checkAndUpdateCliViaBrew() {
|
|
|
2834
3046
|
async function checkAndUpdateCliViaNpm() {
|
|
2835
3047
|
const { execFileSync: execFileSync4 } = await import("child_process");
|
|
2836
3048
|
if (agtCliVersion === "dev") return;
|
|
3049
|
+
const channel = process.env.AGT_CLI_RELEASE_CHANNEL || "latest";
|
|
2837
3050
|
let latest;
|
|
2838
3051
|
try {
|
|
2839
3052
|
const res = await fetch(
|
|
2840
|
-
|
|
3053
|
+
`https://registry.npmjs.org/@integrity-labs/agt-cli/${encodeURIComponent(channel)}`,
|
|
2841
3054
|
{
|
|
2842
3055
|
signal: AbortSignal.timeout(1e4),
|
|
2843
3056
|
headers: { Accept: "application/json" }
|
|
2844
3057
|
}
|
|
2845
3058
|
);
|
|
2846
3059
|
if (!res.ok) {
|
|
2847
|
-
log(`[self-update] npm registry returned ${res.status}`);
|
|
3060
|
+
log(`[self-update] npm registry returned ${res.status} (channel=${channel})`);
|
|
2848
3061
|
return;
|
|
2849
3062
|
}
|
|
2850
3063
|
const body = await res.json();
|
|
@@ -2857,14 +3070,15 @@ async function checkAndUpdateCliViaNpm() {
|
|
|
2857
3070
|
log(`[self-update] npm registry fetch failed: ${err.message}`);
|
|
2858
3071
|
return;
|
|
2859
3072
|
}
|
|
2860
|
-
|
|
3073
|
+
const shouldUpdate = channel === "latest" ? isNewerSemver(agtCliVersion, latest) : latest !== agtCliVersion;
|
|
3074
|
+
if (!shouldUpdate) {
|
|
2861
3075
|
if (!selfUpdateUpToDateLogged) {
|
|
2862
|
-
log(`[self-update] agt CLI is up to date (npm, ${agtCliVersion})`);
|
|
3076
|
+
log(`[self-update] agt CLI is up to date (npm, channel=${channel}, ${agtCliVersion})`);
|
|
2863
3077
|
selfUpdateUpToDateLogged = true;
|
|
2864
3078
|
}
|
|
2865
3079
|
return;
|
|
2866
3080
|
}
|
|
2867
|
-
log(`[self-update] agt CLI update available: ${agtCliVersion} \u2192 ${latest}. Upgrading via npm...`);
|
|
3081
|
+
log(`[self-update] agt CLI update available: ${agtCliVersion} \u2192 ${latest} (channel=${channel}). Upgrading via npm...`);
|
|
2868
3082
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
2869
3083
|
const cmd = isRoot ? "npm" : "sudo";
|
|
2870
3084
|
const args = isRoot ? [
|
|
@@ -2931,10 +3145,10 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
2931
3145
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
2932
3146
|
}
|
|
2933
3147
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
2934
|
-
const claudeDir =
|
|
3148
|
+
const claudeDir = join5(homedir4(), ".claude");
|
|
2935
3149
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
2936
|
-
const p =
|
|
2937
|
-
if (
|
|
3150
|
+
const p = join5(claudeDir, filename);
|
|
3151
|
+
if (existsSync4(p)) {
|
|
2938
3152
|
try {
|
|
2939
3153
|
rmSync2(p, { force: true });
|
|
2940
3154
|
log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
|
|
@@ -2948,7 +3162,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
2948
3162
|
}
|
|
2949
3163
|
function loadGatewayPorts() {
|
|
2950
3164
|
try {
|
|
2951
|
-
return JSON.parse(
|
|
3165
|
+
return JSON.parse(readFileSync4(GATEWAY_PORTS_FILE, "utf-8"));
|
|
2952
3166
|
} catch {
|
|
2953
3167
|
return {};
|
|
2954
3168
|
}
|
|
@@ -2978,10 +3192,10 @@ function freePort(codeName) {
|
|
|
2978
3192
|
}
|
|
2979
3193
|
}
|
|
2980
3194
|
function getStateFile() {
|
|
2981
|
-
return
|
|
3195
|
+
return join5(config?.configDir ?? join5(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
2982
3196
|
}
|
|
2983
3197
|
function channelHashCacheDir() {
|
|
2984
|
-
return config?.configDir ??
|
|
3198
|
+
return config?.configDir ?? join5(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
2985
3199
|
}
|
|
2986
3200
|
function loadChannelHashCache2() {
|
|
2987
3201
|
loadChannelHashCache(knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -3023,9 +3237,9 @@ function log(msg) {
|
|
|
3023
3237
|
`;
|
|
3024
3238
|
if (!managerLogPath) {
|
|
3025
3239
|
try {
|
|
3026
|
-
managerLogPath =
|
|
3240
|
+
managerLogPath = join5(homedir4(), ".augmented", "manager.log");
|
|
3027
3241
|
mkdirSync2(dirname(managerLogPath), { recursive: true });
|
|
3028
|
-
if (
|
|
3242
|
+
if (existsSync4(managerLogPath)) {
|
|
3029
3243
|
chmodSync(managerLogPath, 384);
|
|
3030
3244
|
}
|
|
3031
3245
|
} catch {
|
|
@@ -3053,7 +3267,7 @@ function sha256(content) {
|
|
|
3053
3267
|
}
|
|
3054
3268
|
function hashFile(filePath) {
|
|
3055
3269
|
try {
|
|
3056
|
-
const content =
|
|
3270
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
3057
3271
|
return sha256(content);
|
|
3058
3272
|
} catch {
|
|
3059
3273
|
return null;
|
|
@@ -3078,12 +3292,12 @@ function parseSkillFrontmatter(content) {
|
|
|
3078
3292
|
}
|
|
3079
3293
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
3080
3294
|
const { readdirSync: readdirSync3, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync4 } = await import("fs");
|
|
3081
|
-
const skillsDir =
|
|
3082
|
-
const claudeMdPath =
|
|
3295
|
+
const skillsDir = join5(configDir, codeName, "project", ".claude", "skills");
|
|
3296
|
+
const claudeMdPath = join5(configDir, codeName, "project", "CLAUDE.md");
|
|
3083
3297
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
3084
3298
|
const entries = [];
|
|
3085
3299
|
for (const dir of readdirSync3(skillsDir).sort()) {
|
|
3086
|
-
const skillFile =
|
|
3300
|
+
const skillFile = join5(skillsDir, dir, "SKILL.md");
|
|
3087
3301
|
if (!ex(skillFile)) continue;
|
|
3088
3302
|
try {
|
|
3089
3303
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -3131,10 +3345,10 @@ ${SKILLS_INDEX_END}`;
|
|
|
3131
3345
|
}
|
|
3132
3346
|
async function migrateToProfiles() {
|
|
3133
3347
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3134
|
-
const sharedConfigPath =
|
|
3348
|
+
const sharedConfigPath = join5(homeDir, ".openclaw", "openclaw.json");
|
|
3135
3349
|
let sharedConfig;
|
|
3136
3350
|
try {
|
|
3137
|
-
sharedConfig = JSON.parse(
|
|
3351
|
+
sharedConfig = JSON.parse(readFileSync4(sharedConfigPath, "utf-8"));
|
|
3138
3352
|
} catch {
|
|
3139
3353
|
return;
|
|
3140
3354
|
}
|
|
@@ -3147,19 +3361,19 @@ async function migrateToProfiles() {
|
|
|
3147
3361
|
const codeName = agentEntry["id"];
|
|
3148
3362
|
if (!codeName) continue;
|
|
3149
3363
|
if (codeName === "main") continue;
|
|
3150
|
-
const profileDir =
|
|
3151
|
-
if (
|
|
3364
|
+
const profileDir = join5(homeDir, `.openclaw-${codeName}`);
|
|
3365
|
+
if (existsSync4(join5(profileDir, "openclaw.json"))) continue;
|
|
3152
3366
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
3153
3367
|
if (adapter.seedProfileConfig) {
|
|
3154
3368
|
adapter.seedProfileConfig(codeName);
|
|
3155
3369
|
}
|
|
3156
|
-
const sharedAuthDir =
|
|
3157
|
-
const profileAuthDir =
|
|
3158
|
-
const authFile =
|
|
3159
|
-
if (
|
|
3370
|
+
const sharedAuthDir = join5(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
3371
|
+
const profileAuthDir = join5(profileDir, "agents", codeName, "agent");
|
|
3372
|
+
const authFile = join5(sharedAuthDir, "auth-profiles.json");
|
|
3373
|
+
if (existsSync4(authFile)) {
|
|
3160
3374
|
mkdirSync2(profileAuthDir, { recursive: true });
|
|
3161
|
-
const authContent =
|
|
3162
|
-
writeFileSync3(
|
|
3375
|
+
const authContent = readFileSync4(authFile, "utf-8");
|
|
3376
|
+
writeFileSync3(join5(profileAuthDir, "auth-profiles.json"), authContent);
|
|
3163
3377
|
}
|
|
3164
3378
|
allocatePort(codeName);
|
|
3165
3379
|
migrated++;
|
|
@@ -3197,7 +3411,7 @@ function readGatewayToken(codeName) {
|
|
|
3197
3411
|
}
|
|
3198
3412
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3199
3413
|
try {
|
|
3200
|
-
const cfg = JSON.parse(
|
|
3414
|
+
const cfg = JSON.parse(readFileSync4(join5(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
3201
3415
|
return cfg?.gateway?.auth?.token;
|
|
3202
3416
|
} catch {
|
|
3203
3417
|
return void 0;
|
|
@@ -3206,18 +3420,18 @@ function readGatewayToken(codeName) {
|
|
|
3206
3420
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
3207
3421
|
function isGatewayHung(codeName) {
|
|
3208
3422
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3209
|
-
const jobsPath =
|
|
3210
|
-
if (!
|
|
3423
|
+
const jobsPath = join5(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
3424
|
+
if (!existsSync4(jobsPath)) return false;
|
|
3211
3425
|
try {
|
|
3212
|
-
const data = JSON.parse(
|
|
3426
|
+
const data = JSON.parse(readFileSync4(jobsPath, "utf-8"));
|
|
3213
3427
|
const jobs = data.jobs ?? data;
|
|
3214
3428
|
if (!Array.isArray(jobs)) return false;
|
|
3215
3429
|
const now = Date.now();
|
|
3216
3430
|
for (const job of jobs) {
|
|
3217
|
-
const
|
|
3218
|
-
if (!
|
|
3219
|
-
const runStartedAt =
|
|
3220
|
-
const isRunning =
|
|
3431
|
+
const state4 = job.state;
|
|
3432
|
+
if (!state4) continue;
|
|
3433
|
+
const runStartedAt = state4.runStartedAtMs;
|
|
3434
|
+
const isRunning = state4.status === "running" || state4.running === true;
|
|
3221
3435
|
if (isRunning && runStartedAt && now - runStartedAt > GATEWAY_HUNG_TIMEOUT_MS) {
|
|
3222
3436
|
return true;
|
|
3223
3437
|
}
|
|
@@ -3242,15 +3456,15 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
3242
3456
|
}
|
|
3243
3457
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
3244
3458
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3245
|
-
const cronJobsPath =
|
|
3459
|
+
const cronJobsPath = join5(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
3246
3460
|
clearStaleCronRunState(cronJobsPath);
|
|
3247
3461
|
} else {
|
|
3248
3462
|
if (status.port) {
|
|
3249
3463
|
try {
|
|
3250
3464
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3251
|
-
const configPath =
|
|
3252
|
-
if (
|
|
3253
|
-
const cfg = JSON.parse(
|
|
3465
|
+
const configPath = join5(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
3466
|
+
if (existsSync4(configPath)) {
|
|
3467
|
+
const cfg = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
3254
3468
|
if (cfg.gateway?.port !== status.port) {
|
|
3255
3469
|
if (!cfg.gateway) cfg.gateway = {};
|
|
3256
3470
|
cfg.gateway.port = status.port;
|
|
@@ -3274,9 +3488,9 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
3274
3488
|
gatewaysStartedThisCycle.add(codeName);
|
|
3275
3489
|
try {
|
|
3276
3490
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
3277
|
-
const configPath =
|
|
3278
|
-
if (
|
|
3279
|
-
const cfg = JSON.parse(
|
|
3491
|
+
const configPath = join5(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
3492
|
+
if (existsSync4(configPath)) {
|
|
3493
|
+
const cfg = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
3280
3494
|
if (!cfg.gateway) cfg.gateway = {};
|
|
3281
3495
|
cfg.gateway.port = port;
|
|
3282
3496
|
writeFileSync3(configPath, JSON.stringify(cfg, null, 2));
|
|
@@ -3368,7 +3582,7 @@ async function pollCycle() {
|
|
|
3368
3582
|
log,
|
|
3369
3583
|
resolveFramework: (codeName) => agentFrameworkCache.get(codeName) ?? null,
|
|
3370
3584
|
stopSession: (codeName) => {
|
|
3371
|
-
stopPersistentSessionAndForgetMcpBaseline(codeName);
|
|
3585
|
+
stopPersistentSessionAndForgetMcpBaseline(codeName, "channel-set-change");
|
|
3372
3586
|
persistentSessionAgents.delete(codeName);
|
|
3373
3587
|
claudeAuthTupleBySession.delete(codeName);
|
|
3374
3588
|
},
|
|
@@ -3418,7 +3632,7 @@ async function pollCycle() {
|
|
|
3418
3632
|
const now = Date.now();
|
|
3419
3633
|
if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
|
|
3420
3634
|
try {
|
|
3421
|
-
const firstAgent =
|
|
3635
|
+
const firstAgent = state3.agents[0];
|
|
3422
3636
|
const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework("openclaw");
|
|
3423
3637
|
if (versionAdapter.getVersion) {
|
|
3424
3638
|
cachedFrameworkVersion = await versionAdapter.getVersion();
|
|
@@ -3429,7 +3643,7 @@ async function pollCycle() {
|
|
|
3429
3643
|
}
|
|
3430
3644
|
try {
|
|
3431
3645
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
3432
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
3646
|
+
const { collectDiagnostics } = await import("../persistent-session-L4YIJJML.js");
|
|
3433
3647
|
const diagCodeNames = [...persistentSessionAgents];
|
|
3434
3648
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
3435
3649
|
let tailscaleHostname;
|
|
@@ -3480,7 +3694,7 @@ async function pollCycle() {
|
|
|
3480
3694
|
const {
|
|
3481
3695
|
collectResponsivenessProbes,
|
|
3482
3696
|
getResponsivenessIntervalMs
|
|
3483
|
-
} = await import("../responsiveness-probe-
|
|
3697
|
+
} = await import("../responsiveness-probe-YRLESO54.js");
|
|
3484
3698
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
3485
3699
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
3486
3700
|
const probeCodeNames = [...persistentSessionAgents];
|
|
@@ -3497,13 +3711,50 @@ async function pollCycle() {
|
|
|
3497
3711
|
} catch (err) {
|
|
3498
3712
|
log(`[responsiveness-probe] collection failed: ${err.message}`);
|
|
3499
3713
|
}
|
|
3714
|
+
try {
|
|
3715
|
+
const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
|
|
3716
|
+
const observations = [];
|
|
3717
|
+
const pendingCacheCommits = [];
|
|
3718
|
+
for (const codeName of persistentSessionAgents) {
|
|
3719
|
+
const agentId = codeNameToAgentId.get(codeName);
|
|
3720
|
+
if (!agentId) continue;
|
|
3721
|
+
const tail = readPaneLogTail(codeName, 40);
|
|
3722
|
+
if (!tail) continue;
|
|
3723
|
+
const observed = scrapeMcpFailedBannerCount(tail);
|
|
3724
|
+
const count = observed ?? 0;
|
|
3725
|
+
const last = lastMcpFailedBannerCount.get(codeName);
|
|
3726
|
+
if (last === count) continue;
|
|
3727
|
+
observations.push({ agent_id: agentId, count });
|
|
3728
|
+
pendingCacheCommits.push({ codeName, count });
|
|
3729
|
+
}
|
|
3730
|
+
if (observations.length > 0) {
|
|
3731
|
+
void api.post(
|
|
3732
|
+
"/host/mcp-banner-observation",
|
|
3733
|
+
{ observations }
|
|
3734
|
+
).then((resp) => {
|
|
3735
|
+
const accepted = new Set(
|
|
3736
|
+
(resp?.results ?? []).filter((r) => r.ok).map((r) => r.agent_id)
|
|
3737
|
+
);
|
|
3738
|
+
for (const { codeName, count } of pendingCacheCommits) {
|
|
3739
|
+
const agentId = codeNameToAgentId.get(codeName);
|
|
3740
|
+
if (agentId && accepted.has(agentId)) {
|
|
3741
|
+
lastMcpFailedBannerCount.set(codeName, count);
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
}).catch((err) => {
|
|
3745
|
+
log(`[mcp-banner] post failed: ${err.message}`);
|
|
3746
|
+
});
|
|
3747
|
+
}
|
|
3748
|
+
} catch (err) {
|
|
3749
|
+
log(`[mcp-banner] collection failed: ${err.message}`);
|
|
3750
|
+
}
|
|
3500
3751
|
const data = await api.post("/host/agents", { host_id: hostId });
|
|
3501
3752
|
const agents = data.agents ?? [];
|
|
3502
3753
|
const restartAcks = /* @__PURE__ */ new Map();
|
|
3503
3754
|
for (const agent of agents) {
|
|
3504
3755
|
const requested = agent.restart_requested_at ?? null;
|
|
3505
3756
|
if (!requested) continue;
|
|
3506
|
-
const prev =
|
|
3757
|
+
const prev = state3.agents.find((a) => a.agentId === agent.agent_id);
|
|
3507
3758
|
const lastProcessed = prev?.lastRestartProcessedAt ?? null;
|
|
3508
3759
|
if (lastProcessed && Date.parse(lastProcessed) >= Date.parse(requested)) continue;
|
|
3509
3760
|
log(`[restart] Dashboard requested restart for '${agent.code_name}' at ${requested}`);
|
|
@@ -3526,7 +3777,7 @@ async function pollCycle() {
|
|
|
3526
3777
|
await processAgent(agent, agentStates);
|
|
3527
3778
|
} catch (err) {
|
|
3528
3779
|
log(`Error processing agent '${agent.code_name}': ${err.message}`);
|
|
3529
|
-
const existing =
|
|
3780
|
+
const existing = state3.agents.find((a) => a.agentId === agent.agent_id);
|
|
3530
3781
|
if (existing) {
|
|
3531
3782
|
agentStates.push(existing);
|
|
3532
3783
|
} else {
|
|
@@ -3549,14 +3800,15 @@ async function pollCycle() {
|
|
|
3549
3800
|
}
|
|
3550
3801
|
}
|
|
3551
3802
|
}
|
|
3803
|
+
void maybeReportActivityCache({ api, log });
|
|
3552
3804
|
const restartAckStateChanged = applyRestartAcks({
|
|
3553
3805
|
agentStates,
|
|
3554
|
-
priorAgents:
|
|
3806
|
+
priorAgents: state3.agents,
|
|
3555
3807
|
restartAcks
|
|
3556
3808
|
});
|
|
3557
3809
|
if (restartAckStateChanged) {
|
|
3558
3810
|
try {
|
|
3559
|
-
const ackedState = { ...
|
|
3811
|
+
const ackedState = { ...state3, agents: agentStates };
|
|
3560
3812
|
writeFileSync3(getStateFile(), JSON.stringify(ackedState, null, 2));
|
|
3561
3813
|
} catch (err) {
|
|
3562
3814
|
log(`[restart] failed to persist ack immediately: ${err.message}`);
|
|
@@ -3574,7 +3826,7 @@ async function pollCycle() {
|
|
|
3574
3826
|
} catch {
|
|
3575
3827
|
}
|
|
3576
3828
|
const currentIds = new Set(agents.map((a) => a.agent_id));
|
|
3577
|
-
for (const prev of
|
|
3829
|
+
for (const prev of state3.agents) {
|
|
3578
3830
|
if (!currentIds.has(prev.agentId)) {
|
|
3579
3831
|
log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
|
|
3580
3832
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
@@ -3587,7 +3839,7 @@ async function pollCycle() {
|
|
|
3587
3839
|
}
|
|
3588
3840
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
3589
3841
|
freePort(prev.codeName);
|
|
3590
|
-
const agentDir =
|
|
3842
|
+
const agentDir = join5(adapter.getAgentDir(prev.codeName), "provision");
|
|
3591
3843
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
3592
3844
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
3593
3845
|
}
|
|
@@ -3674,19 +3926,23 @@ async function pollCycle() {
|
|
|
3674
3926
|
}
|
|
3675
3927
|
} catch {
|
|
3676
3928
|
}
|
|
3677
|
-
|
|
3678
|
-
...
|
|
3929
|
+
state3 = {
|
|
3930
|
+
...state3,
|
|
3679
3931
|
lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3680
|
-
pollCount:
|
|
3681
|
-
agents: agentStates
|
|
3932
|
+
pollCount: state3.pollCount + 1,
|
|
3933
|
+
agents: agentStates,
|
|
3934
|
+
// ENG-5441: serialise trip state on every poll so manager restarts
|
|
3935
|
+
// never silently clear a tripped breaker. Cheap — only tripped
|
|
3936
|
+
// entries are stored, and the typical state is zero entries.
|
|
3937
|
+
circuitBreakerTrips: restartBreaker.serialize()
|
|
3682
3938
|
};
|
|
3683
3939
|
if (consecutivePollFailures > 0) {
|
|
3684
3940
|
log(`[poll-backoff] recovered after ${consecutivePollFailures} failure(s), resuming normal interval`);
|
|
3685
3941
|
consecutivePollFailures = 0;
|
|
3686
3942
|
}
|
|
3687
|
-
send({ type: "state-update", state:
|
|
3943
|
+
send({ type: "state-update", state: state3 });
|
|
3688
3944
|
} catch (err) {
|
|
3689
|
-
|
|
3945
|
+
state3.errorCount++;
|
|
3690
3946
|
const message = err.message;
|
|
3691
3947
|
log(`Poll error: ${message}`);
|
|
3692
3948
|
send({ type: "error", message });
|
|
@@ -3713,6 +3969,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3713
3969
|
const previousKnownStatus = knownStatuses.get(agent.agent_id);
|
|
3714
3970
|
agentDisplayNames.set(agent.code_name, agent.display_name);
|
|
3715
3971
|
codeNameToAgentId.set(agent.code_name, agent.agent_id);
|
|
3972
|
+
maybeReportCurrentTrip(agent.code_name);
|
|
3716
3973
|
if (agent.framework) {
|
|
3717
3974
|
agentFrameworkCache.set(agent.code_name, agent.framework);
|
|
3718
3975
|
}
|
|
@@ -3726,7 +3983,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3726
3983
|
}
|
|
3727
3984
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3728
3985
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
3729
|
-
let agentDir =
|
|
3986
|
+
let agentDir = join5(adapter.getAgentDir(agent.code_name), "provision");
|
|
3730
3987
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
3731
3988
|
if (previousKnownStatus !== agent.status) {
|
|
3732
3989
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -3765,7 +4022,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3765
4022
|
const residuals = {
|
|
3766
4023
|
gatewayRunning: gatewayLiveness.running,
|
|
3767
4024
|
portAllocated: Object.prototype.hasOwnProperty.call(ports, agent.code_name),
|
|
3768
|
-
provisionDirExists:
|
|
4025
|
+
provisionDirExists: existsSync4(agentDir)
|
|
3769
4026
|
};
|
|
3770
4027
|
if (!hasRevokedResiduals(residuals)) {
|
|
3771
4028
|
agentStates.push({
|
|
@@ -3825,6 +4082,11 @@ async function processAgent(agent, agentStates) {
|
|
|
3825
4082
|
if (previousStatus && previousStatus !== agent.status) {
|
|
3826
4083
|
log(`Agent '${agent.code_name}' status changed: ${previousStatus} \u2192 ${agent.status}`);
|
|
3827
4084
|
knownVersions.delete(agent.agent_id);
|
|
4085
|
+
if (previousStatus === "paused" && agent.status === "active" && restartBreaker.isTripped(agent.code_name)) {
|
|
4086
|
+
restartBreaker.clear(agent.code_name);
|
|
4087
|
+
reportedTrips.delete(agent.code_name);
|
|
4088
|
+
log(`[circuit-breaker] Cleared trip for '${agent.code_name}' on operator resume (paused \u2192 active)`);
|
|
4089
|
+
}
|
|
3828
4090
|
let channelCacheMutated = false;
|
|
3829
4091
|
for (const key of knownChannelConfigHashes.keys()) {
|
|
3830
4092
|
if (key.startsWith(`${agent.agent_id}:`)) {
|
|
@@ -3842,7 +4104,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3842
4104
|
});
|
|
3843
4105
|
} catch (err) {
|
|
3844
4106
|
log(`Refresh failed for '${agent.code_name}': ${err.message}`);
|
|
3845
|
-
const existing =
|
|
4107
|
+
const existing = state3.agents.find((a) => a.agentId === agent.agent_id);
|
|
3846
4108
|
agentStates.push(existing ?? {
|
|
3847
4109
|
agentId: agent.agent_id,
|
|
3848
4110
|
codeName: agent.code_name,
|
|
@@ -3890,7 +4152,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3890
4152
|
const frameworkId = refreshData.agent.framework ?? "openclaw";
|
|
3891
4153
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
3892
4154
|
const frameworkAdapter = getFramework(frameworkId);
|
|
3893
|
-
agentDir =
|
|
4155
|
+
agentDir = join5(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
3894
4156
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
3895
4157
|
if (frameworkAdapter.seedProfileConfig) {
|
|
3896
4158
|
frameworkAdapter.seedProfileConfig(agent.code_name);
|
|
@@ -3898,7 +4160,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3898
4160
|
const charterVersion = refreshData.charter.version;
|
|
3899
4161
|
const toolsVersion = refreshData.tools.version;
|
|
3900
4162
|
const known = knownVersions.get(agent.agent_id);
|
|
3901
|
-
let lastProvisionAt =
|
|
4163
|
+
let lastProvisionAt = state3.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
|
|
3902
4164
|
const currentChannelIds = launchableChannelIds(refreshData.channel_configs);
|
|
3903
4165
|
const previousChannelIds = knownChannels.get(agent.agent_id);
|
|
3904
4166
|
const channelsChanged = !previousChannelIds || currentChannelIds.size !== previousChannelIds.size || [...currentChannelIds].some((ch) => !previousChannelIds.has(ch)) || [...previousChannelIds].some((ch) => !currentChannelIds.has(ch));
|
|
@@ -3921,7 +4183,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3921
4183
|
const changedFiles = [];
|
|
3922
4184
|
mkdirSync2(agentDir, { recursive: true });
|
|
3923
4185
|
for (const artifact of artifacts) {
|
|
3924
|
-
const filePath =
|
|
4186
|
+
const filePath = join5(agentDir, artifact.relativePath);
|
|
3925
4187
|
let existingHash;
|
|
3926
4188
|
let newHash;
|
|
3927
4189
|
let writeContent = artifact.content;
|
|
@@ -3933,8 +4195,8 @@ async function processAgent(agent, agentStates) {
|
|
|
3933
4195
|
};
|
|
3934
4196
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
3935
4197
|
try {
|
|
3936
|
-
const projectClaudeMd =
|
|
3937
|
-
const existing =
|
|
4198
|
+
const projectClaudeMd = join5(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
4199
|
+
const existing = readFileSync4(projectClaudeMd, "utf-8");
|
|
3938
4200
|
existingHash = sha256(stripDynamicSections(existing));
|
|
3939
4201
|
} catch {
|
|
3940
4202
|
existingHash = null;
|
|
@@ -3952,7 +4214,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3952
4214
|
const generatorKeys = Object.keys(generatorServers);
|
|
3953
4215
|
let existingRaw = "";
|
|
3954
4216
|
try {
|
|
3955
|
-
existingRaw =
|
|
4217
|
+
existingRaw = readFileSync4(filePath, "utf-8");
|
|
3956
4218
|
} catch {
|
|
3957
4219
|
}
|
|
3958
4220
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -3974,22 +4236,22 @@ async function processAgent(agent, agentStates) {
|
|
|
3974
4236
|
}
|
|
3975
4237
|
}
|
|
3976
4238
|
if (changedFiles.length > 0) {
|
|
3977
|
-
const isFirst = !
|
|
4239
|
+
const isFirst = !existsSync4(join5(agentDir, "CHARTER.md"));
|
|
3978
4240
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
3979
4241
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
3980
4242
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
3981
4243
|
for (const file of changedFiles) {
|
|
3982
|
-
const filePath =
|
|
4244
|
+
const filePath = join5(agentDir, file.relativePath);
|
|
3983
4245
|
mkdirSync2(dirname(filePath), { recursive: true });
|
|
3984
4246
|
writeFileSync3(filePath, file.content);
|
|
3985
4247
|
}
|
|
3986
4248
|
try {
|
|
3987
|
-
const provSkillsDir =
|
|
3988
|
-
if (
|
|
4249
|
+
const provSkillsDir = join5(agentDir, ".claude", "skills");
|
|
4250
|
+
if (existsSync4(provSkillsDir)) {
|
|
3989
4251
|
for (const folder of readdirSync2(provSkillsDir)) {
|
|
3990
4252
|
if (folder.startsWith("knowledge-")) {
|
|
3991
4253
|
try {
|
|
3992
|
-
rmSync2(
|
|
4254
|
+
rmSync2(join5(provSkillsDir, folder), { recursive: true });
|
|
3993
4255
|
} catch {
|
|
3994
4256
|
}
|
|
3995
4257
|
}
|
|
@@ -4002,7 +4264,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4002
4264
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
4003
4265
|
const hashes = /* @__PURE__ */ new Map();
|
|
4004
4266
|
for (const file of trackedFiles2) {
|
|
4005
|
-
const h = hashFile(
|
|
4267
|
+
const h = hashFile(join5(agentDir, file));
|
|
4006
4268
|
if (h) hashes.set(file, h);
|
|
4007
4269
|
}
|
|
4008
4270
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -4046,10 +4308,10 @@ async function processAgent(agent, agentStates) {
|
|
|
4046
4308
|
}
|
|
4047
4309
|
let lastDriftCheckAt = now;
|
|
4048
4310
|
const written = writtenHashes.get(agent.agent_id);
|
|
4049
|
-
if (written &&
|
|
4311
|
+
if (written && existsSync4(agentDir)) {
|
|
4050
4312
|
const driftedFiles = [];
|
|
4051
4313
|
for (const [file, expectedHash] of written) {
|
|
4052
|
-
const localHash = hashFile(
|
|
4314
|
+
const localHash = hashFile(join5(agentDir, file));
|
|
4053
4315
|
if (localHash && localHash !== expectedHash) {
|
|
4054
4316
|
driftedFiles.push(file);
|
|
4055
4317
|
}
|
|
@@ -4060,7 +4322,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4060
4322
|
try {
|
|
4061
4323
|
const localHashes = {};
|
|
4062
4324
|
for (const file of driftedFiles) {
|
|
4063
|
-
localHashes[file] = hashFile(
|
|
4325
|
+
localHashes[file] = hashFile(join5(agentDir, file));
|
|
4064
4326
|
}
|
|
4065
4327
|
await api.post("/host/drift", {
|
|
4066
4328
|
agent_id: agent.agent_id,
|
|
@@ -4218,24 +4480,24 @@ async function processAgent(agent, agentStates) {
|
|
|
4218
4480
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
4219
4481
|
try {
|
|
4220
4482
|
const agentProvisionDir = agentDir;
|
|
4221
|
-
const projectDir =
|
|
4483
|
+
const projectDir = join5(homedir4(), ".augmented", agent.code_name, "project");
|
|
4222
4484
|
mkdirSync2(agentProvisionDir, { recursive: true });
|
|
4223
4485
|
mkdirSync2(projectDir, { recursive: true });
|
|
4224
|
-
const provisionMcpPath =
|
|
4225
|
-
const projectMcpPath =
|
|
4486
|
+
const provisionMcpPath = join5(agentProvisionDir, ".mcp.json");
|
|
4487
|
+
const projectMcpPath = join5(projectDir, ".mcp.json");
|
|
4226
4488
|
let mcpConfig = { mcpServers: {} };
|
|
4227
4489
|
try {
|
|
4228
|
-
mcpConfig = JSON.parse(
|
|
4490
|
+
mcpConfig = JSON.parse(readFileSync4(provisionMcpPath, "utf-8"));
|
|
4229
4491
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
4230
4492
|
} catch {
|
|
4231
4493
|
}
|
|
4232
|
-
const localDirectChatChannel =
|
|
4494
|
+
const localDirectChatChannel = join5(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
4233
4495
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
4234
4496
|
const directChatTz = (() => {
|
|
4235
4497
|
const tz = directChatTeamSettings?.["timezone"];
|
|
4236
4498
|
return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
|
|
4237
4499
|
})();
|
|
4238
|
-
if (
|
|
4500
|
+
if (existsSync4(localDirectChatChannel) && !mcpConfig.mcpServers["direct-chat"]) {
|
|
4239
4501
|
mcpConfig.mcpServers["direct-chat"] = {
|
|
4240
4502
|
command: "node",
|
|
4241
4503
|
args: [localDirectChatChannel],
|
|
@@ -4251,8 +4513,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4251
4513
|
writeFileSync3(projectMcpPath, serialized);
|
|
4252
4514
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
4253
4515
|
}
|
|
4254
|
-
const staleChannelsPath =
|
|
4255
|
-
if (
|
|
4516
|
+
const staleChannelsPath = join5(projectDir, ".mcp-channels.json");
|
|
4517
|
+
if (existsSync4(staleChannelsPath)) {
|
|
4256
4518
|
try {
|
|
4257
4519
|
rmSync2(staleChannelsPath, { force: true });
|
|
4258
4520
|
} catch {
|
|
@@ -4262,7 +4524,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4262
4524
|
log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
|
|
4263
4525
|
}
|
|
4264
4526
|
}
|
|
4265
|
-
let lastSecretsProvisionAt =
|
|
4527
|
+
let lastSecretsProvisionAt = state3.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
|
|
4266
4528
|
let secretsHash = knownSecretsHashes.get(agent.agent_id) ?? null;
|
|
4267
4529
|
try {
|
|
4268
4530
|
const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
|
|
@@ -4318,11 +4580,11 @@ async function processAgent(agent, agentStates) {
|
|
|
4318
4580
|
const intHash = computeIntegrationsHash(integrations);
|
|
4319
4581
|
const prevIntHash = knownIntegrationHashes.get(agent.agent_id);
|
|
4320
4582
|
if (intHash !== prevIntHash) {
|
|
4321
|
-
const projectDir =
|
|
4322
|
-
const envIntPath =
|
|
4583
|
+
const projectDir = join5(homedir4(), ".augmented", agent.code_name, "project");
|
|
4584
|
+
const envIntPath = join5(projectDir, ".env.integrations");
|
|
4323
4585
|
let preWriteEnv;
|
|
4324
4586
|
try {
|
|
4325
|
-
preWriteEnv =
|
|
4587
|
+
preWriteEnv = readFileSync4(envIntPath, "utf-8");
|
|
4326
4588
|
} catch {
|
|
4327
4589
|
preWriteEnv = void 0;
|
|
4328
4590
|
}
|
|
@@ -4334,9 +4596,9 @@ async function processAgent(agent, agentStates) {
|
|
|
4334
4596
|
let rotationHandled = true;
|
|
4335
4597
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
4336
4598
|
try {
|
|
4337
|
-
const projectMcpPath =
|
|
4338
|
-
const postWriteEnv =
|
|
4339
|
-
const mcpContent =
|
|
4599
|
+
const projectMcpPath = join5(projectDir, ".mcp.json");
|
|
4600
|
+
const postWriteEnv = readFileSync4(envIntPath, "utf-8");
|
|
4601
|
+
const mcpContent = readFileSync4(projectMcpPath, "utf-8");
|
|
4340
4602
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
4341
4603
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
4342
4604
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -4402,8 +4664,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4402
4664
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
4403
4665
|
if (mcpPath) {
|
|
4404
4666
|
try {
|
|
4405
|
-
const { readFileSync:
|
|
4406
|
-
const mcpConfig = JSON.parse(
|
|
4667
|
+
const { readFileSync: readFileSync5 } = await import("fs");
|
|
4668
|
+
const mcpConfig = JSON.parse(readFileSync5(mcpPath, "utf-8"));
|
|
4407
4669
|
if (mcpConfig.mcpServers) {
|
|
4408
4670
|
const managedPrefixes = [
|
|
4409
4671
|
"composio_",
|
|
@@ -4492,8 +4754,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4492
4754
|
if (agent.status === "active") {
|
|
4493
4755
|
if (frameworkAdapter.installPlugin) {
|
|
4494
4756
|
try {
|
|
4495
|
-
const pluginPath =
|
|
4496
|
-
if (
|
|
4757
|
+
const pluginPath = join5(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
4758
|
+
if (existsSync4(pluginPath)) {
|
|
4497
4759
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
4498
4760
|
agtHost: requireHost(),
|
|
4499
4761
|
agtApiKey: getApiKey() ?? void 0,
|
|
@@ -4559,20 +4821,20 @@ async function processAgent(agent, agentStates) {
|
|
|
4559
4821
|
}
|
|
4560
4822
|
try {
|
|
4561
4823
|
const { readdirSync: readdirSync3, rmSync: rmSync3 } = await import("fs");
|
|
4562
|
-
const { homedir:
|
|
4824
|
+
const { homedir: homedir5 } = await import("os");
|
|
4563
4825
|
const frameworkId2 = frameworkAdapter.id;
|
|
4564
4826
|
const candidateSkillDirs = [
|
|
4565
4827
|
// Claude Code — framework runtime tree
|
|
4566
|
-
|
|
4828
|
+
join5(homedir5(), ".augmented", agent.code_name, "skills"),
|
|
4567
4829
|
// Claude Code — project tree
|
|
4568
|
-
|
|
4830
|
+
join5(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
4569
4831
|
// OpenClaw — framework runtime tree
|
|
4570
|
-
|
|
4832
|
+
join5(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
|
|
4571
4833
|
// Defensive: legacy provision-side path, not currently an
|
|
4572
4834
|
// install target but cheap to sweep.
|
|
4573
|
-
|
|
4835
|
+
join5(agentDir, ".claude", "skills")
|
|
4574
4836
|
];
|
|
4575
|
-
const existingDirs = candidateSkillDirs.filter((d) =>
|
|
4837
|
+
const existingDirs = candidateSkillDirs.filter((d) => existsSync4(d));
|
|
4576
4838
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
4577
4839
|
for (const dir of existingDirs) {
|
|
4578
4840
|
try {
|
|
@@ -4586,8 +4848,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4586
4848
|
}
|
|
4587
4849
|
const removeSkillFolder = (entry, reason) => {
|
|
4588
4850
|
for (const dir of existingDirs) {
|
|
4589
|
-
const p =
|
|
4590
|
-
if (
|
|
4851
|
+
const p = join5(dir, entry);
|
|
4852
|
+
if (existsSync4(p)) {
|
|
4591
4853
|
rmSync3(p, { recursive: true, force: true });
|
|
4592
4854
|
}
|
|
4593
4855
|
}
|
|
@@ -4747,8 +5009,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4747
5009
|
const sess = getSessionState(agent.code_name);
|
|
4748
5010
|
let mcpJsonParsed = null;
|
|
4749
5011
|
try {
|
|
4750
|
-
const mcpPath =
|
|
4751
|
-
mcpJsonParsed = JSON.parse(
|
|
5012
|
+
const mcpPath = join5(getProjectDir(agent.code_name), ".mcp.json");
|
|
5013
|
+
mcpJsonParsed = JSON.parse(readFileSync4(mcpPath, "utf-8"));
|
|
4752
5014
|
} catch {
|
|
4753
5015
|
}
|
|
4754
5016
|
reapMissingMcpSessions({
|
|
@@ -4756,7 +5018,12 @@ async function processAgent(agent, agentStates) {
|
|
|
4756
5018
|
codeName: agent.code_name,
|
|
4757
5019
|
mcpJson: mcpJsonParsed,
|
|
4758
5020
|
sessionStartedAt: sess?.startedAt ?? null,
|
|
4759
|
-
|
|
5021
|
+
// ENG-5441: mcp-presence-reaper-driven restart counts against
|
|
5022
|
+
// the breaker. Each declared MCP has its own ENG-5279 give-up
|
|
5023
|
+
// cap (~3 cycles per server), but a thrashing collection of
|
|
5024
|
+
// MCPs can still rotate restarts of the whole session. The
|
|
5025
|
+
// breaker catches the rotating case the per-MCP cap can't see.
|
|
5026
|
+
stopSession: (codeName) => stopPersistentSessionAndForgetMcpBaseline(codeName, "mcp-presence-reaper"),
|
|
4760
5027
|
// ENG-5292: when the reaper gives up on a managed MCP (cap from
|
|
4761
5028
|
// ENG-5279 + state-preservation from ENG-5285 both said "this
|
|
4762
5029
|
// MCP keeps failing after 3 restart cycles"), mark the matching
|
|
@@ -4892,10 +5159,10 @@ async function processAgent(agent, agentStates) {
|
|
|
4892
5159
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
4893
5160
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
4894
5161
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4895
|
-
const jobsPath =
|
|
4896
|
-
if (
|
|
5162
|
+
const jobsPath = join5(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
5163
|
+
if (existsSync4(jobsPath)) {
|
|
4897
5164
|
try {
|
|
4898
|
-
const jobsData = JSON.parse(
|
|
5165
|
+
const jobsData = JSON.parse(readFileSync4(jobsPath, "utf-8"));
|
|
4899
5166
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
4900
5167
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
4901
5168
|
);
|
|
@@ -5036,10 +5303,10 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
5036
5303
|
}
|
|
5037
5304
|
}
|
|
5038
5305
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
5039
|
-
if (trackedFiles.length > 0 &&
|
|
5306
|
+
if (trackedFiles.length > 0 && existsSync4(agentDir)) {
|
|
5040
5307
|
const hashes = /* @__PURE__ */ new Map();
|
|
5041
5308
|
for (const file of trackedFiles) {
|
|
5042
|
-
const h = hashFile(
|
|
5309
|
+
const h = hashFile(join5(agentDir, file));
|
|
5043
5310
|
if (h) hashes.set(file, h);
|
|
5044
5311
|
}
|
|
5045
5312
|
writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -5073,19 +5340,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
5073
5340
|
lastCleanupAt.set(codeName, Date.now());
|
|
5074
5341
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
5075
5342
|
for (const agentDir of ["main", codeName]) {
|
|
5076
|
-
const sessionsDir =
|
|
5343
|
+
const sessionsDir = join5(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
5077
5344
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
5078
5345
|
}
|
|
5079
|
-
const cronRunsDir =
|
|
5346
|
+
const cronRunsDir = join5(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
5080
5347
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
5081
|
-
const cronJobsPath =
|
|
5348
|
+
const cronJobsPath = join5(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
5082
5349
|
clearStaleCronRunState(cronJobsPath);
|
|
5083
5350
|
}
|
|
5084
5351
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
5085
|
-
const indexPath =
|
|
5086
|
-
if (!
|
|
5352
|
+
const indexPath = join5(sessionsDir, "sessions.json");
|
|
5353
|
+
if (!existsSync4(indexPath)) return;
|
|
5087
5354
|
try {
|
|
5088
|
-
const raw =
|
|
5355
|
+
const raw = readFileSync4(indexPath, "utf-8");
|
|
5089
5356
|
const index = JSON.parse(raw);
|
|
5090
5357
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
5091
5358
|
key: k,
|
|
@@ -5098,9 +5365,9 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5098
5365
|
for (const entry of toDelete) {
|
|
5099
5366
|
delete index[entry.key];
|
|
5100
5367
|
if (entry.sessionId) {
|
|
5101
|
-
const sessionFile =
|
|
5368
|
+
const sessionFile = join5(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
5102
5369
|
try {
|
|
5103
|
-
if (
|
|
5370
|
+
if (existsSync4(sessionFile)) {
|
|
5104
5371
|
unlinkSync(sessionFile);
|
|
5105
5372
|
deletedFiles++;
|
|
5106
5373
|
}
|
|
@@ -5120,8 +5387,8 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5120
5387
|
delete index[parentKey];
|
|
5121
5388
|
if (parentSessionId) {
|
|
5122
5389
|
try {
|
|
5123
|
-
const f =
|
|
5124
|
-
if (
|
|
5390
|
+
const f = join5(sessionsDir, `${parentSessionId}.jsonl`);
|
|
5391
|
+
if (existsSync4(f)) {
|
|
5125
5392
|
unlinkSync(f);
|
|
5126
5393
|
deletedFiles++;
|
|
5127
5394
|
}
|
|
@@ -5139,28 +5406,28 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
5139
5406
|
}
|
|
5140
5407
|
var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
5141
5408
|
function clearStaleCronRunState(jobsPath) {
|
|
5142
|
-
if (!
|
|
5409
|
+
if (!existsSync4(jobsPath)) return;
|
|
5143
5410
|
try {
|
|
5144
|
-
const raw =
|
|
5411
|
+
const raw = readFileSync4(jobsPath, "utf-8");
|
|
5145
5412
|
const data = JSON.parse(raw);
|
|
5146
5413
|
const jobs = data.jobs ?? data;
|
|
5147
5414
|
if (!Array.isArray(jobs)) return;
|
|
5148
5415
|
let changed = false;
|
|
5149
5416
|
const now = Date.now();
|
|
5150
5417
|
for (const job of jobs) {
|
|
5151
|
-
const
|
|
5152
|
-
if (!
|
|
5153
|
-
const runStartedAt =
|
|
5154
|
-
const isRunning =
|
|
5418
|
+
const state4 = job.state;
|
|
5419
|
+
if (!state4) continue;
|
|
5420
|
+
const runStartedAt = state4.runningAtMs ?? state4.runStartedAtMs;
|
|
5421
|
+
const isRunning = state4.running === true || state4.status === "running";
|
|
5155
5422
|
if (isRunning && runStartedAt && now - runStartedAt > STALE_RUN_TIMEOUT_MS) {
|
|
5156
|
-
|
|
5157
|
-
delete
|
|
5158
|
-
delete
|
|
5159
|
-
delete
|
|
5423
|
+
state4.running = false;
|
|
5424
|
+
delete state4.status;
|
|
5425
|
+
delete state4.runStartedAtMs;
|
|
5426
|
+
delete state4.currentRunId;
|
|
5160
5427
|
changed = true;
|
|
5161
5428
|
log(`Cleared stale running state for cron job '${job.name}' (stuck for ${Math.round((now - runStartedAt) / 6e4)}min)`);
|
|
5162
5429
|
} else if (isRunning && !runStartedAt) {
|
|
5163
|
-
|
|
5430
|
+
state4.running = false;
|
|
5164
5431
|
changed = true;
|
|
5165
5432
|
log(`Cleared stale running state for cron job '${job.name}' (no start timestamp)`);
|
|
5166
5433
|
}
|
|
@@ -5172,13 +5439,13 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
5172
5439
|
}
|
|
5173
5440
|
}
|
|
5174
5441
|
function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
5175
|
-
if (!
|
|
5442
|
+
if (!existsSync4(dir)) return;
|
|
5176
5443
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
5177
5444
|
let removed = 0;
|
|
5178
5445
|
try {
|
|
5179
5446
|
for (const f of readdirSync2(dir)) {
|
|
5180
5447
|
if (!f.endsWith(ext)) continue;
|
|
5181
|
-
const fullPath =
|
|
5448
|
+
const fullPath = join5(dir, f);
|
|
5182
5449
|
try {
|
|
5183
5450
|
const st = statSync(fullPath);
|
|
5184
5451
|
if (st.mtimeMs < cutoff) {
|
|
@@ -5224,16 +5491,16 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
5224
5491
|
enabled: t.enabled ?? true,
|
|
5225
5492
|
triggered_at: t.triggered_at ?? null
|
|
5226
5493
|
}));
|
|
5227
|
-
const
|
|
5228
|
-
claudeSchedulerStates.set(codeName,
|
|
5494
|
+
const state5 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
5495
|
+
claudeSchedulerStates.set(codeName, state5);
|
|
5229
5496
|
knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
5230
5497
|
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
5231
5498
|
}
|
|
5232
5499
|
if (!claudeSchedulerStates.has(codeName)) {
|
|
5233
5500
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
5234
5501
|
}
|
|
5235
|
-
const
|
|
5236
|
-
const ready = getReadyTasks(
|
|
5502
|
+
const state4 = claudeSchedulerStates.get(codeName);
|
|
5503
|
+
const ready = getReadyTasks(state4, inFlightClaudeTasks);
|
|
5237
5504
|
if (ready.length === 0) return;
|
|
5238
5505
|
for (const task of ready) {
|
|
5239
5506
|
if ((claudeTaskConcurrency.get(codeName) ?? 0) >= MAX_CLAUDE_CONCURRENCY) break;
|
|
@@ -5322,7 +5589,7 @@ async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
|
5322
5589
|
}
|
|
5323
5590
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
5324
5591
|
const projectDir = getProjectDir2(codeName);
|
|
5325
|
-
const mcpConfigPath =
|
|
5592
|
+
const mcpConfigPath = join5(projectDir, ".mcp.json");
|
|
5326
5593
|
let runId = null;
|
|
5327
5594
|
let kanbanItemId = null;
|
|
5328
5595
|
let taskResult;
|
|
@@ -5330,11 +5597,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5330
5597
|
const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
|
|
5331
5598
|
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
|
|
5332
5599
|
try {
|
|
5333
|
-
const claudeMdPath =
|
|
5600
|
+
const claudeMdPath = join5(projectDir, "CLAUDE.md");
|
|
5334
5601
|
const serverNames = [];
|
|
5335
|
-
if (
|
|
5602
|
+
if (existsSync4(mcpConfigPath)) {
|
|
5336
5603
|
try {
|
|
5337
|
-
const d = JSON.parse(
|
|
5604
|
+
const d = JSON.parse(readFileSync4(mcpConfigPath, "utf-8"));
|
|
5338
5605
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
5339
5606
|
} catch {
|
|
5340
5607
|
}
|
|
@@ -5353,14 +5620,14 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
5353
5620
|
"--allowedTools",
|
|
5354
5621
|
allowedTools
|
|
5355
5622
|
];
|
|
5356
|
-
if (
|
|
5623
|
+
if (existsSync4(claudeMdPath)) {
|
|
5357
5624
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
5358
5625
|
}
|
|
5359
5626
|
const childEnv = { ...process.env };
|
|
5360
|
-
const envIntPath =
|
|
5361
|
-
if (
|
|
5627
|
+
const envIntPath = join5(projectDir, ".env.integrations");
|
|
5628
|
+
if (existsSync4(envIntPath)) {
|
|
5362
5629
|
try {
|
|
5363
|
-
for (const line of
|
|
5630
|
+
for (const line of readFileSync4(envIntPath, "utf-8").split("\n")) {
|
|
5364
5631
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
5365
5632
|
const eqIdx = line.indexOf("=");
|
|
5366
5633
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -5514,8 +5781,8 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
5514
5781
|
}
|
|
5515
5782
|
}
|
|
5516
5783
|
function fireClaudeWorkTrigger(codeName, agentId, boardItems) {
|
|
5517
|
-
const
|
|
5518
|
-
const kanbanTask = findTaskByTemplate(
|
|
5784
|
+
const state4 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
5785
|
+
const kanbanTask = findTaskByTemplate(state4, "kanban-work");
|
|
5519
5786
|
if (!kanbanTask) {
|
|
5520
5787
|
log(`[claude-scheduler] Work trigger: no kanban-work task found for '${codeName}'`);
|
|
5521
5788
|
return;
|
|
@@ -5538,13 +5805,23 @@ function fireClaudeWorkTrigger(codeName, agentId, boardItems) {
|
|
|
5538
5805
|
});
|
|
5539
5806
|
}
|
|
5540
5807
|
var persistentSessionAgents = /* @__PURE__ */ new Set();
|
|
5808
|
+
var lastMcpFailedBannerCount = /* @__PURE__ */ new Map();
|
|
5541
5809
|
var persistentSessionStuckTracker = new PersistentSessionStuckTracker();
|
|
5542
5810
|
var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
|
|
5543
5811
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
5544
5812
|
const codeName = agent.code_name;
|
|
5545
5813
|
const projectDir = getProjectDir(codeName);
|
|
5546
|
-
const mcpConfigPath =
|
|
5547
|
-
const claudeMdPath =
|
|
5814
|
+
const mcpConfigPath = join5(projectDir, ".mcp.json");
|
|
5815
|
+
const claudeMdPath = join5(projectDir, "CLAUDE.md");
|
|
5816
|
+
if (restartBreaker.isTripped(codeName)) {
|
|
5817
|
+
const trip = restartBreaker.getTrip(codeName);
|
|
5818
|
+
return {
|
|
5819
|
+
decision: "circuit-tripped",
|
|
5820
|
+
spawnAttempted: false,
|
|
5821
|
+
sessionHealthyAfter: isSessionHealthy(codeName),
|
|
5822
|
+
detail: trip.statusMessage
|
|
5823
|
+
};
|
|
5824
|
+
}
|
|
5548
5825
|
const teamSettingsForTz = refreshData.team?.settings;
|
|
5549
5826
|
const agentTimezone = (() => {
|
|
5550
5827
|
const tz = teamSettingsForTz?.["timezone"];
|
|
@@ -5627,7 +5904,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
5627
5904
|
const recordedAuthTuple = claudeAuthTupleBySession.get(codeName);
|
|
5628
5905
|
if (recordedAuthTuple && recordedAuthTuple !== currentAuthTuple && isSessionHealthy(codeName)) {
|
|
5629
5906
|
log(`[persistent-session] Auth config changed for '${codeName}' (${recordedAuthTuple} \u2192 ${currentAuthTuple}) \u2014 restarting session`);
|
|
5630
|
-
stopPersistentSessionAndForgetMcpBaseline(codeName);
|
|
5907
|
+
stopPersistentSessionAndForgetMcpBaseline(codeName, "auth-tuple-change");
|
|
5631
5908
|
persistentSessionAgents.delete(codeName);
|
|
5632
5909
|
restartTrigger = "auth-tuple";
|
|
5633
5910
|
}
|
|
@@ -5639,7 +5916,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
5639
5916
|
log(
|
|
5640
5917
|
`[persistent-session] Day rollover for '${codeName}' (yesterday=${current.date}) \u2014 agent idle, restarting to mint fresh session`
|
|
5641
5918
|
);
|
|
5642
|
-
stopPersistentSessionAndForgetMcpBaseline(codeName);
|
|
5919
|
+
stopPersistentSessionAndForgetMcpBaseline(codeName, "day-rollover");
|
|
5643
5920
|
persistentSessionAgents.delete(codeName);
|
|
5644
5921
|
restartTrigger = "day-rollover";
|
|
5645
5922
|
} else {
|
|
@@ -5650,6 +5927,15 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
5650
5927
|
}
|
|
5651
5928
|
}
|
|
5652
5929
|
}
|
|
5930
|
+
if (restartBreaker.isTripped(codeName)) {
|
|
5931
|
+
const trip = restartBreaker.getTrip(codeName);
|
|
5932
|
+
return {
|
|
5933
|
+
decision: "circuit-tripped",
|
|
5934
|
+
spawnAttempted: false,
|
|
5935
|
+
sessionHealthyAfter: isSessionHealthy(codeName),
|
|
5936
|
+
detail: trip.statusMessage
|
|
5937
|
+
};
|
|
5938
|
+
}
|
|
5653
5939
|
if (!isSessionHealthy(codeName)) {
|
|
5654
5940
|
if (persistentSessionAgents.has(codeName)) {
|
|
5655
5941
|
const ctx = getLastFailureContext(codeName);
|
|
@@ -5763,9 +6049,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash2("sha256").
|
|
|
5763
6049
|
} else if (!claudeSchedulerStates.has(codeName)) {
|
|
5764
6050
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
5765
6051
|
}
|
|
5766
|
-
const
|
|
5767
|
-
if (
|
|
5768
|
-
const ready = getReadyTasks(
|
|
6052
|
+
const state4 = claudeSchedulerStates.get(codeName);
|
|
6053
|
+
if (state4) {
|
|
6054
|
+
const ready = getReadyTasks(state4, inFlightClaudeTasks);
|
|
5769
6055
|
if (ready.length > 0) {
|
|
5770
6056
|
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(", ")}`);
|
|
5771
6057
|
}
|
|
@@ -6144,11 +6430,11 @@ ${escapeXml(msg.content)}
|
|
|
6144
6430
|
if (fw === "claude-code") {
|
|
6145
6431
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-XHJS2MZV.js");
|
|
6146
6432
|
const projDir = ccProjectDir(agent.codeName);
|
|
6147
|
-
const mcpConfigPath =
|
|
6433
|
+
const mcpConfigPath = join5(projDir, ".mcp.json");
|
|
6148
6434
|
const serverNames = [];
|
|
6149
|
-
if (
|
|
6435
|
+
if (existsSync4(mcpConfigPath)) {
|
|
6150
6436
|
try {
|
|
6151
|
-
const d = JSON.parse(
|
|
6437
|
+
const d = JSON.parse(readFileSync4(mcpConfigPath, "utf-8"));
|
|
6152
6438
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
6153
6439
|
} catch {
|
|
6154
6440
|
}
|
|
@@ -6167,15 +6453,15 @@ ${escapeXml(msg.content)}
|
|
|
6167
6453
|
"--allowedTools",
|
|
6168
6454
|
allowedTools
|
|
6169
6455
|
];
|
|
6170
|
-
const chatClaudeMd =
|
|
6171
|
-
if (
|
|
6456
|
+
const chatClaudeMd = join5(projDir, "CLAUDE.md");
|
|
6457
|
+
if (existsSync4(chatClaudeMd)) {
|
|
6172
6458
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
6173
6459
|
}
|
|
6174
|
-
const envIntPath =
|
|
6460
|
+
const envIntPath = join5(projDir, ".env.integrations");
|
|
6175
6461
|
const childEnv = { ...process.env };
|
|
6176
|
-
if (
|
|
6462
|
+
if (existsSync4(envIntPath)) {
|
|
6177
6463
|
try {
|
|
6178
|
-
for (const line of
|
|
6464
|
+
for (const line of readFileSync4(envIntPath, "utf-8").split("\n")) {
|
|
6179
6465
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
6180
6466
|
const eqIdx = line.indexOf("=");
|
|
6181
6467
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -6518,12 +6804,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
6518
6804
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
6519
6805
|
try {
|
|
6520
6806
|
const candidates = [
|
|
6521
|
-
|
|
6522
|
-
|
|
6807
|
+
join5(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
6808
|
+
join5(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
6523
6809
|
];
|
|
6524
6810
|
for (const candidate of candidates) {
|
|
6525
|
-
if (
|
|
6526
|
-
const content =
|
|
6811
|
+
if (existsSync4(candidate)) {
|
|
6812
|
+
const content = readFileSync4(candidate, "utf-8");
|
|
6527
6813
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
6528
6814
|
builtInSkillCache.set(skillId, files);
|
|
6529
6815
|
return files;
|
|
@@ -7136,7 +7422,7 @@ async function processClaudePairSessions(agents) {
|
|
|
7136
7422
|
killPairSession,
|
|
7137
7423
|
pairTmuxSession,
|
|
7138
7424
|
finalizeClaudePairOnboarding
|
|
7139
|
-
} = await import("../claude-pair-runtime-
|
|
7425
|
+
} = await import("../claude-pair-runtime-XCFWTH6T.js");
|
|
7140
7426
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
7141
7427
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
7142
7428
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -7357,6 +7643,14 @@ function generateArtifacts(agent, refreshData, adapter) {
|
|
|
7357
7643
|
};
|
|
7358
7644
|
}
|
|
7359
7645
|
}
|
|
7646
|
+
const activeTasksInjectEnabled = process.env["AGT_ACTIVE_TASKS_INJECT"] === "1";
|
|
7647
|
+
const activeTasksFromRefresh = activeTasksInjectEnabled ? refreshData.active_tasks ?? [] : void 0;
|
|
7648
|
+
if (activeTasksInjectEnabled) {
|
|
7649
|
+
const tokens = estimateActiveTasksTokens(activeTasksFromRefresh);
|
|
7650
|
+
log(
|
|
7651
|
+
`[provision/active-tasks] agent=${agent.code_name} count=${activeTasksFromRefresh?.length ?? 0} estimated_tokens=${tokens}`
|
|
7652
|
+
);
|
|
7653
|
+
}
|
|
7360
7654
|
const provisionInput = {
|
|
7361
7655
|
agent: refreshData.agent,
|
|
7362
7656
|
charterFrontmatter,
|
|
@@ -7367,6 +7661,7 @@ function generateArtifacts(agent, refreshData, adapter) {
|
|
|
7367
7661
|
deploymentTarget: "local_docker",
|
|
7368
7662
|
gatewayPort: 9e3,
|
|
7369
7663
|
team: refreshData.team ?? void 0,
|
|
7664
|
+
activeTasks: activeTasksFromRefresh,
|
|
7370
7665
|
// ENG-5009: org name for the identity-preamble. Only present when
|
|
7371
7666
|
// /host/refresh ships the field — falls through cleanly for older
|
|
7372
7667
|
// API payloads that don't carry it.
|
|
@@ -7422,8 +7717,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
7422
7717
|
};
|
|
7423
7718
|
}
|
|
7424
7719
|
async function syncMemories(agent, configDir, log2) {
|
|
7425
|
-
const projectDir =
|
|
7426
|
-
const memoryDir =
|
|
7720
|
+
const projectDir = join5(configDir, agent.code_name, "project");
|
|
7721
|
+
const memoryDir = join5(projectDir, "memory");
|
|
7427
7722
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
7428
7723
|
if (isFreshSync) {
|
|
7429
7724
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -7434,14 +7729,14 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7434
7729
|
}
|
|
7435
7730
|
pendingFreshMemorySync.delete(agent.agent_id);
|
|
7436
7731
|
}
|
|
7437
|
-
if (
|
|
7732
|
+
if (existsSync4(memoryDir)) {
|
|
7438
7733
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
7439
7734
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
7440
7735
|
const changedMemories = [];
|
|
7441
7736
|
for (const file of readdirSync2(memoryDir)) {
|
|
7442
7737
|
if (!file.endsWith(".md")) continue;
|
|
7443
7738
|
try {
|
|
7444
|
-
const raw =
|
|
7739
|
+
const raw = readFileSync4(join5(memoryDir, file), "utf-8");
|
|
7445
7740
|
const fileHash = createHash2("sha256").update(raw).digest("hex").slice(0, 16);
|
|
7446
7741
|
currentHashes.set(file, fileHash);
|
|
7447
7742
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -7466,7 +7761,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7466
7761
|
} catch (err) {
|
|
7467
7762
|
for (const mem of changedMemories) {
|
|
7468
7763
|
for (const [file] of currentHashes) {
|
|
7469
|
-
const parsed = parseMemoryFile(
|
|
7764
|
+
const parsed = parseMemoryFile(readFileSync4(join5(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
7470
7765
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
7471
7766
|
}
|
|
7472
7767
|
}
|
|
@@ -7479,7 +7774,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
7479
7774
|
}
|
|
7480
7775
|
}
|
|
7481
7776
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
7482
|
-
const localFiles =
|
|
7777
|
+
const localFiles = existsSync4(memoryDir) ? readdirSync2(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
7483
7778
|
const localListHash = createHash2("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
7484
7779
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
7485
7780
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
@@ -7501,7 +7796,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
|
7501
7796
|
const mem = dbMemories.memories[i];
|
|
7502
7797
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
7503
7798
|
const slug = rawSlug || `memory-${i}`;
|
|
7504
|
-
const filePath =
|
|
7799
|
+
const filePath = join5(memoryDir, `${slug}.md`);
|
|
7505
7800
|
const desired = `---
|
|
7506
7801
|
name: ${JSON.stringify(mem.name)}
|
|
7507
7802
|
type: ${mem.type}
|
|
@@ -7510,10 +7805,10 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
|
|
|
7510
7805
|
|
|
7511
7806
|
${mem.content}
|
|
7512
7807
|
`;
|
|
7513
|
-
if (
|
|
7808
|
+
if (existsSync4(filePath)) {
|
|
7514
7809
|
let existing = "";
|
|
7515
7810
|
try {
|
|
7516
|
-
existing =
|
|
7811
|
+
existing = readFileSync4(filePath, "utf-8");
|
|
7517
7812
|
} catch {
|
|
7518
7813
|
}
|
|
7519
7814
|
if (existing === desired) continue;
|
|
@@ -7537,7 +7832,7 @@ ${mem.content}
|
|
|
7537
7832
|
}
|
|
7538
7833
|
}
|
|
7539
7834
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
7540
|
-
if (
|
|
7835
|
+
if (existsSync4(agentDir)) {
|
|
7541
7836
|
try {
|
|
7542
7837
|
rmSync2(agentDir, { recursive: true, force: true });
|
|
7543
7838
|
log(`Removed provision directory for '${codeName}'`);
|
|
@@ -7708,19 +8003,24 @@ function startManager(opts) {
|
|
|
7708
8003
|
config = opts;
|
|
7709
8004
|
try {
|
|
7710
8005
|
const stateFile = getStateFile();
|
|
7711
|
-
if (
|
|
7712
|
-
const raw =
|
|
8006
|
+
if (existsSync4(stateFile)) {
|
|
8007
|
+
const raw = readFileSync4(stateFile, "utf-8");
|
|
7713
8008
|
const parsed = JSON.parse(raw);
|
|
7714
8009
|
if (Array.isArray(parsed.agents)) {
|
|
7715
|
-
|
|
7716
|
-
log(`[startup] rehydrated ${
|
|
8010
|
+
state3.agents = parsed.agents;
|
|
8011
|
+
log(`[startup] rehydrated ${state3.agents.length} agent state(s) from ${stateFile}`);
|
|
8012
|
+
}
|
|
8013
|
+
if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
|
|
8014
|
+
restartBreaker.hydrate(parsed.circuitBreakerTrips);
|
|
8015
|
+
const n = Object.keys(parsed.circuitBreakerTrips).length;
|
|
8016
|
+
if (n > 0) log(`[startup] rehydrated ${n} circuit-breaker trip(s) \u2014 agents will remain paused until manually resumed`);
|
|
7717
8017
|
}
|
|
7718
8018
|
}
|
|
7719
8019
|
} catch (err) {
|
|
7720
8020
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
7721
8021
|
}
|
|
7722
8022
|
log(
|
|
7723
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
8023
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join5(homedir4(), ".augmented", "manager.log")}`
|
|
7724
8024
|
);
|
|
7725
8025
|
warnOnConflictingKanbanModes();
|
|
7726
8026
|
deployMcpAssets();
|
|
@@ -7783,14 +8083,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
7783
8083
|
}
|
|
7784
8084
|
}
|
|
7785
8085
|
function deployMcpAssets() {
|
|
7786
|
-
const targetDir =
|
|
8086
|
+
const targetDir = join5(homedir4(), ".augmented", "_mcp");
|
|
7787
8087
|
mkdirSync2(targetDir, { recursive: true });
|
|
7788
8088
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
7789
8089
|
let mcpSourceDir = "";
|
|
7790
8090
|
let dir = moduleDir;
|
|
7791
8091
|
for (let i = 0; i < 6; i++) {
|
|
7792
|
-
const candidate =
|
|
7793
|
-
if (
|
|
8092
|
+
const candidate = join5(dir, "dist", "mcp");
|
|
8093
|
+
if (existsSync4(join5(candidate, "index.js"))) {
|
|
7794
8094
|
mcpSourceDir = candidate;
|
|
7795
8095
|
break;
|
|
7796
8096
|
}
|
|
@@ -7805,8 +8105,8 @@ function deployMcpAssets() {
|
|
|
7805
8105
|
const changedBasenames = [];
|
|
7806
8106
|
const fileHash = (p) => {
|
|
7807
8107
|
try {
|
|
7808
|
-
if (!
|
|
7809
|
-
return createHash2("sha256").update(
|
|
8108
|
+
if (!existsSync4(p)) return null;
|
|
8109
|
+
return createHash2("sha256").update(readFileSync4(p)).digest("hex");
|
|
7810
8110
|
} catch {
|
|
7811
8111
|
return null;
|
|
7812
8112
|
}
|
|
@@ -7817,9 +8117,9 @@ function deployMcpAssets() {
|
|
|
7817
8117
|
"telegram-channel.js"
|
|
7818
8118
|
]);
|
|
7819
8119
|
for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
|
|
7820
|
-
const src =
|
|
7821
|
-
const dst =
|
|
7822
|
-
if (!
|
|
8120
|
+
const src = join5(mcpSourceDir, file);
|
|
8121
|
+
const dst = join5(targetDir, file);
|
|
8122
|
+
if (!existsSync4(src)) continue;
|
|
7823
8123
|
const before = fileHash(dst);
|
|
7824
8124
|
try {
|
|
7825
8125
|
copyFileSync(src, dst);
|
|
@@ -7836,16 +8136,16 @@ function deployMcpAssets() {
|
|
|
7836
8136
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
7837
8137
|
restartRunningChannelMcps(changedBasenames);
|
|
7838
8138
|
}
|
|
7839
|
-
const localMcpPath =
|
|
8139
|
+
const localMcpPath = join5(targetDir, "index.js");
|
|
7840
8140
|
try {
|
|
7841
|
-
const agentsDir =
|
|
7842
|
-
if (
|
|
8141
|
+
const agentsDir = join5(homedir4(), ".augmented", "agents");
|
|
8142
|
+
if (existsSync4(agentsDir)) {
|
|
7843
8143
|
for (const entry of readdirSync2(agentsDir, { withFileTypes: true })) {
|
|
7844
8144
|
if (!entry.isDirectory()) continue;
|
|
7845
8145
|
for (const subdir of ["provision", "project"]) {
|
|
7846
|
-
const mcpJsonPath =
|
|
8146
|
+
const mcpJsonPath = join5(agentsDir, entry.name, subdir, ".mcp.json");
|
|
7847
8147
|
try {
|
|
7848
|
-
const raw =
|
|
8148
|
+
const raw = readFileSync4(mcpJsonPath, "utf-8");
|
|
7849
8149
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
7850
8150
|
const mcpConfig = JSON.parse(raw);
|
|
7851
8151
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|