@integrity-labs/agt-cli 0.27.51 → 0.27.53
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-UOHGOS3O.js → chunk-2NOH5XB5.js} +13 -1
- package/dist/chunk-2NOH5XB5.js.map +1 -0
- package/dist/{chunk-5S322IC7.js → chunk-4ZKBMJ3Y.js} +2 -2
- package/dist/{chunk-Y4KVZGPE.js → chunk-GPBGRRCV.js} +50 -34
- package/dist/chunk-GPBGRRCV.js.map +1 -0
- package/dist/{claude-pair-runtime-PZBLMYVH.js → claude-pair-runtime-OOVDC4YK.js} +2 -2
- package/dist/lib/manager-worker.js +442 -198
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/{persistent-session-JRF3JXTC.js → persistent-session-6TWTADQN.js} +3 -3
- package/dist/{responsiveness-probe-JZLGWZFN.js → responsiveness-probe-KBUKS7PN.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-UOHGOS3O.js.map +0 -1
- package/dist/chunk-Y4KVZGPE.js.map +0 -1
- /package/dist/{chunk-5S322IC7.js.map → chunk-4ZKBMJ3Y.js.map} +0 -0
- /package/dist/{claude-pair-runtime-PZBLMYVH.js.map → claude-pair-runtime-OOVDC4YK.js.map} +0 -0
- /package/dist/{persistent-session-JRF3JXTC.js.map → persistent-session-6TWTADQN.js.map} +0 -0
- /package/dist/{responsiveness-probe-JZLGWZFN.js.map → responsiveness-probe-KBUKS7PN.js.map} +0 -0
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
provisionOrientHook,
|
|
16
16
|
provisionStopHook,
|
|
17
17
|
requireHost
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-GPBGRRCV.js";
|
|
19
19
|
import {
|
|
20
20
|
getProjectDir as getProjectDir2,
|
|
21
21
|
getReadyTasks,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
stopAllSessionsAndWait,
|
|
47
47
|
stopPersistentSession,
|
|
48
48
|
takeZombieDetection
|
|
49
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-4ZKBMJ3Y.js";
|
|
50
50
|
import {
|
|
51
51
|
KANBAN_CHECK_COMMAND,
|
|
52
52
|
appendDmFooter,
|
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
resolveConnectivityProbe,
|
|
70
70
|
resolveDmTarget,
|
|
71
71
|
wrapScheduledTaskPrompt
|
|
72
|
-
} from "../chunk-
|
|
72
|
+
} from "../chunk-2NOH5XB5.js";
|
|
73
73
|
import {
|
|
74
74
|
parsePsRows,
|
|
75
75
|
reapOrphanChannelMcps
|
|
@@ -77,10 +77,10 @@ import {
|
|
|
77
77
|
|
|
78
78
|
// src/lib/manager-worker.ts
|
|
79
79
|
import { createHash as createHash3 } from "crypto";
|
|
80
|
-
import { readFileSync as
|
|
80
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, appendFileSync, mkdirSync as mkdirSync4, chmodSync, existsSync as existsSync5, rmSync as rmSync2, readdirSync as readdirSync3, statSync as statSync2, unlinkSync, copyFileSync } from "fs";
|
|
81
81
|
import https from "https";
|
|
82
82
|
import { execFileSync as syncExecFile } from "child_process";
|
|
83
|
-
import { join as
|
|
83
|
+
import { join as join7, dirname as dirname3 } from "path";
|
|
84
84
|
import { homedir as homedir4 } from "os";
|
|
85
85
|
import { fileURLToPath } from "url";
|
|
86
86
|
|
|
@@ -412,7 +412,13 @@ function reapMissingMcpSessions(args) {
|
|
|
412
412
|
graceMs = DEFAULT_COLD_START_GRACE_MS,
|
|
413
413
|
rotationGraceMs = DEFAULT_ROTATION_GRACE_MS,
|
|
414
414
|
onGiveUp,
|
|
415
|
-
onRestart
|
|
415
|
+
onRestart,
|
|
416
|
+
// ENG-5932: defaults make quarantine a strict no-op (classify everything
|
|
417
|
+
// essential, mode off) so legacy callers/tests are unaffected.
|
|
418
|
+
classifyKey = () => "essential",
|
|
419
|
+
quarantineDwellMs = 0,
|
|
420
|
+
quarantineMode = "off",
|
|
421
|
+
onQuarantine
|
|
416
422
|
} = args;
|
|
417
423
|
const now = args.now ?? Date.now;
|
|
418
424
|
const runPs = args.runPs ?? (() => execFileSync2("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", timeout: 5e3 }));
|
|
@@ -466,6 +472,8 @@ function reapMissingMcpSessions(args) {
|
|
|
466
472
|
state5.lastSeenLiveAt = now();
|
|
467
473
|
state5.lastAttemptedSessionStartedAt = null;
|
|
468
474
|
state5.gaveUpLogged = false;
|
|
475
|
+
state5.firstMissingAt = null;
|
|
476
|
+
state5.quarantineLogged = false;
|
|
469
477
|
}
|
|
470
478
|
}
|
|
471
479
|
if (missing.length === 0) {
|
|
@@ -476,16 +484,22 @@ function reapMissingMcpSessions(args) {
|
|
|
476
484
|
rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0
|
|
477
485
|
};
|
|
478
486
|
}
|
|
487
|
+
const nowMs = now();
|
|
479
488
|
const givenUp = [];
|
|
480
489
|
const active = [];
|
|
490
|
+
const quarantined = [];
|
|
491
|
+
const wouldQuarantine = [];
|
|
481
492
|
for (const key of missing) {
|
|
482
493
|
const sk = stateKey(codeName, key);
|
|
483
494
|
const state5 = presenceReaperState.get(sk) ?? {
|
|
484
495
|
attempts: 0,
|
|
485
496
|
lastSeenLiveAt: null,
|
|
486
497
|
lastAttemptedSessionStartedAt: null,
|
|
487
|
-
gaveUpLogged: false
|
|
498
|
+
gaveUpLogged: false,
|
|
499
|
+
firstMissingAt: null,
|
|
500
|
+
quarantineLogged: false
|
|
488
501
|
};
|
|
502
|
+
if (state5.firstMissingAt === null) state5.firstMissingAt = nowMs;
|
|
489
503
|
if (state5.lastAttemptedSessionStartedAt !== sessionStartedAt) {
|
|
490
504
|
state5.attempts += 1;
|
|
491
505
|
state5.lastAttemptedSessionStartedAt = sessionStartedAt;
|
|
@@ -508,6 +522,32 @@ function reapMissingMcpSessions(args) {
|
|
|
508
522
|
}
|
|
509
523
|
}
|
|
510
524
|
}
|
|
525
|
+
if (quarantineMode !== "off" && !state5.quarantineLogged && classifyKey(key) === "optional") {
|
|
526
|
+
const dwellElapsed = quarantineDwellMs <= 0 || state5.firstMissingAt !== null && nowMs - state5.firstMissingAt >= quarantineDwellMs;
|
|
527
|
+
if (dwellElapsed) {
|
|
528
|
+
state5.quarantineLogged = true;
|
|
529
|
+
if (quarantineMode === "enforce") {
|
|
530
|
+
quarantined.push(key);
|
|
531
|
+
log2(
|
|
532
|
+
`[mcp-presence-reaper] QUARANTINE '${codeName}:${key}' \u2014 optional channel dead past restart budget + ${quarantineDwellMs}ms dwell; dropping from provisioned set so the dead channel stops restarting the whole session (ENG-5932)`
|
|
533
|
+
);
|
|
534
|
+
if (onQuarantine) {
|
|
535
|
+
try {
|
|
536
|
+
onQuarantine(codeName, key);
|
|
537
|
+
} catch (err) {
|
|
538
|
+
log2(
|
|
539
|
+
`[mcp-presence-reaper] onQuarantine callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
wouldQuarantine.push(key);
|
|
545
|
+
log2(
|
|
546
|
+
`[mcp-presence-reaper] SHADOW would-quarantine '${codeName}:${key}' \u2014 optional channel dead past restart budget + ${quarantineDwellMs}ms dwell; no action taken (ENG-5932 shadow mode)`
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
511
551
|
} else {
|
|
512
552
|
active.push(key);
|
|
513
553
|
}
|
|
@@ -518,7 +558,9 @@ function reapMissingMcpSessions(args) {
|
|
|
518
558
|
restarted: false,
|
|
519
559
|
reason: "all-keys-over-cap",
|
|
520
560
|
givenUp,
|
|
521
|
-
rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0
|
|
561
|
+
rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
|
|
562
|
+
quarantined: quarantined.length > 0 ? quarantined : void 0,
|
|
563
|
+
wouldQuarantine: wouldQuarantine.length > 0 ? wouldQuarantine : void 0
|
|
522
564
|
};
|
|
523
565
|
}
|
|
524
566
|
const givenUpSuffix = givenUp.length > 0 ? ` (skipping over-cap: [${givenUp.join(", ")}])` : "";
|
|
@@ -546,7 +588,9 @@ function reapMissingMcpSessions(args) {
|
|
|
546
588
|
missing,
|
|
547
589
|
restarted: true,
|
|
548
590
|
givenUp: givenUp.length > 0 ? givenUp : void 0,
|
|
549
|
-
rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0
|
|
591
|
+
rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
|
|
592
|
+
quarantined: quarantined.length > 0 ? quarantined : void 0,
|
|
593
|
+
wouldQuarantine: wouldQuarantine.length > 0 ? wouldQuarantine : void 0
|
|
550
594
|
};
|
|
551
595
|
}
|
|
552
596
|
|
|
@@ -609,6 +653,151 @@ function decideSenderPolicyRestart(input) {
|
|
|
609
653
|
return { restart: true, firstPoll: false, changed: true };
|
|
610
654
|
}
|
|
611
655
|
|
|
656
|
+
// src/lib/channel-quarantine.ts
|
|
657
|
+
import { readFileSync } from "fs";
|
|
658
|
+
import { join } from "path";
|
|
659
|
+
|
|
660
|
+
// src/lib/atomic-write.ts
|
|
661
|
+
import { closeSync, fsyncSync, openSync, writeSync, renameSync, mkdirSync } from "fs";
|
|
662
|
+
import { dirname } from "path";
|
|
663
|
+
function atomicWriteFileSync(path, data) {
|
|
664
|
+
const dirPath = dirname(path);
|
|
665
|
+
const tmpPath = `${path}.tmp.${process.pid}.${Math.random().toString(36).slice(2, 8)}`;
|
|
666
|
+
try {
|
|
667
|
+
mkdirSync(dirPath, { recursive: true });
|
|
668
|
+
} catch {
|
|
669
|
+
}
|
|
670
|
+
const fd = openSync(tmpPath, "w", 420);
|
|
671
|
+
try {
|
|
672
|
+
writeSync(fd, data);
|
|
673
|
+
try {
|
|
674
|
+
fsyncSync(fd);
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
} finally {
|
|
678
|
+
closeSync(fd);
|
|
679
|
+
}
|
|
680
|
+
renameSync(tmpPath, path);
|
|
681
|
+
try {
|
|
682
|
+
const dirFd = openSync(dirPath, "r");
|
|
683
|
+
try {
|
|
684
|
+
fsyncSync(dirFd);
|
|
685
|
+
} finally {
|
|
686
|
+
closeSync(dirFd);
|
|
687
|
+
}
|
|
688
|
+
} catch {
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/lib/channel-quarantine.ts
|
|
693
|
+
var ESSENTIAL_CHANNEL_KEYS = /* @__PURE__ */ new Set(["direct-chat", "augmented"]);
|
|
694
|
+
var OPTIONAL_CHANNEL_KEYS = /* @__PURE__ */ new Set([
|
|
695
|
+
"telegram",
|
|
696
|
+
"slack",
|
|
697
|
+
"msteams",
|
|
698
|
+
"discord",
|
|
699
|
+
"whatsapp",
|
|
700
|
+
"signal",
|
|
701
|
+
"imessage",
|
|
702
|
+
"matrix",
|
|
703
|
+
"irc",
|
|
704
|
+
"nostr"
|
|
705
|
+
]);
|
|
706
|
+
function readDeclaredCriticality(entry) {
|
|
707
|
+
if (!entry || typeof entry !== "object") return void 0;
|
|
708
|
+
const meta = entry._augmented;
|
|
709
|
+
if (!meta || typeof meta !== "object") return void 0;
|
|
710
|
+
const value = meta.criticality;
|
|
711
|
+
return value === "essential" || value === "optional" ? value : void 0;
|
|
712
|
+
}
|
|
713
|
+
function classifyChannelCriticality(serverKey, entry) {
|
|
714
|
+
if (ESSENTIAL_CHANNEL_KEYS.has(serverKey)) return "essential";
|
|
715
|
+
const declared = readDeclaredCriticality(entry);
|
|
716
|
+
if (declared) return declared;
|
|
717
|
+
if (OPTIONAL_CHANNEL_KEYS.has(serverKey)) return "optional";
|
|
718
|
+
return "essential";
|
|
719
|
+
}
|
|
720
|
+
function defaultQuarantinePath(configDir) {
|
|
721
|
+
return join(configDir, "channel-quarantine.json");
|
|
722
|
+
}
|
|
723
|
+
var ChannelQuarantineStore = class {
|
|
724
|
+
path;
|
|
725
|
+
cache = null;
|
|
726
|
+
constructor(path) {
|
|
727
|
+
this.path = path;
|
|
728
|
+
}
|
|
729
|
+
load() {
|
|
730
|
+
if (this.cache) return this.cache;
|
|
731
|
+
try {
|
|
732
|
+
const raw = readFileSync(this.path, "utf-8");
|
|
733
|
+
const parsed = JSON.parse(raw);
|
|
734
|
+
this.cache = isQuarantineFile(parsed) ? parsed : {};
|
|
735
|
+
} catch {
|
|
736
|
+
this.cache = {};
|
|
737
|
+
}
|
|
738
|
+
return this.cache;
|
|
739
|
+
}
|
|
740
|
+
persist() {
|
|
741
|
+
atomicWriteFileSync(this.path, JSON.stringify(this.cache ?? {}, null, 2));
|
|
742
|
+
}
|
|
743
|
+
/** Is `serverKey` currently quarantined for `codeName`? */
|
|
744
|
+
isQuarantined(codeName, serverKey) {
|
|
745
|
+
return this.load()[codeName]?.[serverKey] !== void 0;
|
|
746
|
+
}
|
|
747
|
+
/** The set of quarantined server keys for `codeName` (empty Set if none). */
|
|
748
|
+
getQuarantinedKeys(codeName) {
|
|
749
|
+
const forAgent = this.load()[codeName];
|
|
750
|
+
return new Set(forAgent ? Object.keys(forAgent) : []);
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Mark `serverKey` quarantined for `codeName`. Idempotent — re-quarantining
|
|
754
|
+
* an already-quarantined key preserves the original `quarantinedAt` (so the
|
|
755
|
+
* "how long has this been quarantined" escalation clock in Slice 3 isn't
|
|
756
|
+
* reset by provisioning churn). Returns true iff this call newly quarantined
|
|
757
|
+
* the key (false if it was already quarantined).
|
|
758
|
+
*/
|
|
759
|
+
quarantine(codeName, serverKey, reason, now) {
|
|
760
|
+
const file = this.load();
|
|
761
|
+
const forAgent = file[codeName] ??= {};
|
|
762
|
+
if (forAgent[serverKey]) return false;
|
|
763
|
+
forAgent[serverKey] = { quarantinedAt: now, reason };
|
|
764
|
+
this.persist();
|
|
765
|
+
return true;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Clear the quarantine for `serverKey` (explicit re-enable). Returns true iff
|
|
769
|
+
* a marker was actually removed. NOTE: re-enable is an EXPLICIT verb (Slice 2
|
|
770
|
+
* exposes it via the API/console) — provisioning's idempotent `.mcp.json`
|
|
771
|
+
* rewrites must NEVER call this, or a quarantined channel would re-arm into a
|
|
772
|
+
* fresh flap laundered through the config loop.
|
|
773
|
+
*/
|
|
774
|
+
clearQuarantine(codeName, serverKey) {
|
|
775
|
+
const file = this.load();
|
|
776
|
+
const forAgent = file[codeName];
|
|
777
|
+
if (!forAgent || !forAgent[serverKey]) return false;
|
|
778
|
+
delete forAgent[serverKey];
|
|
779
|
+
if (Object.keys(forAgent).length === 0) delete file[codeName];
|
|
780
|
+
this.persist();
|
|
781
|
+
return true;
|
|
782
|
+
}
|
|
783
|
+
/** Test seam: drop the in-memory cache so the next read re-hits disk. */
|
|
784
|
+
__invalidateCacheForTests() {
|
|
785
|
+
this.cache = null;
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
function isQuarantineFile(value) {
|
|
789
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
790
|
+
for (const forAgent of Object.values(value)) {
|
|
791
|
+
if (!forAgent || typeof forAgent !== "object" || Array.isArray(forAgent)) return false;
|
|
792
|
+
for (const entry of Object.values(forAgent)) {
|
|
793
|
+
if (!entry || typeof entry !== "object") return false;
|
|
794
|
+
const e = entry;
|
|
795
|
+
if (typeof e.quarantinedAt !== "number" || typeof e.reason !== "string") return false;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return true;
|
|
799
|
+
}
|
|
800
|
+
|
|
612
801
|
// src/lib/restart-breaker.ts
|
|
613
802
|
function reaperRestartBreakerReason(activeKeys) {
|
|
614
803
|
return activeKeys.length >= 2 ? "mcp-presence-reaper" : void 0;
|
|
@@ -942,8 +1131,8 @@ function runCliProbe(binary, args, opts = {}) {
|
|
|
942
1131
|
}
|
|
943
1132
|
|
|
944
1133
|
// src/lib/self-update-coalesce.ts
|
|
945
|
-
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
946
|
-
import { dirname } from "path";
|
|
1134
|
+
import { readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
1135
|
+
import { dirname as dirname2 } from "path";
|
|
947
1136
|
var DEFAULT_SELF_UPDATE_COALESCE_MS = 30 * 60 * 1e3;
|
|
948
1137
|
function resolveCoalesceWindowMs(env = process.env) {
|
|
949
1138
|
const raw = env.AGT_SELF_UPDATE_COALESCE_MS;
|
|
@@ -952,7 +1141,7 @@ function resolveCoalesceWindowMs(env = process.env) {
|
|
|
952
1141
|
if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_SELF_UPDATE_COALESCE_MS;
|
|
953
1142
|
return Math.trunc(parsed);
|
|
954
1143
|
}
|
|
955
|
-
function readLastSelfUpdateAppliedMs(markerPath, now = Date.now(), read = (p) =>
|
|
1144
|
+
function readLastSelfUpdateAppliedMs(markerPath, now = Date.now(), read = (p) => readFileSync2(p, "utf-8")) {
|
|
956
1145
|
let raw;
|
|
957
1146
|
try {
|
|
958
1147
|
raw = read(markerPath);
|
|
@@ -965,7 +1154,7 @@ function readLastSelfUpdateAppliedMs(markerPath, now = Date.now(), read = (p) =>
|
|
|
965
1154
|
return v;
|
|
966
1155
|
}
|
|
967
1156
|
function stampLastSelfUpdateApplied(markerPath, now = Date.now(), write = (p, v) => {
|
|
968
|
-
|
|
1157
|
+
mkdirSync2(dirname2(p), { recursive: true });
|
|
969
1158
|
writeFileSync(p, v);
|
|
970
1159
|
}) {
|
|
971
1160
|
try {
|
|
@@ -988,44 +1177,12 @@ function formatCoalesceDeferLogLine(opts) {
|
|
|
988
1177
|
return `[self-update] coalescing \u2014 ${opts.installed} \u2192 ${opts.latest} (${opts.channelLabel}) available, deferring for ${remainingSec}s (last restart inside ${windowMin}min window). See ENG-5862. Override with AGT_SELF_UPDATE_COALESCE_MS=0 during incident response.`;
|
|
989
1178
|
}
|
|
990
1179
|
|
|
991
|
-
// src/lib/atomic-write.ts
|
|
992
|
-
import { closeSync, fsyncSync, openSync, writeSync, renameSync, mkdirSync as mkdirSync2 } from "fs";
|
|
993
|
-
import { dirname as dirname2 } from "path";
|
|
994
|
-
function atomicWriteFileSync(path, data) {
|
|
995
|
-
const dirPath = dirname2(path);
|
|
996
|
-
const tmpPath = `${path}.tmp.${process.pid}.${Math.random().toString(36).slice(2, 8)}`;
|
|
997
|
-
try {
|
|
998
|
-
mkdirSync2(dirPath, { recursive: true });
|
|
999
|
-
} catch {
|
|
1000
|
-
}
|
|
1001
|
-
const fd = openSync(tmpPath, "w", 420);
|
|
1002
|
-
try {
|
|
1003
|
-
writeSync(fd, data);
|
|
1004
|
-
try {
|
|
1005
|
-
fsyncSync(fd);
|
|
1006
|
-
} catch {
|
|
1007
|
-
}
|
|
1008
|
-
} finally {
|
|
1009
|
-
closeSync(fd);
|
|
1010
|
-
}
|
|
1011
|
-
renameSync(tmpPath, path);
|
|
1012
|
-
try {
|
|
1013
|
-
const dirFd = openSync(dirPath, "r");
|
|
1014
|
-
try {
|
|
1015
|
-
fsyncSync(dirFd);
|
|
1016
|
-
} finally {
|
|
1017
|
-
closeSync(dirFd);
|
|
1018
|
-
}
|
|
1019
|
-
} catch {
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
1180
|
// src/lib/claude-pid-tracker.ts
|
|
1024
|
-
import { existsSync, readFileSync as
|
|
1181
|
+
import { existsSync, readFileSync as readFileSync3 } from "fs";
|
|
1025
1182
|
function readPidFile(path) {
|
|
1026
1183
|
if (!existsSync(path)) return { version: 1, spawns: [] };
|
|
1027
1184
|
try {
|
|
1028
|
-
const raw = JSON.parse(
|
|
1185
|
+
const raw = JSON.parse(readFileSync3(path, "utf-8"));
|
|
1029
1186
|
if (raw.version !== 1 || !Array.isArray(raw.spawns)) return { version: 1, spawns: [] };
|
|
1030
1187
|
const spawns = raw.spawns.filter(
|
|
1031
1188
|
(s) => !!s && typeof s.pid === "number" && Number.isFinite(s.pid) && s.pid > 0
|
|
@@ -1133,8 +1290,8 @@ async function maybeReportUsageBanner(args) {
|
|
|
1133
1290
|
}
|
|
1134
1291
|
|
|
1135
1292
|
// src/lib/token-usage-monitor.ts
|
|
1136
|
-
import { readdirSync, readFileSync as
|
|
1137
|
-
import { join } from "path";
|
|
1293
|
+
import { readdirSync, readFileSync as readFileSync4, statSync } from "fs";
|
|
1294
|
+
import { join as join2 } from "path";
|
|
1138
1295
|
var MIN_CHECK_INTERVAL_MS2 = 6e4;
|
|
1139
1296
|
var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
1140
1297
|
var MAX_ENTRIES_PER_POST = 200;
|
|
@@ -1163,7 +1320,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1163
1320
|
if (!name.endsWith(".jsonl")) continue;
|
|
1164
1321
|
const sessionId = name.slice(0, -".jsonl".length);
|
|
1165
1322
|
if (!sessionId) continue;
|
|
1166
|
-
const path =
|
|
1323
|
+
const path = join2(dir, name);
|
|
1167
1324
|
let st;
|
|
1168
1325
|
try {
|
|
1169
1326
|
st = statSync(path);
|
|
@@ -1179,7 +1336,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1179
1336
|
}
|
|
1180
1337
|
let content;
|
|
1181
1338
|
try {
|
|
1182
|
-
content =
|
|
1339
|
+
content = readFileSync4(path, "utf-8");
|
|
1183
1340
|
} catch (err) {
|
|
1184
1341
|
log2(`[token-usage] read failed for '${codeName}/${name}': ${err.message}`);
|
|
1185
1342
|
continue;
|
|
@@ -1260,11 +1417,11 @@ async function maybeReportTokenUsage(args) {
|
|
|
1260
1417
|
}
|
|
1261
1418
|
|
|
1262
1419
|
// src/lib/activity-cache-monitor.ts
|
|
1263
|
-
import { existsSync as existsSync2, readFileSync as
|
|
1420
|
+
import { existsSync as existsSync2, readFileSync as readFileSync5 } from "fs";
|
|
1264
1421
|
import { homedir } from "os";
|
|
1265
|
-
import { join as
|
|
1422
|
+
import { join as join3 } from "path";
|
|
1266
1423
|
var MIN_CHECK_INTERVAL_MS3 = 6e4;
|
|
1267
|
-
var STATS_CACHE_PATH =
|
|
1424
|
+
var STATS_CACHE_PATH = join3(homedir(), ".claude", "stats-cache.json");
|
|
1268
1425
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
1269
1426
|
var state3 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
1270
1427
|
function selectNewDailyRows(raw, lastObservedDate) {
|
|
@@ -1312,7 +1469,7 @@ async function maybeReportActivityCache(args) {
|
|
|
1312
1469
|
}
|
|
1313
1470
|
let raw;
|
|
1314
1471
|
try {
|
|
1315
|
-
raw =
|
|
1472
|
+
raw = readFileSync5(STATS_CACHE_PATH, "utf-8");
|
|
1316
1473
|
} catch (err) {
|
|
1317
1474
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
1318
1475
|
return;
|
|
@@ -1762,7 +1919,7 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
1762
1919
|
// src/lib/claude-auth-detect.ts
|
|
1763
1920
|
import { readFile, readdir } from "fs/promises";
|
|
1764
1921
|
import { homedir as homedir2, platform } from "os";
|
|
1765
|
-
import { join as
|
|
1922
|
+
import { join as join4 } from "path";
|
|
1766
1923
|
import { execFile as execFile2 } from "child_process";
|
|
1767
1924
|
import { promisify } from "util";
|
|
1768
1925
|
var execFileAsync = promisify(execFile2);
|
|
@@ -1777,8 +1934,8 @@ async function detectClaudeAuth() {
|
|
|
1777
1934
|
}
|
|
1778
1935
|
async function findClaudeCredentialsPaths() {
|
|
1779
1936
|
const candidates = [
|
|
1780
|
-
|
|
1781
|
-
|
|
1937
|
+
join4(homedir2(), ".claude", ".credentials.json"),
|
|
1938
|
+
join4(homedir2(), ".claude", "credentials.json")
|
|
1782
1939
|
];
|
|
1783
1940
|
const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
|
|
1784
1941
|
if (isLinuxRoot) {
|
|
@@ -1786,8 +1943,8 @@ async function findClaudeCredentialsPaths() {
|
|
|
1786
1943
|
const entries = await readdir("/home", { withFileTypes: true });
|
|
1787
1944
|
for (const entry of entries) {
|
|
1788
1945
|
if (!entry.isDirectory()) continue;
|
|
1789
|
-
candidates.push(
|
|
1790
|
-
candidates.push(
|
|
1946
|
+
candidates.push(join4("/home", entry.name, ".claude", ".credentials.json"));
|
|
1947
|
+
candidates.push(join4("/home", entry.name, ".claude", "credentials.json"));
|
|
1791
1948
|
}
|
|
1792
1949
|
} catch {
|
|
1793
1950
|
}
|
|
@@ -1879,18 +2036,18 @@ function normalize(value) {
|
|
|
1879
2036
|
}
|
|
1880
2037
|
|
|
1881
2038
|
// src/lib/channel-hash-cache.ts
|
|
1882
|
-
import { existsSync as existsSync3, readFileSync as
|
|
1883
|
-
import { join as
|
|
2039
|
+
import { existsSync as existsSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "fs";
|
|
2040
|
+
import { join as join5 } from "path";
|
|
1884
2041
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
1885
2042
|
function getChannelHashCacheFile(configDir) {
|
|
1886
|
-
return
|
|
2043
|
+
return join5(configDir, CACHE_FILENAME);
|
|
1887
2044
|
}
|
|
1888
2045
|
function loadChannelHashCache(target, configDir) {
|
|
1889
2046
|
const path = getChannelHashCacheFile(configDir);
|
|
1890
2047
|
if (!existsSync3(path)) return;
|
|
1891
2048
|
let parsed;
|
|
1892
2049
|
try {
|
|
1893
|
-
parsed = JSON.parse(
|
|
2050
|
+
parsed = JSON.parse(readFileSync6(path, "utf-8"));
|
|
1894
2051
|
} catch {
|
|
1895
2052
|
return;
|
|
1896
2053
|
}
|
|
@@ -2435,15 +2592,15 @@ function clearAgentState(agentId, codeName) {
|
|
|
2435
2592
|
}
|
|
2436
2593
|
|
|
2437
2594
|
// src/lib/restart-flags.ts
|
|
2438
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as
|
|
2595
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync7, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2439
2596
|
import { homedir as homedir3 } from "os";
|
|
2440
|
-
import { join as
|
|
2597
|
+
import { join as join6 } from "path";
|
|
2441
2598
|
import { randomUUID } from "crypto";
|
|
2442
2599
|
function restartFlagsDir() {
|
|
2443
|
-
return
|
|
2600
|
+
return join6(homedir3(), ".augmented", "restart-flags");
|
|
2444
2601
|
}
|
|
2445
2602
|
function flagPath(codeName) {
|
|
2446
|
-
return
|
|
2603
|
+
return join6(restartFlagsDir(), `${codeName}.flag`);
|
|
2447
2604
|
}
|
|
2448
2605
|
function readRestartFlags() {
|
|
2449
2606
|
const dir = restartFlagsDir();
|
|
@@ -2452,7 +2609,7 @@ function readRestartFlags() {
|
|
|
2452
2609
|
for (const entry of readdirSync2(dir)) {
|
|
2453
2610
|
if (!entry.endsWith(".flag")) continue;
|
|
2454
2611
|
try {
|
|
2455
|
-
const raw =
|
|
2612
|
+
const raw = readFileSync7(join6(dir, entry), "utf8");
|
|
2456
2613
|
const parsed = JSON.parse(raw);
|
|
2457
2614
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
2458
2615
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -2981,8 +3138,8 @@ function applyRestartAcks(args) {
|
|
|
2981
3138
|
var GATEWAY_PORT_BASE = 18800;
|
|
2982
3139
|
var GATEWAY_PORT_STEP = 10;
|
|
2983
3140
|
var GATEWAY_PORT_MAX = 18899;
|
|
2984
|
-
var AUGMENTED_DIR =
|
|
2985
|
-
var GATEWAY_PORTS_FILE =
|
|
3141
|
+
var AUGMENTED_DIR = join7(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
3142
|
+
var GATEWAY_PORTS_FILE = join7(AUGMENTED_DIR, "gateway-ports.json");
|
|
2986
3143
|
var CHANNEL_SWEEP_INTERVAL_MS = (() => {
|
|
2987
3144
|
const raw = parseInt(process.env["AGT_CHANNEL_SWEEP_INTERVAL_MS"] ?? "", 10);
|
|
2988
3145
|
if (!Number.isFinite(raw)) return 5 * 60 * 1e3;
|
|
@@ -3151,7 +3308,7 @@ var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
|
3151
3308
|
var runningMcpServerKeys = /* @__PURE__ */ new Map();
|
|
3152
3309
|
function projectMcpHash(_codeName, projectDir) {
|
|
3153
3310
|
try {
|
|
3154
|
-
const raw =
|
|
3311
|
+
const raw = readFileSync8(join7(projectDir, ".mcp.json"), "utf-8");
|
|
3155
3312
|
return createHash3("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
3156
3313
|
} catch {
|
|
3157
3314
|
return null;
|
|
@@ -3159,7 +3316,7 @@ function projectMcpHash(_codeName, projectDir) {
|
|
|
3159
3316
|
}
|
|
3160
3317
|
function projectMcpKeys(_codeName, projectDir) {
|
|
3161
3318
|
try {
|
|
3162
|
-
const raw =
|
|
3319
|
+
const raw = readFileSync8(join7(projectDir, ".mcp.json"), "utf-8");
|
|
3163
3320
|
const parsed = JSON.parse(raw);
|
|
3164
3321
|
const servers = parsed.mcpServers;
|
|
3165
3322
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -3170,7 +3327,7 @@ function projectMcpKeys(_codeName, projectDir) {
|
|
|
3170
3327
|
}
|
|
3171
3328
|
function readMcpHttpServerConfig(projectDir, serverKey) {
|
|
3172
3329
|
try {
|
|
3173
|
-
const raw =
|
|
3330
|
+
const raw = readFileSync8(join7(projectDir, ".mcp.json"), "utf-8");
|
|
3174
3331
|
const servers = JSON.parse(raw).mcpServers ?? {};
|
|
3175
3332
|
const entry = servers[serverKey];
|
|
3176
3333
|
if (entry && typeof entry.url === "string" && (entry.type === "http" || entry.type === void 0)) {
|
|
@@ -3259,11 +3416,22 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
|
3259
3416
|
const previousKeys = runningMcpServerKeys.get(codeName);
|
|
3260
3417
|
const currentKeys = projectMcpKeys(codeName, projectDir);
|
|
3261
3418
|
const decision = decideMcpRestartOnDrift(previousKeys, currentKeys);
|
|
3419
|
+
let quarantineOnlyDrift = false;
|
|
3420
|
+
if (decision.restart && !decision.membershipUnknown && currentKeys) {
|
|
3421
|
+
const quarantined = channelQuarantineStore().getQuarantinedKeys(codeName);
|
|
3422
|
+
const added = decision.addedOrRemoved.filter((k) => currentKeys.has(k));
|
|
3423
|
+
const removed = decision.addedOrRemoved.filter((k) => !currentKeys.has(k));
|
|
3424
|
+
quarantineOnlyDrift = added.length === 0 && removed.length > 0 && removed.every((k) => quarantined.has(k));
|
|
3425
|
+
}
|
|
3262
3426
|
if (decision.membershipUnknown) {
|
|
3263
3427
|
clearPresenceReaperState(codeName);
|
|
3264
3428
|
log(
|
|
3265
3429
|
`[hot-reload] .mcp.json key membership unavailable for '${codeName}' \u2014 clearing full presence-reaper state and restarting (ENG-5285/ENG-5537)`
|
|
3266
3430
|
);
|
|
3431
|
+
} else if (quarantineOnlyDrift) {
|
|
3432
|
+
log(
|
|
3433
|
+
`[channel-quarantine] .mcp.json drift for '${codeName}' is quarantined-channel removal only [${decision.addedOrRemoved.join(", ")}] \u2014 adopting new baseline WITHOUT restart (0-restart-on-removal, ENG-5932)`
|
|
3434
|
+
);
|
|
3267
3435
|
} else if (decision.restart) {
|
|
3268
3436
|
clearPresenceReaperStateForKeys(codeName, new Set(decision.addedOrRemoved));
|
|
3269
3437
|
log(
|
|
@@ -3274,7 +3442,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
|
3274
3442
|
`[hot-reload] .mcp.json value-only drift for '${codeName}' \u2014 preserving presence-reaper state and NOT restarting (ENG-5285/ENG-5537)`
|
|
3275
3443
|
);
|
|
3276
3444
|
}
|
|
3277
|
-
if (decision.restart) {
|
|
3445
|
+
if (decision.restart && !quarantineOnlyDrift) {
|
|
3278
3446
|
scheduleSessionRestart(codeName, 0, ".mcp.json content change (ENG-4897)");
|
|
3279
3447
|
runningMcpHashes.delete(codeName);
|
|
3280
3448
|
runningMcpServerKeys.delete(codeName);
|
|
@@ -3344,7 +3512,7 @@ var cachedFrameworkVersion = null;
|
|
|
3344
3512
|
var lastVersionCheckAt = 0;
|
|
3345
3513
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3346
3514
|
var lastResponsivenessProbeAt = 0;
|
|
3347
|
-
var agtCliVersion = true ? "0.27.
|
|
3515
|
+
var agtCliVersion = true ? "0.27.53" : "dev";
|
|
3348
3516
|
function resolveBrewPath(execFileSync4) {
|
|
3349
3517
|
try {
|
|
3350
3518
|
const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -3521,7 +3689,7 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
3521
3689
|
try {
|
|
3522
3690
|
let settings = {};
|
|
3523
3691
|
if (existsSync5(path)) {
|
|
3524
|
-
const raw =
|
|
3692
|
+
const raw = readFileSync8(path, "utf-8").trim();
|
|
3525
3693
|
if (raw) {
|
|
3526
3694
|
let parsed;
|
|
3527
3695
|
try {
|
|
@@ -3593,7 +3761,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
3593
3761
|
var CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
3594
3762
|
var claudeCodeUpgradeInFlight = false;
|
|
3595
3763
|
function claudeCodeUpgradeMarkerPath() {
|
|
3596
|
-
return
|
|
3764
|
+
return join7(homedir4(), ".augmented", ".last-claude-code-upgrade-check");
|
|
3597
3765
|
}
|
|
3598
3766
|
function stampClaudeCodeUpgradeMarker() {
|
|
3599
3767
|
try {
|
|
@@ -3603,7 +3771,7 @@ function stampClaudeCodeUpgradeMarker() {
|
|
|
3603
3771
|
}
|
|
3604
3772
|
function claudeCodeUpgradeThrottled() {
|
|
3605
3773
|
try {
|
|
3606
|
-
const lastCheck = parseInt(
|
|
3774
|
+
const lastCheck = parseInt(readFileSync8(claudeCodeUpgradeMarkerPath(), "utf-8").trim(), 10);
|
|
3607
3775
|
if (!Number.isFinite(lastCheck)) return false;
|
|
3608
3776
|
return Date.now() - lastCheck < CLAUDE_CODE_UPGRADE_CHECK_INTERVAL_MS;
|
|
3609
3777
|
} catch {
|
|
@@ -3653,7 +3821,7 @@ ${r.stderr}`;
|
|
|
3653
3821
|
}
|
|
3654
3822
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
3655
3823
|
function selfUpdateAppliedMarkerPath() {
|
|
3656
|
-
return
|
|
3824
|
+
return join7(homedir4(), ".augmented", ".last-self-update-applied");
|
|
3657
3825
|
}
|
|
3658
3826
|
var selfUpdateUpToDateLogged = false;
|
|
3659
3827
|
var restartAfterUpgrade = false;
|
|
@@ -3676,7 +3844,7 @@ async function checkAndUpdateCli() {
|
|
|
3676
3844
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
3677
3845
|
if (!isBrewFormula && !isNpmGlobal) return;
|
|
3678
3846
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
3679
|
-
const markerPath =
|
|
3847
|
+
const markerPath = join7(homedir4(), ".augmented", ".last-update-check");
|
|
3680
3848
|
try {
|
|
3681
3849
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
3682
3850
|
if (Date.now() - lastCheck < UPDATE_CHECK_INTERVAL_MS) return;
|
|
@@ -3897,9 +4065,9 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3897
4065
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
3898
4066
|
}
|
|
3899
4067
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
3900
|
-
const claudeDir =
|
|
4068
|
+
const claudeDir = join7(homedir4(), ".claude");
|
|
3901
4069
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
3902
|
-
const p =
|
|
4070
|
+
const p = join7(claudeDir, filename);
|
|
3903
4071
|
if (existsSync5(p)) {
|
|
3904
4072
|
try {
|
|
3905
4073
|
rmSync2(p, { force: true });
|
|
@@ -3914,7 +4082,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
3914
4082
|
}
|
|
3915
4083
|
function loadGatewayPorts() {
|
|
3916
4084
|
try {
|
|
3917
|
-
return JSON.parse(
|
|
4085
|
+
return JSON.parse(readFileSync8(GATEWAY_PORTS_FILE, "utf-8"));
|
|
3918
4086
|
} catch {
|
|
3919
4087
|
return {};
|
|
3920
4088
|
}
|
|
@@ -3944,10 +4112,10 @@ function freePort(codeName) {
|
|
|
3944
4112
|
}
|
|
3945
4113
|
}
|
|
3946
4114
|
function getStateFile() {
|
|
3947
|
-
return
|
|
4115
|
+
return join7(config?.configDir ?? join7(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
3948
4116
|
}
|
|
3949
4117
|
function channelHashCacheDir() {
|
|
3950
|
-
return config?.configDir ??
|
|
4118
|
+
return config?.configDir ?? join7(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
3951
4119
|
}
|
|
3952
4120
|
function loadChannelHashCache2() {
|
|
3953
4121
|
loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -3955,6 +4123,30 @@ function loadChannelHashCache2() {
|
|
|
3955
4123
|
function saveChannelHashCache2() {
|
|
3956
4124
|
saveChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
3957
4125
|
}
|
|
4126
|
+
var _channelQuarantineStore = null;
|
|
4127
|
+
function channelQuarantineStore() {
|
|
4128
|
+
if (!_channelQuarantineStore) {
|
|
4129
|
+
const dir = config?.configDir ?? join7(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
4130
|
+
_channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
|
|
4131
|
+
}
|
|
4132
|
+
return _channelQuarantineStore;
|
|
4133
|
+
}
|
|
4134
|
+
function channelQuarantineMode() {
|
|
4135
|
+
const v = process.env["AGT_CHANNEL_QUARANTINE_MODE"]?.toLowerCase();
|
|
4136
|
+
if (v === "off") return "off";
|
|
4137
|
+
if (v === "enforce") return "enforce";
|
|
4138
|
+
return "shadow";
|
|
4139
|
+
}
|
|
4140
|
+
function channelQuarantineDwellMs() {
|
|
4141
|
+
const raw = parseInt(process.env["AGT_CHANNEL_QUARANTINE_DWELL_MS"] ?? "", 10);
|
|
4142
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : 18e4;
|
|
4143
|
+
}
|
|
4144
|
+
function setWithout(source, remove) {
|
|
4145
|
+
if (remove.size === 0) return new Set(source);
|
|
4146
|
+
const out = /* @__PURE__ */ new Set();
|
|
4147
|
+
for (const v of source) if (!remove.has(v)) out.add(v);
|
|
4148
|
+
return out;
|
|
4149
|
+
}
|
|
3958
4150
|
function send(msg) {
|
|
3959
4151
|
if (msg.type === "state-update") {
|
|
3960
4152
|
try {
|
|
@@ -3989,7 +4181,7 @@ function log(msg) {
|
|
|
3989
4181
|
`;
|
|
3990
4182
|
if (!managerLogPath) {
|
|
3991
4183
|
try {
|
|
3992
|
-
managerLogPath =
|
|
4184
|
+
managerLogPath = join7(homedir4(), ".augmented", "manager.log");
|
|
3993
4185
|
mkdirSync4(dirname3(managerLogPath), { recursive: true });
|
|
3994
4186
|
if (existsSync5(managerLogPath)) {
|
|
3995
4187
|
chmodSync(managerLogPath, 384);
|
|
@@ -4019,7 +4211,7 @@ function sha256(content) {
|
|
|
4019
4211
|
}
|
|
4020
4212
|
function hashFile(filePath) {
|
|
4021
4213
|
try {
|
|
4022
|
-
const content =
|
|
4214
|
+
const content = readFileSync8(filePath, "utf-8");
|
|
4023
4215
|
return sha256(content);
|
|
4024
4216
|
} catch {
|
|
4025
4217
|
return null;
|
|
@@ -4044,12 +4236,12 @@ function parseSkillFrontmatter(content) {
|
|
|
4044
4236
|
}
|
|
4045
4237
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
4046
4238
|
const { readdirSync: readdirSync4, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync5 } = await import("fs");
|
|
4047
|
-
const skillsDir =
|
|
4048
|
-
const claudeMdPath =
|
|
4239
|
+
const skillsDir = join7(configDir, codeName, "project", ".claude", "skills");
|
|
4240
|
+
const claudeMdPath = join7(configDir, codeName, "project", "CLAUDE.md");
|
|
4049
4241
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
4050
4242
|
const entries = [];
|
|
4051
4243
|
for (const dir of readdirSync4(skillsDir).sort()) {
|
|
4052
|
-
const skillFile =
|
|
4244
|
+
const skillFile = join7(skillsDir, dir, "SKILL.md");
|
|
4053
4245
|
if (!ex(skillFile)) continue;
|
|
4054
4246
|
try {
|
|
4055
4247
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -4097,10 +4289,10 @@ ${SKILLS_INDEX_END}`;
|
|
|
4097
4289
|
}
|
|
4098
4290
|
async function migrateToProfiles() {
|
|
4099
4291
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4100
|
-
const sharedConfigPath =
|
|
4292
|
+
const sharedConfigPath = join7(homeDir, ".openclaw", "openclaw.json");
|
|
4101
4293
|
let sharedConfig;
|
|
4102
4294
|
try {
|
|
4103
|
-
sharedConfig = JSON.parse(
|
|
4295
|
+
sharedConfig = JSON.parse(readFileSync8(sharedConfigPath, "utf-8"));
|
|
4104
4296
|
} catch {
|
|
4105
4297
|
return;
|
|
4106
4298
|
}
|
|
@@ -4113,19 +4305,19 @@ async function migrateToProfiles() {
|
|
|
4113
4305
|
const codeName = agentEntry["id"];
|
|
4114
4306
|
if (!codeName) continue;
|
|
4115
4307
|
if (codeName === "main") continue;
|
|
4116
|
-
const profileDir =
|
|
4117
|
-
if (existsSync5(
|
|
4308
|
+
const profileDir = join7(homeDir, `.openclaw-${codeName}`);
|
|
4309
|
+
if (existsSync5(join7(profileDir, "openclaw.json"))) continue;
|
|
4118
4310
|
log(`Migrating agent '${codeName}' to per-agent profile`);
|
|
4119
4311
|
if (adapter.seedProfileConfig) {
|
|
4120
4312
|
adapter.seedProfileConfig(codeName);
|
|
4121
4313
|
}
|
|
4122
|
-
const sharedAuthDir =
|
|
4123
|
-
const profileAuthDir =
|
|
4124
|
-
const authFile =
|
|
4314
|
+
const sharedAuthDir = join7(homeDir, ".openclaw", "agents", codeName, "agent");
|
|
4315
|
+
const profileAuthDir = join7(profileDir, "agents", codeName, "agent");
|
|
4316
|
+
const authFile = join7(sharedAuthDir, "auth-profiles.json");
|
|
4125
4317
|
if (existsSync5(authFile)) {
|
|
4126
4318
|
mkdirSync4(profileAuthDir, { recursive: true });
|
|
4127
|
-
const authContent =
|
|
4128
|
-
writeFileSync4(
|
|
4319
|
+
const authContent = readFileSync8(authFile, "utf-8");
|
|
4320
|
+
writeFileSync4(join7(profileAuthDir, "auth-profiles.json"), authContent);
|
|
4129
4321
|
}
|
|
4130
4322
|
allocatePort(codeName);
|
|
4131
4323
|
migrated++;
|
|
@@ -4163,7 +4355,7 @@ function readGatewayToken(codeName) {
|
|
|
4163
4355
|
}
|
|
4164
4356
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4165
4357
|
try {
|
|
4166
|
-
const cfg = JSON.parse(
|
|
4358
|
+
const cfg = JSON.parse(readFileSync8(join7(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
4167
4359
|
return cfg?.gateway?.auth?.token;
|
|
4168
4360
|
} catch {
|
|
4169
4361
|
return void 0;
|
|
@@ -4172,10 +4364,10 @@ function readGatewayToken(codeName) {
|
|
|
4172
4364
|
var GATEWAY_HUNG_TIMEOUT_MS = 5 * 6e4;
|
|
4173
4365
|
function isGatewayHung(codeName) {
|
|
4174
4366
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4175
|
-
const jobsPath =
|
|
4367
|
+
const jobsPath = join7(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
4176
4368
|
if (!existsSync5(jobsPath)) return false;
|
|
4177
4369
|
try {
|
|
4178
|
-
const data = JSON.parse(
|
|
4370
|
+
const data = JSON.parse(readFileSync8(jobsPath, "utf-8"));
|
|
4179
4371
|
const jobs = data.jobs ?? data;
|
|
4180
4372
|
if (!Array.isArray(jobs)) return false;
|
|
4181
4373
|
const now = Date.now();
|
|
@@ -4208,15 +4400,15 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4208
4400
|
}
|
|
4209
4401
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
4210
4402
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4211
|
-
const cronJobsPath =
|
|
4403
|
+
const cronJobsPath = join7(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
4212
4404
|
clearStaleCronRunState(cronJobsPath);
|
|
4213
4405
|
} else {
|
|
4214
4406
|
if (status.port) {
|
|
4215
4407
|
try {
|
|
4216
4408
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4217
|
-
const configPath =
|
|
4409
|
+
const configPath = join7(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4218
4410
|
if (existsSync5(configPath)) {
|
|
4219
|
-
const cfg = JSON.parse(
|
|
4411
|
+
const cfg = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
4220
4412
|
if (cfg.gateway?.port !== status.port) {
|
|
4221
4413
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4222
4414
|
cfg.gateway.port = status.port;
|
|
@@ -4240,9 +4432,9 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
4240
4432
|
gatewaysStartedThisCycle.add(codeName);
|
|
4241
4433
|
try {
|
|
4242
4434
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
4243
|
-
const configPath =
|
|
4435
|
+
const configPath = join7(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
4244
4436
|
if (existsSync5(configPath)) {
|
|
4245
|
-
const cfg = JSON.parse(
|
|
4437
|
+
const cfg = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
4246
4438
|
if (!cfg.gateway) cfg.gateway = {};
|
|
4247
4439
|
cfg.gateway.port = port;
|
|
4248
4440
|
writeFileSync4(configPath, JSON.stringify(cfg, null, 2));
|
|
@@ -4395,7 +4587,7 @@ async function pollCycle() {
|
|
|
4395
4587
|
}
|
|
4396
4588
|
try {
|
|
4397
4589
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
4398
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
4590
|
+
const { collectDiagnostics } = await import("../persistent-session-6TWTADQN.js");
|
|
4399
4591
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
4400
4592
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
4401
4593
|
let tailscaleHostname;
|
|
@@ -4463,7 +4655,7 @@ async function pollCycle() {
|
|
|
4463
4655
|
const {
|
|
4464
4656
|
collectResponsivenessProbes,
|
|
4465
4657
|
getResponsivenessIntervalMs
|
|
4466
|
-
} = await import("../responsiveness-probe-
|
|
4658
|
+
} = await import("../responsiveness-probe-KBUKS7PN.js");
|
|
4467
4659
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
4468
4660
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
4469
4661
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -4608,7 +4800,7 @@ async function pollCycle() {
|
|
|
4608
4800
|
}
|
|
4609
4801
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
4610
4802
|
freePort(prev.codeName);
|
|
4611
|
-
const agentDir =
|
|
4803
|
+
const agentDir = join7(adapter.getAgentDir(prev.codeName), "provision");
|
|
4612
4804
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
4613
4805
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
4614
4806
|
}
|
|
@@ -4759,7 +4951,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4759
4951
|
}
|
|
4760
4952
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4761
4953
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
4762
|
-
let agentDir =
|
|
4954
|
+
let agentDir = join7(adapter.getAgentDir(agent.code_name), "provision");
|
|
4763
4955
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
4764
4956
|
if (previousKnownStatus !== agent.status) {
|
|
4765
4957
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -4928,7 +5120,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4928
5120
|
const frameworkId = refreshData.agent.framework ?? "openclaw";
|
|
4929
5121
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
4930
5122
|
const frameworkAdapter = getFramework(frameworkId);
|
|
4931
|
-
agentDir =
|
|
5123
|
+
agentDir = join7(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
4932
5124
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
4933
5125
|
if (frameworkAdapter.seedProfileConfig) {
|
|
4934
5126
|
frameworkAdapter.seedProfileConfig(agent.code_name);
|
|
@@ -4937,7 +5129,11 @@ async function processAgent(agent, agentStates) {
|
|
|
4937
5129
|
const toolsVersion = refreshData.tools.version;
|
|
4938
5130
|
const known = agentState.knownVersions.get(agent.agent_id);
|
|
4939
5131
|
let lastProvisionAt = state4.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
|
|
4940
|
-
const
|
|
5132
|
+
const quarantinedChannels = channelQuarantineStore().getQuarantinedKeys(agent.code_name);
|
|
5133
|
+
const currentChannelIds = setWithout(
|
|
5134
|
+
launchableChannelIds(refreshData.channel_configs),
|
|
5135
|
+
quarantinedChannels
|
|
5136
|
+
);
|
|
4941
5137
|
const previousChannelIds = agentState.knownChannels.get(agent.agent_id);
|
|
4942
5138
|
const channelsChanged = !previousChannelIds || currentChannelIds.size !== previousChannelIds.size || [...currentChannelIds].some((ch) => !previousChannelIds.has(ch)) || [...previousChannelIds].some((ch) => !currentChannelIds.has(ch));
|
|
4943
5139
|
let channelConfigConverged = true;
|
|
@@ -4959,7 +5155,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4959
5155
|
const changedFiles = [];
|
|
4960
5156
|
mkdirSync4(agentDir, { recursive: true });
|
|
4961
5157
|
for (const artifact of artifacts) {
|
|
4962
|
-
const filePath =
|
|
5158
|
+
const filePath = join7(agentDir, artifact.relativePath);
|
|
4963
5159
|
let existingHash;
|
|
4964
5160
|
let newHash;
|
|
4965
5161
|
let writeContent = artifact.content;
|
|
@@ -4978,8 +5174,8 @@ async function processAgent(agent, agentStates) {
|
|
|
4978
5174
|
};
|
|
4979
5175
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
4980
5176
|
try {
|
|
4981
|
-
const projectClaudeMd =
|
|
4982
|
-
const existing =
|
|
5177
|
+
const projectClaudeMd = join7(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
5178
|
+
const existing = readFileSync8(projectClaudeMd, "utf-8");
|
|
4983
5179
|
existingHash = sha256(stripDynamicSections(existing));
|
|
4984
5180
|
} catch {
|
|
4985
5181
|
existingHash = null;
|
|
@@ -4997,7 +5193,7 @@ async function processAgent(agent, agentStates) {
|
|
|
4997
5193
|
const generatorKeys = Object.keys(generatorServers);
|
|
4998
5194
|
let existingRaw = "";
|
|
4999
5195
|
try {
|
|
5000
|
-
existingRaw =
|
|
5196
|
+
existingRaw = readFileSync8(filePath, "utf-8");
|
|
5001
5197
|
} catch {
|
|
5002
5198
|
}
|
|
5003
5199
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -5019,22 +5215,22 @@ async function processAgent(agent, agentStates) {
|
|
|
5019
5215
|
}
|
|
5020
5216
|
}
|
|
5021
5217
|
if (changedFiles.length > 0) {
|
|
5022
|
-
const isFirst = !existsSync5(
|
|
5218
|
+
const isFirst = !existsSync5(join7(agentDir, "CHARTER.md"));
|
|
5023
5219
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
5024
5220
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
5025
5221
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
5026
5222
|
for (const file of changedFiles) {
|
|
5027
|
-
const filePath =
|
|
5223
|
+
const filePath = join7(agentDir, file.relativePath);
|
|
5028
5224
|
mkdirSync4(dirname3(filePath), { recursive: true });
|
|
5029
5225
|
writeFileSync4(filePath, file.content);
|
|
5030
5226
|
}
|
|
5031
5227
|
try {
|
|
5032
|
-
const provSkillsDir =
|
|
5228
|
+
const provSkillsDir = join7(agentDir, ".claude", "skills");
|
|
5033
5229
|
if (existsSync5(provSkillsDir)) {
|
|
5034
5230
|
for (const folder of readdirSync3(provSkillsDir)) {
|
|
5035
5231
|
if (folder.startsWith("knowledge-")) {
|
|
5036
5232
|
try {
|
|
5037
|
-
rmSync2(
|
|
5233
|
+
rmSync2(join7(provSkillsDir, folder), { recursive: true });
|
|
5038
5234
|
} catch {
|
|
5039
5235
|
}
|
|
5040
5236
|
}
|
|
@@ -5047,7 +5243,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5047
5243
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
5048
5244
|
const hashes = /* @__PURE__ */ new Map();
|
|
5049
5245
|
for (const file of trackedFiles2) {
|
|
5050
|
-
const h = hashFile(
|
|
5246
|
+
const h = hashFile(join7(agentDir, file));
|
|
5051
5247
|
if (h) hashes.set(file, h);
|
|
5052
5248
|
}
|
|
5053
5249
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -5115,7 +5311,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5115
5311
|
if (written && existsSync5(agentDir)) {
|
|
5116
5312
|
const driftedFiles = [];
|
|
5117
5313
|
for (const [file, expectedHash] of written) {
|
|
5118
|
-
const localHash = hashFile(
|
|
5314
|
+
const localHash = hashFile(join7(agentDir, file));
|
|
5119
5315
|
if (localHash && localHash !== expectedHash) {
|
|
5120
5316
|
driftedFiles.push(file);
|
|
5121
5317
|
}
|
|
@@ -5126,7 +5322,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5126
5322
|
try {
|
|
5127
5323
|
const localHashes = {};
|
|
5128
5324
|
for (const file of driftedFiles) {
|
|
5129
|
-
localHashes[file] = hashFile(
|
|
5325
|
+
localHashes[file] = hashFile(join7(agentDir, file));
|
|
5130
5326
|
}
|
|
5131
5327
|
await api.post("/host/drift", {
|
|
5132
5328
|
agent_id: agent.agent_id,
|
|
@@ -5164,6 +5360,14 @@ async function processAgent(agent, agentStates) {
|
|
|
5164
5360
|
if (refreshData.channel_configs && frameworkAdapter.writeChannelCredentials) {
|
|
5165
5361
|
if (agent.status === "active") {
|
|
5166
5362
|
for (const [channelId, entry] of Object.entries(refreshData.channel_configs)) {
|
|
5363
|
+
if (quarantinedChannels.has(channelId)) {
|
|
5364
|
+
try {
|
|
5365
|
+
frameworkAdapter.removeChannelCredentials?.(agent.code_name, channelId);
|
|
5366
|
+
} catch (err) {
|
|
5367
|
+
log(`[channel-quarantine] failed to drop quarantined channel '${agent.code_name}/${channelId}' from .mcp.json: ${err.message}`);
|
|
5368
|
+
}
|
|
5369
|
+
continue;
|
|
5370
|
+
}
|
|
5167
5371
|
if ((entry.status === "active" || entry.status === "pending") && entry.config) {
|
|
5168
5372
|
if (!activeChannels.has(channelId)) {
|
|
5169
5373
|
activeChannels.set(channelId, /* @__PURE__ */ new Set());
|
|
@@ -5270,7 +5474,14 @@ async function processAgent(agent, agentStates) {
|
|
|
5270
5474
|
framework: agentFrameworkCache.get(agent.code_name) ?? "openclaw",
|
|
5271
5475
|
sessionHealthy: isSessionHealthy(agent.code_name)
|
|
5272
5476
|
}) : { restart: false, added: [], removed: [] };
|
|
5273
|
-
|
|
5477
|
+
const quarantineOnlyRemoval = restartDecision.restart && restartDecision.added.length === 0 && restartDecision.removed.length > 0 && restartDecision.removed.every((c) => quarantinedChannels.has(c));
|
|
5478
|
+
const channelSetRestartScheduled = restartDecision.restart && !quarantineOnlyRemoval;
|
|
5479
|
+
if (quarantineOnlyRemoval) {
|
|
5480
|
+
log(
|
|
5481
|
+
`[channel-quarantine] suppressing channel-set-change restart for '${agent.code_name}' \u2014 only delta is quarantined-channel removal [${restartDecision.removed.join(", ")}] (0-restart-on-removal, ENG-5932)`
|
|
5482
|
+
);
|
|
5483
|
+
}
|
|
5484
|
+
if (channelSetRestartScheduled) {
|
|
5274
5485
|
const reasonParts = [];
|
|
5275
5486
|
if (restartDecision.added.length > 0) reasonParts.push(`added=${restartDecision.added.join(",")}`);
|
|
5276
5487
|
if (restartDecision.removed.length > 0) reasonParts.push(`removed=${restartDecision.removed.join(",")}`);
|
|
@@ -5295,7 +5506,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5295
5506
|
sessionMode: refreshData.agent.session_mode,
|
|
5296
5507
|
framework: agentFrameworkCache.get(agent.code_name) ?? "openclaw",
|
|
5297
5508
|
sessionHealthy: isSessionHealthy(agent.code_name),
|
|
5298
|
-
channelSetRestartAlreadyScheduled:
|
|
5509
|
+
channelSetRestartAlreadyScheduled: channelSetRestartScheduled
|
|
5299
5510
|
}) : { restart: false, firstPoll: prevSenderPolicyHash === void 0, changed: false };
|
|
5300
5511
|
if (senderPolicyDecision.restart) {
|
|
5301
5512
|
log(
|
|
@@ -5328,18 +5539,18 @@ async function processAgent(agent, agentStates) {
|
|
|
5328
5539
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? "openclaw") === "claude-code") {
|
|
5329
5540
|
try {
|
|
5330
5541
|
const agentProvisionDir = agentDir;
|
|
5331
|
-
const projectDir =
|
|
5542
|
+
const projectDir = join7(homedir4(), ".augmented", agent.code_name, "project");
|
|
5332
5543
|
mkdirSync4(agentProvisionDir, { recursive: true });
|
|
5333
5544
|
mkdirSync4(projectDir, { recursive: true });
|
|
5334
|
-
const provisionMcpPath =
|
|
5335
|
-
const projectMcpPath =
|
|
5545
|
+
const provisionMcpPath = join7(agentProvisionDir, ".mcp.json");
|
|
5546
|
+
const projectMcpPath = join7(projectDir, ".mcp.json");
|
|
5336
5547
|
let mcpConfig = { mcpServers: {} };
|
|
5337
5548
|
try {
|
|
5338
|
-
mcpConfig = JSON.parse(
|
|
5549
|
+
mcpConfig = JSON.parse(readFileSync8(provisionMcpPath, "utf-8"));
|
|
5339
5550
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
5340
5551
|
} catch {
|
|
5341
5552
|
}
|
|
5342
|
-
const localDirectChatChannel =
|
|
5553
|
+
const localDirectChatChannel = join7(homedir4(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
5343
5554
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
5344
5555
|
const directChatTz = (() => {
|
|
5345
5556
|
const tz = directChatTeamSettings?.["timezone"];
|
|
@@ -5366,7 +5577,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5366
5577
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
5367
5578
|
}
|
|
5368
5579
|
}
|
|
5369
|
-
const staleChannelsPath =
|
|
5580
|
+
const staleChannelsPath = join7(projectDir, ".mcp-channels.json");
|
|
5370
5581
|
if (existsSync5(staleChannelsPath)) {
|
|
5371
5582
|
try {
|
|
5372
5583
|
rmSync2(staleChannelsPath, { force: true });
|
|
@@ -5431,7 +5642,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5431
5642
|
}
|
|
5432
5643
|
if (process.env.AGT_CONNECTIVITY_PROBE_ENABLED === "true") {
|
|
5433
5644
|
try {
|
|
5434
|
-
const probeProjectDir =
|
|
5645
|
+
const probeProjectDir = join7(homedir4(), ".augmented", agent.code_name, "project");
|
|
5435
5646
|
await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
|
|
5436
5647
|
} catch (err) {
|
|
5437
5648
|
log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -5441,11 +5652,11 @@ async function processAgent(agent, agentStates) {
|
|
|
5441
5652
|
const intHash = computeIntegrationsHash(integrations);
|
|
5442
5653
|
const prevIntHash = agentState.knownIntegrationHashes.get(agent.agent_id);
|
|
5443
5654
|
if (intHash !== prevIntHash) {
|
|
5444
|
-
const projectDir =
|
|
5445
|
-
const envIntPath =
|
|
5655
|
+
const projectDir = join7(homedir4(), ".augmented", agent.code_name, "project");
|
|
5656
|
+
const envIntPath = join7(projectDir, ".env.integrations");
|
|
5446
5657
|
let preWriteEnv;
|
|
5447
5658
|
try {
|
|
5448
|
-
preWriteEnv =
|
|
5659
|
+
preWriteEnv = readFileSync8(envIntPath, "utf-8");
|
|
5449
5660
|
} catch {
|
|
5450
5661
|
preWriteEnv = void 0;
|
|
5451
5662
|
}
|
|
@@ -5457,9 +5668,9 @@ async function processAgent(agent, agentStates) {
|
|
|
5457
5668
|
let rotationHandled = true;
|
|
5458
5669
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
5459
5670
|
try {
|
|
5460
|
-
const projectMcpPath =
|
|
5461
|
-
const postWriteEnv =
|
|
5462
|
-
const mcpContent =
|
|
5671
|
+
const projectMcpPath = join7(projectDir, ".mcp.json");
|
|
5672
|
+
const postWriteEnv = readFileSync8(envIntPath, "utf-8");
|
|
5673
|
+
const mcpContent = readFileSync8(projectMcpPath, "utf-8");
|
|
5463
5674
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
5464
5675
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
5465
5676
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -5527,8 +5738,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5527
5738
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
5528
5739
|
if (mcpPath) {
|
|
5529
5740
|
try {
|
|
5530
|
-
const { readFileSync:
|
|
5531
|
-
const mcpConfig = JSON.parse(
|
|
5741
|
+
const { readFileSync: readFileSync9 } = await import("fs");
|
|
5742
|
+
const mcpConfig = JSON.parse(readFileSync9(mcpPath, "utf-8"));
|
|
5532
5743
|
if (mcpConfig.mcpServers) {
|
|
5533
5744
|
const managedPrefixes = [
|
|
5534
5745
|
"composio_",
|
|
@@ -5624,7 +5835,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5624
5835
|
if (agent.status === "active") {
|
|
5625
5836
|
if (frameworkAdapter.installPlugin) {
|
|
5626
5837
|
try {
|
|
5627
|
-
const pluginPath =
|
|
5838
|
+
const pluginPath = join7(process.cwd(), "packages", "openclaw-plugin-augmented", "src", "index.ts");
|
|
5628
5839
|
if (existsSync5(pluginPath)) {
|
|
5629
5840
|
frameworkAdapter.installPlugin(agent.code_name, "augmented", pluginPath, {
|
|
5630
5841
|
agtHost: requireHost(),
|
|
@@ -5695,14 +5906,14 @@ async function processAgent(agent, agentStates) {
|
|
|
5695
5906
|
const frameworkId2 = frameworkAdapter.id;
|
|
5696
5907
|
const candidateSkillDirs = [
|
|
5697
5908
|
// Claude Code — framework runtime tree
|
|
5698
|
-
|
|
5909
|
+
join7(homedir5(), ".augmented", agent.code_name, "skills"),
|
|
5699
5910
|
// Claude Code — project tree
|
|
5700
|
-
|
|
5911
|
+
join7(homedir5(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
5701
5912
|
// OpenClaw — framework runtime tree
|
|
5702
|
-
|
|
5913
|
+
join7(homedir5(), `.openclaw-${agent.code_name}`, "skills"),
|
|
5703
5914
|
// Defensive: legacy provision-side path, not currently an
|
|
5704
5915
|
// install target but cheap to sweep.
|
|
5705
|
-
|
|
5916
|
+
join7(agentDir, ".claude", "skills")
|
|
5706
5917
|
];
|
|
5707
5918
|
const existingDirs = candidateSkillDirs.filter((d) => existsSync5(d));
|
|
5708
5919
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
@@ -5718,7 +5929,7 @@ async function processAgent(agent, agentStates) {
|
|
|
5718
5929
|
}
|
|
5719
5930
|
const removeSkillFolder = (entry, reason) => {
|
|
5720
5931
|
for (const dir of existingDirs) {
|
|
5721
|
-
const p =
|
|
5932
|
+
const p = join7(dir, entry);
|
|
5722
5933
|
if (existsSync5(p)) {
|
|
5723
5934
|
rmSync3(p, { recursive: true, force: true });
|
|
5724
5935
|
}
|
|
@@ -5880,8 +6091,8 @@ async function processAgent(agent, agentStates) {
|
|
|
5880
6091
|
const sess = getSessionState(agent.code_name);
|
|
5881
6092
|
let mcpJsonParsed = null;
|
|
5882
6093
|
try {
|
|
5883
|
-
const mcpPath =
|
|
5884
|
-
mcpJsonParsed = JSON.parse(
|
|
6094
|
+
const mcpPath = join7(getProjectDir(agent.code_name), ".mcp.json");
|
|
6095
|
+
mcpJsonParsed = JSON.parse(readFileSync8(mcpPath, "utf-8"));
|
|
5885
6096
|
} catch {
|
|
5886
6097
|
}
|
|
5887
6098
|
reapMissingMcpSessions({
|
|
@@ -5952,6 +6163,37 @@ async function processAgent(agent, agentStates) {
|
|
|
5952
6163
|
`[mcp-presence-reaper] failed to record restart event for '${codeName}' (ENG-5286): ${err.message} \u2014 local restart still proceeded; CloudWatch metric will under-count this event`
|
|
5953
6164
|
);
|
|
5954
6165
|
});
|
|
6166
|
+
},
|
|
6167
|
+
// ENG-5932: classify each declared key for the quarantine path. Reads a
|
|
6168
|
+
// server-declared criticality field off the .mcp.json entry when
|
|
6169
|
+
// present (Slice 2 will stamp it); otherwise fails closed to ESSENTIAL
|
|
6170
|
+
// for everything except the known optional channel set. Tool servers
|
|
6171
|
+
// (composio_*/xero) classify ESSENTIAL here and stay on the existing
|
|
6172
|
+
// managed-toolkit onGiveUp path above.
|
|
6173
|
+
classifyKey: (serverKey) => classifyChannelCriticality(
|
|
6174
|
+
serverKey,
|
|
6175
|
+
mcpJsonParsed?.mcpServers?.[serverKey]
|
|
6176
|
+
),
|
|
6177
|
+
quarantineDwellMs: channelQuarantineDwellMs(),
|
|
6178
|
+
quarantineMode: channelQuarantineMode(),
|
|
6179
|
+
// ENG-5932: enforce-mode quarantine. Persist the local marker; the next
|
|
6180
|
+
// provisioning poll then omits the channel from .mcp.json + the launch
|
|
6181
|
+
// flags, and the channel-set-change / .mcp.json-drift restart paths are
|
|
6182
|
+
// taught to NOT bounce the session for a quarantine-driven removal.
|
|
6183
|
+
// Local-authoritative: no API call — with the control plane down, the
|
|
6184
|
+
// flap still stops.
|
|
6185
|
+
onQuarantine: (codeName, serverKey) => {
|
|
6186
|
+
const newly = channelQuarantineStore().quarantine(
|
|
6187
|
+
codeName,
|
|
6188
|
+
serverKey,
|
|
6189
|
+
`mcp-presence-reaper: optional channel dead past restart budget + dwell`,
|
|
6190
|
+
Date.now()
|
|
6191
|
+
);
|
|
6192
|
+
if (newly) {
|
|
6193
|
+
log(
|
|
6194
|
+
`[channel-quarantine] persisted quarantine marker for '${codeName}:${serverKey}' \u2014 will be dropped from .mcp.json on next provisioning poll (ENG-5932)`
|
|
6195
|
+
);
|
|
6196
|
+
}
|
|
5955
6197
|
}
|
|
5956
6198
|
});
|
|
5957
6199
|
} catch (err) {
|
|
@@ -6035,10 +6277,10 @@ async function processAgent(agent, agentStates) {
|
|
|
6035
6277
|
lastWorkTriggerAt.set(agent.code_name, triggerTs);
|
|
6036
6278
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort) {
|
|
6037
6279
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
6038
|
-
const jobsPath =
|
|
6280
|
+
const jobsPath = join7(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
6039
6281
|
if (existsSync5(jobsPath)) {
|
|
6040
6282
|
try {
|
|
6041
|
-
const jobsData = JSON.parse(
|
|
6283
|
+
const jobsData = JSON.parse(readFileSync8(jobsPath, "utf-8"));
|
|
6042
6284
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
6043
6285
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
6044
6286
|
);
|
|
@@ -6175,7 +6417,7 @@ In progress for ${age} minutes \u2014 auto-failed`).catch(() => {
|
|
|
6175
6417
|
if (trackedFiles.length > 0 && existsSync5(agentDir)) {
|
|
6176
6418
|
const hashes = /* @__PURE__ */ new Map();
|
|
6177
6419
|
for (const file of trackedFiles) {
|
|
6178
|
-
const h = hashFile(
|
|
6420
|
+
const h = hashFile(join7(agentDir, file));
|
|
6179
6421
|
if (h) hashes.set(file, h);
|
|
6180
6422
|
}
|
|
6181
6423
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -6209,19 +6451,19 @@ function cleanupStaleSessions(codeName) {
|
|
|
6209
6451
|
lastCleanupAt.set(codeName, Date.now());
|
|
6210
6452
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
6211
6453
|
for (const agentDir of ["main", codeName]) {
|
|
6212
|
-
const sessionsDir =
|
|
6454
|
+
const sessionsDir = join7(homeDir, `.openclaw-${codeName}`, "agents", agentDir, "sessions");
|
|
6213
6455
|
cleanupCronSessions(sessionsDir, CRON_SESSION_KEEP_COUNT);
|
|
6214
6456
|
}
|
|
6215
|
-
const cronRunsDir =
|
|
6457
|
+
const cronRunsDir = join7(homeDir, `.openclaw-${codeName}`, "cron", "runs");
|
|
6216
6458
|
cleanupOldFiles(cronRunsDir, CRON_RUN_RETENTION_DAYS, ".jsonl");
|
|
6217
|
-
const cronJobsPath =
|
|
6459
|
+
const cronJobsPath = join7(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
6218
6460
|
clearStaleCronRunState(cronJobsPath);
|
|
6219
6461
|
}
|
|
6220
6462
|
function cleanupCronSessions(sessionsDir, keepCount) {
|
|
6221
|
-
const indexPath =
|
|
6463
|
+
const indexPath = join7(sessionsDir, "sessions.json");
|
|
6222
6464
|
if (!existsSync5(indexPath)) return;
|
|
6223
6465
|
try {
|
|
6224
|
-
const raw =
|
|
6466
|
+
const raw = readFileSync8(indexPath, "utf-8");
|
|
6225
6467
|
const index = JSON.parse(raw);
|
|
6226
6468
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
6227
6469
|
key: k,
|
|
@@ -6234,7 +6476,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6234
6476
|
for (const entry of toDelete) {
|
|
6235
6477
|
delete index[entry.key];
|
|
6236
6478
|
if (entry.sessionId) {
|
|
6237
|
-
const sessionFile =
|
|
6479
|
+
const sessionFile = join7(sessionsDir, `${entry.sessionId}.jsonl`);
|
|
6238
6480
|
try {
|
|
6239
6481
|
if (existsSync5(sessionFile)) {
|
|
6240
6482
|
unlinkSync(sessionFile);
|
|
@@ -6256,7 +6498,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
6256
6498
|
delete index[parentKey];
|
|
6257
6499
|
if (parentSessionId) {
|
|
6258
6500
|
try {
|
|
6259
|
-
const f =
|
|
6501
|
+
const f = join7(sessionsDir, `${parentSessionId}.jsonl`);
|
|
6260
6502
|
if (existsSync5(f)) {
|
|
6261
6503
|
unlinkSync(f);
|
|
6262
6504
|
deletedFiles++;
|
|
@@ -6277,7 +6519,7 @@ var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
|
6277
6519
|
function clearStaleCronRunState(jobsPath) {
|
|
6278
6520
|
if (!existsSync5(jobsPath)) return;
|
|
6279
6521
|
try {
|
|
6280
|
-
const raw =
|
|
6522
|
+
const raw = readFileSync8(jobsPath, "utf-8");
|
|
6281
6523
|
const data = JSON.parse(raw);
|
|
6282
6524
|
const jobs = data.jobs ?? data;
|
|
6283
6525
|
if (!Array.isArray(jobs)) return;
|
|
@@ -6314,7 +6556,7 @@ function cleanupOldFiles(dir, maxAgeDays, ext) {
|
|
|
6314
6556
|
try {
|
|
6315
6557
|
for (const f of readdirSync3(dir)) {
|
|
6316
6558
|
if (!f.endsWith(ext)) continue;
|
|
6317
|
-
const fullPath =
|
|
6559
|
+
const fullPath = join7(dir, f);
|
|
6318
6560
|
try {
|
|
6319
6561
|
const st = statSync2(fullPath);
|
|
6320
6562
|
if (st.mtimeMs < cutoff) {
|
|
@@ -6334,7 +6576,7 @@ var inFlightClaudeTasks = /* @__PURE__ */ new Set();
|
|
|
6334
6576
|
var claudeTaskConcurrency = /* @__PURE__ */ new Map();
|
|
6335
6577
|
var MAX_CLAUDE_CONCURRENCY = 2;
|
|
6336
6578
|
function claudePidFilePath() {
|
|
6337
|
-
return
|
|
6579
|
+
return join7(homedir4(), ".augmented", "manager-claude-pids.json");
|
|
6338
6580
|
}
|
|
6339
6581
|
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
6340
6582
|
function registerClaudeSpawn(record) {
|
|
@@ -6642,7 +6884,7 @@ async function fireScheduledTaskViaKanban(codeName, agentId, task, prompt) {
|
|
|
6642
6884
|
}
|
|
6643
6885
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
6644
6886
|
const projectDir = getProjectDir2(codeName);
|
|
6645
|
-
const mcpConfigPath =
|
|
6887
|
+
const mcpConfigPath = join7(projectDir, ".mcp.json");
|
|
6646
6888
|
let runId = null;
|
|
6647
6889
|
let kanbanItemId = null;
|
|
6648
6890
|
let taskResult;
|
|
@@ -6650,11 +6892,11 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
6650
6892
|
const priorRuns = await fetchPriorScheduledRuns(agentId, task.taskId);
|
|
6651
6893
|
prompt = wrapScheduledTaskPrompt(prompt, { priorRuns });
|
|
6652
6894
|
try {
|
|
6653
|
-
const claudeMdPath =
|
|
6895
|
+
const claudeMdPath = join7(projectDir, "CLAUDE.md");
|
|
6654
6896
|
const serverNames = [];
|
|
6655
6897
|
if (existsSync5(mcpConfigPath)) {
|
|
6656
6898
|
try {
|
|
6657
|
-
const d = JSON.parse(
|
|
6899
|
+
const d = JSON.parse(readFileSync8(mcpConfigPath, "utf-8"));
|
|
6658
6900
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
6659
6901
|
} catch {
|
|
6660
6902
|
}
|
|
@@ -6677,10 +6919,10 @@ async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
|
6677
6919
|
claudeArgs.push("--system-prompt-file", claudeMdPath);
|
|
6678
6920
|
}
|
|
6679
6921
|
const childEnv = { ...process.env };
|
|
6680
|
-
const envIntPath =
|
|
6922
|
+
const envIntPath = join7(projectDir, ".env.integrations");
|
|
6681
6923
|
if (existsSync5(envIntPath)) {
|
|
6682
6924
|
try {
|
|
6683
|
-
for (const line of
|
|
6925
|
+
for (const line of readFileSync8(envIntPath, "utf-8").split("\n")) {
|
|
6684
6926
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
6685
6927
|
const eqIdx = line.indexOf("=");
|
|
6686
6928
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -6849,8 +7091,8 @@ var claudeAuthTupleBySession = /* @__PURE__ */ new Map();
|
|
|
6849
7091
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
6850
7092
|
const codeName = agent.code_name;
|
|
6851
7093
|
const projectDir = getProjectDir(codeName);
|
|
6852
|
-
const mcpConfigPath =
|
|
6853
|
-
const claudeMdPath =
|
|
7094
|
+
const mcpConfigPath = join7(projectDir, ".mcp.json");
|
|
7095
|
+
const claudeMdPath = join7(projectDir, "CLAUDE.md");
|
|
6854
7096
|
if (restartBreaker.isTripped(codeName)) {
|
|
6855
7097
|
const trip = restartBreaker.getTrip(codeName);
|
|
6856
7098
|
return {
|
|
@@ -6869,7 +7111,9 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
6869
7111
|
const channels = [];
|
|
6870
7112
|
const devChannels = [];
|
|
6871
7113
|
if (channelConfigs) {
|
|
7114
|
+
const quarantinedChannels = channelQuarantineStore().getQuarantinedKeys(codeName);
|
|
6872
7115
|
const isChannelEnabled = (id) => {
|
|
7116
|
+
if (quarantinedChannels.has(id)) return false;
|
|
6873
7117
|
const entry = channelConfigs[id];
|
|
6874
7118
|
return !!entry?.config && (entry.status === "active" || entry.status === "pending");
|
|
6875
7119
|
};
|
|
@@ -7471,11 +7715,11 @@ ${escapeXml(msg.content)}
|
|
|
7471
7715
|
if (fw === "claude-code") {
|
|
7472
7716
|
const { getProjectDir: ccProjectDir } = await import("../claude-scheduler-EM24LTGV.js");
|
|
7473
7717
|
const projDir = ccProjectDir(agent.codeName);
|
|
7474
|
-
const mcpConfigPath =
|
|
7718
|
+
const mcpConfigPath = join7(projDir, ".mcp.json");
|
|
7475
7719
|
const serverNames = [];
|
|
7476
7720
|
if (existsSync5(mcpConfigPath)) {
|
|
7477
7721
|
try {
|
|
7478
|
-
const d = JSON.parse(
|
|
7722
|
+
const d = JSON.parse(readFileSync8(mcpConfigPath, "utf-8"));
|
|
7479
7723
|
if (d.mcpServers) serverNames.push(...Object.keys(d.mcpServers));
|
|
7480
7724
|
} catch {
|
|
7481
7725
|
}
|
|
@@ -7494,15 +7738,15 @@ ${escapeXml(msg.content)}
|
|
|
7494
7738
|
"--allowedTools",
|
|
7495
7739
|
allowedTools
|
|
7496
7740
|
];
|
|
7497
|
-
const chatClaudeMd =
|
|
7741
|
+
const chatClaudeMd = join7(projDir, "CLAUDE.md");
|
|
7498
7742
|
if (existsSync5(chatClaudeMd)) {
|
|
7499
7743
|
chatArgs.push("--system-prompt-file", chatClaudeMd);
|
|
7500
7744
|
}
|
|
7501
|
-
const envIntPath =
|
|
7745
|
+
const envIntPath = join7(projDir, ".env.integrations");
|
|
7502
7746
|
const childEnv = { ...process.env };
|
|
7503
7747
|
if (existsSync5(envIntPath)) {
|
|
7504
7748
|
try {
|
|
7505
|
-
for (const line of
|
|
7749
|
+
for (const line of readFileSync8(envIntPath, "utf-8").split("\n")) {
|
|
7506
7750
|
if (!line || line.startsWith("#") || !line.includes("=")) continue;
|
|
7507
7751
|
const eqIdx = line.indexOf("=");
|
|
7508
7752
|
childEnv[line.slice(0, eqIdx)] = line.slice(eqIdx + 1);
|
|
@@ -7875,12 +8119,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
7875
8119
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
7876
8120
|
try {
|
|
7877
8121
|
const candidates = [
|
|
7878
|
-
|
|
7879
|
-
|
|
8122
|
+
join7(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
8123
|
+
join7(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
7880
8124
|
];
|
|
7881
8125
|
for (const candidate of candidates) {
|
|
7882
8126
|
if (existsSync5(candidate)) {
|
|
7883
|
-
const content =
|
|
8127
|
+
const content = readFileSync8(candidate, "utf-8");
|
|
7884
8128
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
7885
8129
|
builtInSkillCache.set(skillId, files);
|
|
7886
8130
|
return files;
|
|
@@ -8523,7 +8767,7 @@ async function processClaudePairSessions(agents) {
|
|
|
8523
8767
|
killPairSession,
|
|
8524
8768
|
pairTmuxSession,
|
|
8525
8769
|
finalizeClaudePairOnboarding
|
|
8526
|
-
} = await import("../claude-pair-runtime-
|
|
8770
|
+
} = await import("../claude-pair-runtime-OOVDC4YK.js");
|
|
8527
8771
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
8528
8772
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
8529
8773
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -8833,8 +9077,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
8833
9077
|
};
|
|
8834
9078
|
}
|
|
8835
9079
|
async function syncMemories(agent, configDir, log2) {
|
|
8836
|
-
const projectDir =
|
|
8837
|
-
const memoryDir =
|
|
9080
|
+
const projectDir = join7(configDir, agent.code_name, "project");
|
|
9081
|
+
const memoryDir = join7(projectDir, "memory");
|
|
8838
9082
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
8839
9083
|
if (isFreshSync) {
|
|
8840
9084
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -8852,7 +9096,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
8852
9096
|
for (const file of readdirSync3(memoryDir)) {
|
|
8853
9097
|
if (!file.endsWith(".md")) continue;
|
|
8854
9098
|
try {
|
|
8855
|
-
const raw =
|
|
9099
|
+
const raw = readFileSync8(join7(memoryDir, file), "utf-8");
|
|
8856
9100
|
const fileHash = createHash3("sha256").update(raw).digest("hex").slice(0, 16);
|
|
8857
9101
|
currentHashes.set(file, fileHash);
|
|
8858
9102
|
if (prevHashes.get(file) === fileHash) continue;
|
|
@@ -8877,7 +9121,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
8877
9121
|
} catch (err) {
|
|
8878
9122
|
for (const mem of changedMemories) {
|
|
8879
9123
|
for (const [file] of currentHashes) {
|
|
8880
|
-
const parsed = parseMemoryFile(
|
|
9124
|
+
const parsed = parseMemoryFile(readFileSync8(join7(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
8881
9125
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
8882
9126
|
}
|
|
8883
9127
|
}
|
|
@@ -8912,7 +9156,7 @@ async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
|
8912
9156
|
const mem = dbMemories.memories[i];
|
|
8913
9157
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
8914
9158
|
const slug = rawSlug || `memory-${i}`;
|
|
8915
|
-
const filePath =
|
|
9159
|
+
const filePath = join7(memoryDir, `${slug}.md`);
|
|
8916
9160
|
const desired = `---
|
|
8917
9161
|
name: ${JSON.stringify(mem.name)}
|
|
8918
9162
|
type: ${mem.type}
|
|
@@ -8924,7 +9168,7 @@ ${mem.content}
|
|
|
8924
9168
|
if (existsSync5(filePath)) {
|
|
8925
9169
|
let existing = "";
|
|
8926
9170
|
try {
|
|
8927
|
-
existing =
|
|
9171
|
+
existing = readFileSync8(filePath, "utf-8");
|
|
8928
9172
|
} catch {
|
|
8929
9173
|
}
|
|
8930
9174
|
if (existing === desired) continue;
|
|
@@ -9138,7 +9382,7 @@ function startManager(opts) {
|
|
|
9138
9382
|
try {
|
|
9139
9383
|
const stateFile = getStateFile();
|
|
9140
9384
|
if (existsSync5(stateFile)) {
|
|
9141
|
-
const raw =
|
|
9385
|
+
const raw = readFileSync8(stateFile, "utf-8");
|
|
9142
9386
|
const parsed = JSON.parse(raw);
|
|
9143
9387
|
if (Array.isArray(parsed.agents)) {
|
|
9144
9388
|
state4.agents = parsed.agents;
|
|
@@ -9154,7 +9398,7 @@ function startManager(opts) {
|
|
|
9154
9398
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
9155
9399
|
}
|
|
9156
9400
|
log(
|
|
9157
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
9401
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join7(homedir4(), ".augmented", "manager.log")}`
|
|
9158
9402
|
);
|
|
9159
9403
|
deployMcpAssets();
|
|
9160
9404
|
reapOrphanChannelMcps({ log });
|
|
@@ -9175,7 +9419,7 @@ async function reapOrphanedClaudePids() {
|
|
|
9175
9419
|
const looksLikeClaude = (pid) => {
|
|
9176
9420
|
if (process.platform !== "linux") return true;
|
|
9177
9421
|
try {
|
|
9178
|
-
const comm =
|
|
9422
|
+
const comm = readFileSync8(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
9179
9423
|
return comm.includes("claude");
|
|
9180
9424
|
} catch {
|
|
9181
9425
|
return false;
|
|
@@ -9285,14 +9529,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
9285
9529
|
}
|
|
9286
9530
|
}
|
|
9287
9531
|
function deployMcpAssets() {
|
|
9288
|
-
const targetDir =
|
|
9532
|
+
const targetDir = join7(homedir4(), ".augmented", "_mcp");
|
|
9289
9533
|
mkdirSync4(targetDir, { recursive: true });
|
|
9290
9534
|
const moduleDir = dirname3(fileURLToPath(import.meta.url));
|
|
9291
9535
|
let mcpSourceDir = "";
|
|
9292
9536
|
let dir = moduleDir;
|
|
9293
9537
|
for (let i = 0; i < 6; i++) {
|
|
9294
|
-
const candidate =
|
|
9295
|
-
if (existsSync5(
|
|
9538
|
+
const candidate = join7(dir, "dist", "mcp");
|
|
9539
|
+
if (existsSync5(join7(candidate, "index.js"))) {
|
|
9296
9540
|
mcpSourceDir = candidate;
|
|
9297
9541
|
break;
|
|
9298
9542
|
}
|
|
@@ -9308,7 +9552,7 @@ function deployMcpAssets() {
|
|
|
9308
9552
|
const fileHash = (p) => {
|
|
9309
9553
|
try {
|
|
9310
9554
|
if (!existsSync5(p)) return null;
|
|
9311
|
-
return createHash3("sha256").update(
|
|
9555
|
+
return createHash3("sha256").update(readFileSync8(p)).digest("hex");
|
|
9312
9556
|
} catch {
|
|
9313
9557
|
return null;
|
|
9314
9558
|
}
|
|
@@ -9319,8 +9563,8 @@ function deployMcpAssets() {
|
|
|
9319
9563
|
"telegram-channel.js"
|
|
9320
9564
|
]);
|
|
9321
9565
|
for (const file of ["index.js", "slack-channel.js", "direct-chat-channel.js", "telegram-channel.js"]) {
|
|
9322
|
-
const src =
|
|
9323
|
-
const dst =
|
|
9566
|
+
const src = join7(mcpSourceDir, file);
|
|
9567
|
+
const dst = join7(targetDir, file);
|
|
9324
9568
|
if (!existsSync5(src)) continue;
|
|
9325
9569
|
const before = fileHash(dst);
|
|
9326
9570
|
try {
|
|
@@ -9338,16 +9582,16 @@ function deployMcpAssets() {
|
|
|
9338
9582
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
9339
9583
|
restartRunningChannelMcps(changedBasenames);
|
|
9340
9584
|
}
|
|
9341
|
-
const localMcpPath =
|
|
9585
|
+
const localMcpPath = join7(targetDir, "index.js");
|
|
9342
9586
|
try {
|
|
9343
|
-
const agentsDir =
|
|
9587
|
+
const agentsDir = join7(homedir4(), ".augmented", "agents");
|
|
9344
9588
|
if (existsSync5(agentsDir)) {
|
|
9345
9589
|
for (const entry of readdirSync3(agentsDir, { withFileTypes: true })) {
|
|
9346
9590
|
if (!entry.isDirectory()) continue;
|
|
9347
9591
|
for (const subdir of ["provision", "project"]) {
|
|
9348
|
-
const mcpJsonPath =
|
|
9592
|
+
const mcpJsonPath = join7(agentsDir, entry.name, subdir, ".mcp.json");
|
|
9349
9593
|
try {
|
|
9350
|
-
const raw =
|
|
9594
|
+
const raw = readFileSync8(mcpJsonPath, "utf-8");
|
|
9351
9595
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
9352
9596
|
const mcpConfig = JSON.parse(raw);
|
|
9353
9597
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|