@integrity-labs/agt-cli 0.19.15 → 0.19.17
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.
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveChannels,
|
|
23
23
|
resolveDmTarget,
|
|
24
24
|
wrapScheduledTaskPrompt
|
|
25
|
-
} from "../chunk-
|
|
25
|
+
} from "../chunk-AYSU35A4.js";
|
|
26
26
|
import {
|
|
27
27
|
findTaskByTemplate,
|
|
28
28
|
getProjectDir,
|
|
@@ -60,10 +60,18 @@ import { join as join4, dirname } from "path";
|
|
|
60
60
|
import { homedir as homedir3 } from "os";
|
|
61
61
|
import { fileURLToPath } from "url";
|
|
62
62
|
|
|
63
|
+
// src/lib/mcp-config-drift.ts
|
|
64
|
+
function decideMcpDriftAction(currentHash, knownHash) {
|
|
65
|
+
if (!currentHash) return { kind: "no-config" };
|
|
66
|
+
if (!knownHash) return { kind: "baseline", hash: currentHash };
|
|
67
|
+
if (knownHash === currentHash) return { kind: "no-drift" };
|
|
68
|
+
return { kind: "drift", previous: knownHash, current: currentHash };
|
|
69
|
+
}
|
|
70
|
+
|
|
63
71
|
// src/lib/stale-mcp-reaper.ts
|
|
64
72
|
import { execFileSync } from "child_process";
|
|
65
|
-
function
|
|
66
|
-
const
|
|
73
|
+
function parseEnvIntegrationsEntries(content) {
|
|
74
|
+
const entries = /* @__PURE__ */ new Map();
|
|
67
75
|
for (const raw of content.split(/\r?\n/)) {
|
|
68
76
|
const line = raw.trim();
|
|
69
77
|
if (!line || line.startsWith("#")) continue;
|
|
@@ -71,9 +79,22 @@ function parseEnvIntegrationsVars(content) {
|
|
|
71
79
|
if (eq <= 0) continue;
|
|
72
80
|
const name = line.slice(0, eq).trim();
|
|
73
81
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) continue;
|
|
74
|
-
|
|
82
|
+
const value = line.slice(eq + 1);
|
|
83
|
+
entries.set(name, value);
|
|
84
|
+
}
|
|
85
|
+
return entries;
|
|
86
|
+
}
|
|
87
|
+
function diffEnvIntegrations(oldContent, newContent) {
|
|
88
|
+
const oldEntries = oldContent === void 0 ? /* @__PURE__ */ new Map() : parseEnvIntegrationsEntries(oldContent);
|
|
89
|
+
const newEntries = parseEnvIntegrationsEntries(newContent);
|
|
90
|
+
const changed = /* @__PURE__ */ new Set();
|
|
91
|
+
for (const [name, value] of newEntries) {
|
|
92
|
+
if (oldEntries.get(name) !== value) changed.add(name);
|
|
93
|
+
}
|
|
94
|
+
for (const name of oldEntries.keys()) {
|
|
95
|
+
if (!newEntries.has(name)) changed.add(name);
|
|
75
96
|
}
|
|
76
|
-
return [...
|
|
97
|
+
return [...changed];
|
|
77
98
|
}
|
|
78
99
|
function findMcpServersUsingVars(mcp, changedVars) {
|
|
79
100
|
const changedSet = new Set(changedVars);
|
|
@@ -102,14 +123,48 @@ function findMcpServersUsingVars(mcp, changedVars) {
|
|
|
102
123
|
}
|
|
103
124
|
return result;
|
|
104
125
|
}
|
|
105
|
-
function
|
|
126
|
+
function buildArgvMatchersForEntry(key, entry) {
|
|
127
|
+
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
106
128
|
const patterns = [];
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
129
|
+
const args = entry?.args ?? [];
|
|
130
|
+
for (let i = args.length - 1; i >= 0; i--) {
|
|
131
|
+
const arg = args[i];
|
|
132
|
+
if (typeof arg !== "string" || !arg) continue;
|
|
133
|
+
if (arg.startsWith("-")) continue;
|
|
134
|
+
const stripped = arg.replace(
|
|
135
|
+
/@[^/@]*$/,
|
|
136
|
+
(m) => (
|
|
137
|
+
// @latest, @1.2.3, etc. — drop. But @scope at the START of a
|
|
138
|
+
// package spec must NOT be stripped, so we only strip a tail
|
|
139
|
+
// `@...` if it doesn't itself start with a slash inside.
|
|
140
|
+
// The regex above only matches a single trailing `@...`
|
|
141
|
+
// segment after the last `/`, so this is safe for `@scope/pkg`.
|
|
142
|
+
m.includes("/") ? m : ""
|
|
143
|
+
)
|
|
144
|
+
);
|
|
145
|
+
const tail = "(?![A-Za-z0-9_-])";
|
|
146
|
+
if (/^@?[a-z0-9]([a-z0-9._-]*\/)?[a-z0-9._-]+$/i.test(stripped)) {
|
|
147
|
+
patterns.push(new RegExp(`${escapeRe(stripped)}${tail}`));
|
|
148
|
+
const basename = stripped.split("/").pop();
|
|
149
|
+
if (basename && basename !== stripped) {
|
|
150
|
+
patterns.push(new RegExp(`${escapeRe(basename)}${tail}`));
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
if (stripped.includes("/")) {
|
|
155
|
+
const basename = stripped.split("/").pop();
|
|
156
|
+
if (basename) {
|
|
157
|
+
patterns.push(new RegExp(`${escapeRe(basename)}${tail}`));
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (patterns.length === 0) {
|
|
163
|
+
const safe = escapeRe(key);
|
|
164
|
+
patterns.push(new RegExp(`(?<![A-Za-z0-9_])${safe}(?![A-Za-z0-9_])`));
|
|
110
165
|
if (safe.includes("_")) {
|
|
111
166
|
const dashed = safe.replace(/_/g, "-");
|
|
112
|
-
patterns.push(new RegExp(
|
|
167
|
+
patterns.push(new RegExp(`(?<![A-Za-z0-9_])${dashed}(?![A-Za-z0-9_])`));
|
|
113
168
|
}
|
|
114
169
|
}
|
|
115
170
|
return patterns;
|
|
@@ -119,11 +174,15 @@ function buildClaudeAgentMatcher(codeName) {
|
|
|
119
174
|
return new RegExp(`\\bclaude\\b.*--name\\s+agt-${safe}(?=\\s|$)`);
|
|
120
175
|
}
|
|
121
176
|
function findMcpChildrenForAgent(args) {
|
|
122
|
-
const { rows, codeName, serverKeys } = args;
|
|
177
|
+
const { rows, codeName, serverKeys, mcpJson } = args;
|
|
123
178
|
const maxDepth = args.maxDepth ?? 8;
|
|
124
179
|
if (serverKeys.length === 0 || rows.length === 0) return [];
|
|
125
180
|
const claudeMatcher = buildClaudeAgentMatcher(codeName);
|
|
126
|
-
const argvMatchers =
|
|
181
|
+
const argvMatchers = [];
|
|
182
|
+
for (const key of serverKeys) {
|
|
183
|
+
const entry = mcpJson?.mcpServers?.[key];
|
|
184
|
+
argvMatchers.push(...buildArgvMatchersForEntry(key, entry));
|
|
185
|
+
}
|
|
127
186
|
const byPid = new Map(rows.map((r) => [r.pid, r]));
|
|
128
187
|
const matched = [];
|
|
129
188
|
for (const row of rows) {
|
|
@@ -144,7 +203,7 @@ function findMcpChildrenForAgent(args) {
|
|
|
144
203
|
return matched;
|
|
145
204
|
}
|
|
146
205
|
function reapStaleMcpChildren(args) {
|
|
147
|
-
const { log: log2, codeName, serverKeys, graceMs = 5e3 } = args;
|
|
206
|
+
const { log: log2, codeName, serverKeys, mcpJson, graceMs = 5e3 } = args;
|
|
148
207
|
if (serverKeys.length === 0) return [];
|
|
149
208
|
const runPs = args.runPs ?? (() => execFileSync("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", timeout: 5e3 }));
|
|
150
209
|
const killProcess = args.killProcess ?? ((pid, signal) => {
|
|
@@ -169,7 +228,7 @@ function reapStaleMcpChildren(args) {
|
|
|
169
228
|
return [];
|
|
170
229
|
}
|
|
171
230
|
const rows = parsePsRows(psOutput);
|
|
172
|
-
const targets = findMcpChildrenForAgent({ rows, codeName, serverKeys });
|
|
231
|
+
const targets = findMcpChildrenForAgent({ rows, codeName, serverKeys, mcpJson });
|
|
173
232
|
if (targets.length === 0) return [];
|
|
174
233
|
const byPid = new Map(rows.map((r) => [r.pid, r]));
|
|
175
234
|
const describe = (pid) => {
|
|
@@ -196,7 +255,8 @@ function reapStaleMcpChildren(args) {
|
|
|
196
255
|
findMcpChildrenForAgent({
|
|
197
256
|
rows: parsePsRows(freshPsOutput),
|
|
198
257
|
codeName,
|
|
199
|
-
serverKeys
|
|
258
|
+
serverKeys,
|
|
259
|
+
mcpJson
|
|
200
260
|
})
|
|
201
261
|
);
|
|
202
262
|
const stragglers = targets.filter((pid) => isAlive(pid) && stillOwned.has(pid));
|
|
@@ -1748,6 +1808,7 @@ function scheduleSessionRestart(codeName, delayMs, reason) {
|
|
|
1748
1808
|
const timer = setTimeout(() => {
|
|
1749
1809
|
pendingSessionRestarts.delete(codeName);
|
|
1750
1810
|
stopPersistentSession(codeName, log);
|
|
1811
|
+
runningMcpHashes.delete(codeName);
|
|
1751
1812
|
log(`[hot-reload] Session stopped for '${codeName}' \u2014 will respawn with ${reason}`);
|
|
1752
1813
|
}, delayMs);
|
|
1753
1814
|
timer.unref?.();
|
|
@@ -1762,6 +1823,38 @@ function cancelPendingSessionRestart(codeName) {
|
|
|
1762
1823
|
}
|
|
1763
1824
|
var writtenHashes = /* @__PURE__ */ new Map();
|
|
1764
1825
|
var knownSecretsHashes = /* @__PURE__ */ new Map();
|
|
1826
|
+
var runningMcpHashes = /* @__PURE__ */ new Map();
|
|
1827
|
+
function projectMcpHash(codeName, projectDir) {
|
|
1828
|
+
try {
|
|
1829
|
+
return createHash("sha256").update(readFileSync3(join4(projectDir, ".mcp.json"))).digest("hex");
|
|
1830
|
+
} catch {
|
|
1831
|
+
return null;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
function stopPersistentSessionAndForgetMcpBaseline(codeName) {
|
|
1835
|
+
cancelPendingSessionRestart(codeName);
|
|
1836
|
+
stopPersistentSession(codeName, log);
|
|
1837
|
+
runningMcpHashes.delete(codeName);
|
|
1838
|
+
}
|
|
1839
|
+
function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
1840
|
+
const currentHash = projectMcpHash(codeName, projectDir);
|
|
1841
|
+
const action = decideMcpDriftAction(currentHash, runningMcpHashes.get(codeName));
|
|
1842
|
+
switch (action.kind) {
|
|
1843
|
+
case "no-config":
|
|
1844
|
+
case "no-drift":
|
|
1845
|
+
return;
|
|
1846
|
+
case "baseline":
|
|
1847
|
+
runningMcpHashes.set(codeName, action.hash);
|
|
1848
|
+
return;
|
|
1849
|
+
case "drift":
|
|
1850
|
+
log(
|
|
1851
|
+
`[hot-reload] .mcp.json content changed for '${codeName}' (${action.previous.slice(0, 12)} \u2192 ${action.current.slice(0, 12)}); scheduling restart (ENG-4897)`
|
|
1852
|
+
);
|
|
1853
|
+
scheduleSessionRestart(codeName, 0, ".mcp.json content change (ENG-4897)");
|
|
1854
|
+
runningMcpHashes.delete(codeName);
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1765
1858
|
var knownChannelConfigHashes = /* @__PURE__ */ new Map();
|
|
1766
1859
|
var knownModels = /* @__PURE__ */ new Map();
|
|
1767
1860
|
var knownTasksHashes = /* @__PURE__ */ new Map();
|
|
@@ -1836,7 +1929,7 @@ function clearAgentCaches(agentId, codeName) {
|
|
|
1836
1929
|
var cachedFrameworkVersion = null;
|
|
1837
1930
|
var lastVersionCheckAt = 0;
|
|
1838
1931
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
1839
|
-
var agtCliVersion = true ? "0.19.
|
|
1932
|
+
var agtCliVersion = true ? "0.19.17" : "dev";
|
|
1840
1933
|
function resolveBrewPath(execFileSync3) {
|
|
1841
1934
|
try {
|
|
1842
1935
|
const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -2679,8 +2772,7 @@ async function pollCycle() {
|
|
|
2679
2772
|
log,
|
|
2680
2773
|
resolveFramework: (codeName) => agentFrameworkCache.get(codeName) ?? null,
|
|
2681
2774
|
stopSession: (codeName) => {
|
|
2682
|
-
|
|
2683
|
-
stopPersistentSession(codeName, log);
|
|
2775
|
+
stopPersistentSessionAndForgetMcpBaseline(codeName);
|
|
2684
2776
|
persistentSessionAgents.delete(codeName);
|
|
2685
2777
|
claudeAuthTupleBySession.delete(codeName);
|
|
2686
2778
|
},
|
|
@@ -2840,8 +2932,7 @@ async function pollCycle() {
|
|
|
2840
2932
|
log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
|
|
2841
2933
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
2842
2934
|
await stopGatewayIfRunning(prev.codeName, adapter);
|
|
2843
|
-
|
|
2844
|
-
stopPersistentSession(prev.codeName, log);
|
|
2935
|
+
stopPersistentSessionAndForgetMcpBaseline(prev.codeName);
|
|
2845
2936
|
try {
|
|
2846
2937
|
const { execSync: es } = await import("child_process");
|
|
2847
2938
|
es(`tmux kill-session -t agt-${prev.codeName} 2>/dev/null`, { stdio: "ignore" });
|
|
@@ -2978,8 +3069,7 @@ async function processAgent(agent, agentStates) {
|
|
|
2978
3069
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
2979
3070
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
2980
3071
|
await stopGatewayIfRunning(agent.code_name, adapter);
|
|
2981
|
-
|
|
2982
|
-
stopPersistentSession(agent.code_name, log);
|
|
3072
|
+
stopPersistentSessionAndForgetMcpBaseline(agent.code_name);
|
|
2983
3073
|
try {
|
|
2984
3074
|
const { execSync: es } = await import("child_process");
|
|
2985
3075
|
es(`tmux kill-session -t agt-${agent.code_name} 2>/dev/null`, { stdio: "ignore" });
|
|
@@ -3007,8 +3097,7 @@ async function processAgent(agent, agentStates) {
|
|
|
3007
3097
|
if (agent.status === "revoked") {
|
|
3008
3098
|
log(`Agent '${agent.code_name}' is revoked, cleaning up`);
|
|
3009
3099
|
await stopGatewayIfRunning(agent.code_name, adapter);
|
|
3010
|
-
|
|
3011
|
-
stopPersistentSession(agent.code_name, log);
|
|
3100
|
+
stopPersistentSessionAndForgetMcpBaseline(agent.code_name);
|
|
3012
3101
|
try {
|
|
3013
3102
|
const { execSync: es } = await import("child_process");
|
|
3014
3103
|
es(`tmux kill-session -t agt-${agent.code_name} 2>/dev/null`, { stdio: "ignore" });
|
|
@@ -3481,40 +3570,50 @@ async function processAgent(agent, agentStates) {
|
|
|
3481
3570
|
const intHash = createHash("sha256").update(JSON.stringify(integrations.map((i) => `${i.definition_id}:${JSON.stringify(i.credentials)}`))).digest("hex").slice(0, 16);
|
|
3482
3571
|
const prevIntHash = knownIntegrationHashes.get(agent.agent_id);
|
|
3483
3572
|
if (intHash !== prevIntHash) {
|
|
3573
|
+
const projectDir = join4(homedir3(), ".augmented", agent.code_name, "project");
|
|
3574
|
+
const envIntPath = join4(projectDir, ".env.integrations");
|
|
3575
|
+
let preWriteEnv;
|
|
3576
|
+
try {
|
|
3577
|
+
preWriteEnv = readFileSync3(envIntPath, "utf-8");
|
|
3578
|
+
} catch {
|
|
3579
|
+
preWriteEnv = void 0;
|
|
3580
|
+
}
|
|
3484
3581
|
if (frameworkAdapter.writeIntegrations) {
|
|
3485
3582
|
frameworkAdapter.writeIntegrations(agent.code_name, integrations);
|
|
3486
3583
|
}
|
|
3487
|
-
knownIntegrationHashes.set(agent.agent_id, intHash);
|
|
3488
3584
|
log(`Integrations provisioned for '${agent.code_name}' (${integrations.length} integration(s))`);
|
|
3489
3585
|
const fw = agentFrameworkCache.get(agent.code_name) ?? "openclaw";
|
|
3586
|
+
let rotationHandled = true;
|
|
3490
3587
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
3491
|
-
let affectedServerKeys = [];
|
|
3492
3588
|
try {
|
|
3493
|
-
const projectDir = join4(homedir3(), ".augmented", agent.code_name, "project");
|
|
3494
|
-
const envIntPath = join4(projectDir, ".env.integrations");
|
|
3495
3589
|
const projectMcpPath = join4(projectDir, ".mcp.json");
|
|
3496
|
-
const
|
|
3590
|
+
const postWriteEnv = readFileSync3(envIntPath, "utf-8");
|
|
3497
3591
|
const mcpContent = readFileSync3(projectMcpPath, "utf-8");
|
|
3498
|
-
const changedVars =
|
|
3499
|
-
const
|
|
3500
|
-
affectedServerKeys = findMcpServersUsingVars(
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3592
|
+
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
3593
|
+
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
3594
|
+
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
3595
|
+
if (affectedServerKeys.length > 0) {
|
|
3596
|
+
reapStaleMcpChildren({
|
|
3597
|
+
log,
|
|
3598
|
+
codeName: agent.code_name,
|
|
3599
|
+
serverKeys: affectedServerKeys,
|
|
3600
|
+
mcpJson: mcpJsonForReap
|
|
3601
|
+
});
|
|
3602
|
+
}
|
|
3603
|
+
const names = integrations.map((i) => i.display_name || i.definition_id).join(", ");
|
|
3604
|
+
const reapNote = affectedServerKeys.length > 0 ? ` The MCP servers that depend on rotating credentials (${affectedServerKeys.join(", ")}) have been signalled to reconnect.` : "";
|
|
3605
|
+
injectMessage(agent.code_name, "system", `Your integrations have been refreshed. You have access to: ${names}.${reapNote}`, {
|
|
3606
|
+
task_name: "integration-update"
|
|
3607
|
+
}, log).catch(() => {
|
|
3509
3608
|
});
|
|
3609
|
+
log(`[hot-reload] Notified '${agent.code_name}' about integration update: ${names} (reaped ${affectedServerKeys.length} stale MCP server(s))`);
|
|
3610
|
+
} catch (err) {
|
|
3611
|
+
rotationHandled = false;
|
|
3612
|
+
log(`[hot-reload] Failed to compute / reap affected MCP servers for '${agent.code_name}': ${err.message} \u2014 will retry next tick`);
|
|
3510
3613
|
}
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
task_name: "integration-update"
|
|
3515
|
-
}, log).catch(() => {
|
|
3516
|
-
});
|
|
3517
|
-
log(`[hot-reload] Notified '${agent.code_name}' about integration update: ${names} (reaped ${affectedServerKeys.length} stale MCP server(s))`);
|
|
3614
|
+
}
|
|
3615
|
+
if (rotationHandled) {
|
|
3616
|
+
knownIntegrationHashes.set(agent.agent_id, intHash);
|
|
3518
3617
|
}
|
|
3519
3618
|
needsGatewayRestart = true;
|
|
3520
3619
|
}
|
|
@@ -3832,6 +3931,11 @@ async function processAgent(agent, agentStates) {
|
|
|
3832
3931
|
const sessionMode = refreshData.agent.session_mode ?? "oneshot";
|
|
3833
3932
|
if (agentFw === "claude-code" && sessionMode === "persistent") {
|
|
3834
3933
|
await ensurePersistentSession(agent, tasks, boardItems, refreshData);
|
|
3934
|
+
try {
|
|
3935
|
+
checkMcpConfigDriftAndScheduleRestart(agent.code_name, getProjectDir2(agent.code_name));
|
|
3936
|
+
} catch (err) {
|
|
3937
|
+
log(`[hot-reload] .mcp.json drift check failed for '${agent.code_name}': ${err.message}`);
|
|
3938
|
+
}
|
|
3835
3939
|
} else if (agentFw === "claude-code" && tasks.length > 0) {
|
|
3836
3940
|
await syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData);
|
|
3837
3941
|
} else if (frameworkAdapter.syncScheduledTasks && gatewayRunning && gatewayPort) {
|
|
@@ -4583,8 +4687,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
4583
4687
|
const msg = err.message;
|
|
4584
4688
|
log(`[persistent-session] Failed to resolve auth for '${codeName}': ${msg} \u2014 refusing to spawn`);
|
|
4585
4689
|
if (isSessionHealthy(codeName)) {
|
|
4586
|
-
|
|
4587
|
-
stopPersistentSession(codeName, log);
|
|
4690
|
+
stopPersistentSessionAndForgetMcpBaseline(codeName);
|
|
4588
4691
|
persistentSessionAgents.delete(codeName);
|
|
4589
4692
|
claudeAuthTupleBySession.delete(codeName);
|
|
4590
4693
|
}
|
|
@@ -4606,8 +4709,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
4606
4709
|
const recordedAuthTuple = claudeAuthTupleBySession.get(codeName);
|
|
4607
4710
|
if (recordedAuthTuple && recordedAuthTuple !== currentAuthTuple && isSessionHealthy(codeName)) {
|
|
4608
4711
|
log(`[persistent-session] Auth config changed for '${codeName}' (${recordedAuthTuple} \u2192 ${currentAuthTuple}) \u2014 restarting session`);
|
|
4609
|
-
|
|
4610
|
-
stopPersistentSession(codeName, log);
|
|
4712
|
+
stopPersistentSessionAndForgetMcpBaseline(codeName);
|
|
4611
4713
|
persistentSessionAgents.delete(codeName);
|
|
4612
4714
|
}
|
|
4613
4715
|
if (isStaleForToday(codeName) && isSessionHealthy(codeName)) {
|
|
@@ -4618,8 +4720,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
4618
4720
|
log(
|
|
4619
4721
|
`[persistent-session] Day rollover for '${codeName}' (yesterday=${current.date}) \u2014 agent idle, restarting to mint fresh session`
|
|
4620
4722
|
);
|
|
4621
|
-
|
|
4622
|
-
stopPersistentSession(codeName, log);
|
|
4723
|
+
stopPersistentSessionAndForgetMcpBaseline(codeName);
|
|
4623
4724
|
persistentSessionAgents.delete(codeName);
|
|
4624
4725
|
} else {
|
|
4625
4726
|
log(
|