@integrity-labs/agt-cli 0.28.208 → 0.28.209
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 +230 -146
- package/dist/bin/agt.js.map +1 -1
- package/dist/{chunk-ELBYWACS.js → chunk-WFCCBTA6.js} +8463 -6842
- package/dist/chunk-WFCCBTA6.js.map +1 -0
- package/dist/{chunk-3MHPMBN4.js → chunk-Z62QHIR3.js} +792 -38
- package/dist/chunk-Z62QHIR3.js.map +1 -0
- package/dist/{claude-pair-runtime-Q4Q2OTTV.js → claude-pair-runtime-UUTPSE2V.js} +2 -2
- package/dist/lib/manager-worker.js +84 -796
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/{persistent-session-6662WBSH.js → persistent-session-Z46SCCQM.js} +2 -3
- package/dist/{responsiveness-probe-TOLTTG6I.js → responsiveness-probe-4MUX7FBH.js} +2 -3
- package/dist/{responsiveness-probe-TOLTTG6I.js.map → responsiveness-probe-4MUX7FBH.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-3MHPMBN4.js.map +0 -1
- package/dist/chunk-ELBYWACS.js.map +0 -1
- package/dist/chunk-GWBOA7ZY.js +0 -1634
- package/dist/chunk-GWBOA7ZY.js.map +0 -1
- /package/dist/{claude-pair-runtime-Q4Q2OTTV.js.map → claude-pair-runtime-UUTPSE2V.js.map} +0 -0
- /package/dist/{persistent-session-6662WBSH.js.map → persistent-session-Z46SCCQM.js.map} +0 -0
|
@@ -9,15 +9,21 @@ import {
|
|
|
9
9
|
atomicWriteFileSync,
|
|
10
10
|
buildConnectivityProbeDeps,
|
|
11
11
|
buildProbeEnv,
|
|
12
|
+
clearPresenceReaperState,
|
|
13
|
+
clearPresenceReaperStateForKeys,
|
|
12
14
|
defaultFlagsCachePath,
|
|
13
15
|
deriveMcpServerKey,
|
|
16
|
+
diffEnvIntegrations,
|
|
14
17
|
estimateActiveTasksTokens,
|
|
15
18
|
exchangeApiKey,
|
|
16
19
|
executeConnectivityProbe,
|
|
17
20
|
extractCommandNotFound,
|
|
21
|
+
findMcpServersUsingVars,
|
|
22
|
+
gatherSessionToolBindProbe,
|
|
18
23
|
getApiKey,
|
|
19
24
|
getCachedClaudeAuthMode,
|
|
20
25
|
getHostId,
|
|
26
|
+
givenUpMcpServerKeys,
|
|
21
27
|
provision,
|
|
22
28
|
provisionAutoKanbanProgressHook,
|
|
23
29
|
provisionChannelProgressHook,
|
|
@@ -25,10 +31,12 @@ import {
|
|
|
25
31
|
provisionOrientHook,
|
|
26
32
|
provisionSessionStateHook,
|
|
27
33
|
provisionStopHook,
|
|
34
|
+
reapMissingMcpSessions,
|
|
35
|
+
reapStaleMcpChildren,
|
|
28
36
|
requireHost,
|
|
29
37
|
safeWriteJsonAtomic,
|
|
30
38
|
setConfigHash
|
|
31
|
-
} from "../chunk-
|
|
39
|
+
} from "../chunk-Z62QHIR3.js";
|
|
32
40
|
import {
|
|
33
41
|
getProjectDir as getProjectDir2,
|
|
34
42
|
getReadyTasks,
|
|
@@ -36,41 +44,6 @@ import {
|
|
|
36
44
|
markTaskFired,
|
|
37
45
|
syncTasksToScheduler
|
|
38
46
|
} from "../chunk-I3YS5WFV.js";
|
|
39
|
-
import {
|
|
40
|
-
buildEgressAllowlist,
|
|
41
|
-
checkChannelInputs,
|
|
42
|
-
creditWatchdogGiveUpCount,
|
|
43
|
-
egressMode,
|
|
44
|
-
getLastFailureContext,
|
|
45
|
-
getProjectDir,
|
|
46
|
-
getSessionState,
|
|
47
|
-
injectMessage,
|
|
48
|
-
injectMessageWithStatus,
|
|
49
|
-
isAgentIdle,
|
|
50
|
-
isAgentPromptReady,
|
|
51
|
-
isSessionHealthy,
|
|
52
|
-
isStaleForToday,
|
|
53
|
-
isolationMode,
|
|
54
|
-
paneLogPath,
|
|
55
|
-
peekCurrentSession,
|
|
56
|
-
prepareForRespawn,
|
|
57
|
-
readPaneLogTail,
|
|
58
|
-
reloadEgressSidecar,
|
|
59
|
-
resetRestartCount,
|
|
60
|
-
resolveClaudeBinary,
|
|
61
|
-
restartEgressSidecar,
|
|
62
|
-
rotateSessionForWedge,
|
|
63
|
-
sendToAgent,
|
|
64
|
-
sessionTranscriptDir,
|
|
65
|
-
startPersistentSession,
|
|
66
|
-
stopAllSessionsAndWait,
|
|
67
|
-
stopPersistentSession,
|
|
68
|
-
subagentActivityAgeSeconds,
|
|
69
|
-
takeWatchdogGiveUpCount,
|
|
70
|
-
takeZombieDetection,
|
|
71
|
-
transcriptActivityAgeSeconds,
|
|
72
|
-
writeEgressAllowlist
|
|
73
|
-
} from "../chunk-GWBOA7ZY.js";
|
|
74
47
|
import {
|
|
75
48
|
CONVERSATION_FAILURE_CATEGORIES,
|
|
76
49
|
DEFAULT_FRAMEWORK,
|
|
@@ -82,17 +55,28 @@ import {
|
|
|
82
55
|
StreamEncoder,
|
|
83
56
|
appendDmFooter,
|
|
84
57
|
attributeTranscriptUsageByRun,
|
|
58
|
+
buildEgressAllowlist,
|
|
85
59
|
buildFailureCategoryPromptLines,
|
|
86
60
|
buildScheduledTaskContextBlocks,
|
|
61
|
+
checkChannelInputs,
|
|
87
62
|
classifyActor,
|
|
88
63
|
classifyOutput,
|
|
89
64
|
coerceOnboardingState,
|
|
65
|
+
creditWatchdogGiveUpCount,
|
|
90
66
|
describeOnboardingChannel,
|
|
67
|
+
egressMode,
|
|
91
68
|
extractFrontmatter,
|
|
92
69
|
formatActorId,
|
|
93
70
|
formatRunMarker,
|
|
94
71
|
getFramework,
|
|
95
72
|
getIntegration,
|
|
73
|
+
getLastFailureContext,
|
|
74
|
+
getProjectDir,
|
|
75
|
+
getSessionState,
|
|
76
|
+
injectMessage,
|
|
77
|
+
injectMessageWithStatus,
|
|
78
|
+
isAgentIdle,
|
|
79
|
+
isAgentPromptReady,
|
|
96
80
|
isBareAck,
|
|
97
81
|
isConversationFailureCategory,
|
|
98
82
|
isEmptyTotals,
|
|
@@ -100,23 +84,44 @@ import {
|
|
|
100
84
|
isParseError,
|
|
101
85
|
isResolveError,
|
|
102
86
|
isSelfCompletion,
|
|
87
|
+
isSessionHealthy,
|
|
88
|
+
isStaleForToday,
|
|
103
89
|
isSystemInjectedContent,
|
|
104
90
|
isUnsetTimezone,
|
|
105
91
|
isVacuousDeliverReason,
|
|
92
|
+
isolationMode,
|
|
106
93
|
laneTagFragment,
|
|
94
|
+
paneLogPath,
|
|
107
95
|
parseDeliverAssertion,
|
|
108
96
|
parseDeliveryTarget,
|
|
109
97
|
parseEnvIntegrations,
|
|
110
98
|
parseTranscriptUsage,
|
|
111
99
|
parseUsageBanner,
|
|
100
|
+
peekCurrentSession,
|
|
101
|
+
prepareForRespawn,
|
|
102
|
+
readPaneLogTail,
|
|
103
|
+
reloadEgressSidecar,
|
|
104
|
+
resetRestartCount,
|
|
112
105
|
resolveAgentTimezone,
|
|
113
106
|
resolveAvatarEnvUrl,
|
|
114
107
|
resolveChannels,
|
|
108
|
+
resolveClaudeBinary,
|
|
115
109
|
resolveDmTarget,
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
restartEgressSidecar,
|
|
111
|
+
rotateSessionForWedge,
|
|
112
|
+
sendToAgent,
|
|
113
|
+
sessionTranscriptDir,
|
|
114
|
+
startPersistentSession,
|
|
115
|
+
stopAllSessionsAndWait,
|
|
116
|
+
stopPersistentSession,
|
|
117
|
+
subagentActivityAgeSeconds,
|
|
118
|
+
sumTranscriptUsageInWindow,
|
|
119
|
+
takeWatchdogGiveUpCount,
|
|
120
|
+
takeZombieDetection,
|
|
121
|
+
transcriptActivityAgeSeconds,
|
|
122
|
+
writeEgressAllowlist
|
|
123
|
+
} from "../chunk-WFCCBTA6.js";
|
|
118
124
|
import {
|
|
119
|
-
parsePsRows,
|
|
120
125
|
reapOrphanChannelMcps
|
|
121
126
|
} from "../chunk-XWVM4KPK.js";
|
|
122
127
|
|
|
@@ -228,561 +233,6 @@ function withResolvedRemoteMcp(integration) {
|
|
|
228
233
|
return { ...integration, remoteMcp: integration.remote_mcp ?? void 0 };
|
|
229
234
|
}
|
|
230
235
|
|
|
231
|
-
// src/lib/stale-mcp-reaper.ts
|
|
232
|
-
import { execFileSync } from "child_process";
|
|
233
|
-
var rotationTimestamps = /* @__PURE__ */ new Map();
|
|
234
|
-
var DEFAULT_ROTATION_GRACE_MS = 15e3;
|
|
235
|
-
function rotationKey(codeName, serverKey) {
|
|
236
|
-
return `${codeName}\0${serverKey}`;
|
|
237
|
-
}
|
|
238
|
-
function recordStaleRotation(codeName, serverKeys, now = Date.now) {
|
|
239
|
-
const ts = now();
|
|
240
|
-
for (const key of serverKeys) {
|
|
241
|
-
rotationTimestamps.set(rotationKey(codeName, key), ts);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
function wasRecentlyRotated(codeName, serverKey, withinMs, now = Date.now) {
|
|
245
|
-
const k = rotationKey(codeName, serverKey);
|
|
246
|
-
const ts = rotationTimestamps.get(k);
|
|
247
|
-
if (ts === void 0) return false;
|
|
248
|
-
if (now() - ts < withinMs) return true;
|
|
249
|
-
rotationTimestamps.delete(k);
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
function parseEnvIntegrationsEntries(content) {
|
|
253
|
-
const entries = /* @__PURE__ */ new Map();
|
|
254
|
-
for (const raw of content.split(/\r?\n/)) {
|
|
255
|
-
const line = raw.trim();
|
|
256
|
-
if (!line || line.startsWith("#")) continue;
|
|
257
|
-
const eq = line.indexOf("=");
|
|
258
|
-
if (eq <= 0) continue;
|
|
259
|
-
const name = line.slice(0, eq).trim();
|
|
260
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) continue;
|
|
261
|
-
const value = line.slice(eq + 1);
|
|
262
|
-
entries.set(name, value);
|
|
263
|
-
}
|
|
264
|
-
return entries;
|
|
265
|
-
}
|
|
266
|
-
function diffEnvIntegrations(oldContent, newContent) {
|
|
267
|
-
const oldEntries = oldContent === void 0 ? /* @__PURE__ */ new Map() : parseEnvIntegrationsEntries(oldContent);
|
|
268
|
-
const newEntries = parseEnvIntegrationsEntries(newContent);
|
|
269
|
-
const changed = /* @__PURE__ */ new Set();
|
|
270
|
-
for (const [name, value] of newEntries) {
|
|
271
|
-
if (oldEntries.get(name) !== value) changed.add(name);
|
|
272
|
-
}
|
|
273
|
-
for (const name of oldEntries.keys()) {
|
|
274
|
-
if (!newEntries.has(name)) changed.add(name);
|
|
275
|
-
}
|
|
276
|
-
return [...changed];
|
|
277
|
-
}
|
|
278
|
-
function findMcpServersUsingVars(mcp, changedVars) {
|
|
279
|
-
const changedSet = new Set(changedVars);
|
|
280
|
-
if (!mcp?.mcpServers || changedSet.size === 0) return [];
|
|
281
|
-
const result = [];
|
|
282
|
-
for (const [serverKey, entry] of Object.entries(mcp.mcpServers)) {
|
|
283
|
-
if (!entry || typeof entry !== "object") continue;
|
|
284
|
-
const env = entry.env;
|
|
285
|
-
if (!env || typeof env !== "object") continue;
|
|
286
|
-
let matches = false;
|
|
287
|
-
for (const value of Object.values(env)) {
|
|
288
|
-
if (typeof value !== "string") continue;
|
|
289
|
-
const placeholderMatches = value.match(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g);
|
|
290
|
-
if (placeholderMatches) {
|
|
291
|
-
for (const ph of placeholderMatches) {
|
|
292
|
-
const name = ph.slice(2, -1);
|
|
293
|
-
if (changedSet.has(name)) {
|
|
294
|
-
matches = true;
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
if (matches) break;
|
|
300
|
-
}
|
|
301
|
-
if (matches) result.push(serverKey);
|
|
302
|
-
}
|
|
303
|
-
return result;
|
|
304
|
-
}
|
|
305
|
-
function buildArgvMatchersForEntry(key, entry) {
|
|
306
|
-
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
307
|
-
const patterns = [];
|
|
308
|
-
const args = entry?.args ?? [];
|
|
309
|
-
for (let i = args.length - 1; i >= 0; i--) {
|
|
310
|
-
const arg = args[i];
|
|
311
|
-
if (typeof arg !== "string" || !arg) continue;
|
|
312
|
-
if (arg.startsWith("-")) continue;
|
|
313
|
-
const stripped = arg.replace(
|
|
314
|
-
/@[^/@]*$/,
|
|
315
|
-
(m) => (
|
|
316
|
-
// @latest, @1.2.3, etc. — drop. But @scope at the START of a
|
|
317
|
-
// package spec must NOT be stripped, so we only strip a tail
|
|
318
|
-
// `@...` if it doesn't itself start with a slash inside.
|
|
319
|
-
// The regex above only matches a single trailing `@...`
|
|
320
|
-
// segment after the last `/`, so this is safe for `@scope/pkg`.
|
|
321
|
-
m.includes("/") ? m : ""
|
|
322
|
-
)
|
|
323
|
-
);
|
|
324
|
-
const tail = "(?![A-Za-z0-9_-])";
|
|
325
|
-
if (/^@?[a-z0-9]([a-z0-9._-]*\/)?[a-z0-9._-]+$/i.test(stripped)) {
|
|
326
|
-
patterns.push(new RegExp(`${escapeRe(stripped)}${tail}`));
|
|
327
|
-
const basename = stripped.split("/").pop();
|
|
328
|
-
if (basename && basename !== stripped) {
|
|
329
|
-
patterns.push(new RegExp(`${escapeRe(basename)}${tail}`));
|
|
330
|
-
}
|
|
331
|
-
break;
|
|
332
|
-
}
|
|
333
|
-
if (stripped.includes("/")) {
|
|
334
|
-
const basename = stripped.split("/").pop();
|
|
335
|
-
if (basename) {
|
|
336
|
-
patterns.push(new RegExp(`${escapeRe(basename)}${tail}`));
|
|
337
|
-
break;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
if (patterns.length === 0) {
|
|
342
|
-
const safe = escapeRe(key);
|
|
343
|
-
patterns.push(new RegExp(`(?<![A-Za-z0-9_])${safe}(?![A-Za-z0-9_])`));
|
|
344
|
-
if (safe.includes("_")) {
|
|
345
|
-
const dashed = safe.replace(/_/g, "-");
|
|
346
|
-
patterns.push(new RegExp(`(?<![A-Za-z0-9_])${dashed}(?![A-Za-z0-9_])`));
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return patterns;
|
|
350
|
-
}
|
|
351
|
-
function buildClaudeAgentMatcher(codeName) {
|
|
352
|
-
const safe = codeName.replace(/[^A-Za-z0-9_-]/g, "");
|
|
353
|
-
return new RegExp(`\\bclaude\\b.*--name\\s+agt-${safe}(?=\\s|$)`);
|
|
354
|
-
}
|
|
355
|
-
function findMcpChildrenForAgent(args) {
|
|
356
|
-
const { rows, codeName, serverKeys, mcpJson } = args;
|
|
357
|
-
const maxDepth = args.maxDepth ?? 8;
|
|
358
|
-
const inContainer = args.inContainer ?? false;
|
|
359
|
-
if (serverKeys.length === 0 || rows.length === 0) return [];
|
|
360
|
-
const claudeMatcher = inContainer ? /\bclaude\b/ : buildClaudeAgentMatcher(codeName);
|
|
361
|
-
const argvMatchers = [];
|
|
362
|
-
for (const key of serverKeys) {
|
|
363
|
-
const entry = mcpJson?.mcpServers?.[key];
|
|
364
|
-
argvMatchers.push(...buildArgvMatchersForEntry(key, entry));
|
|
365
|
-
}
|
|
366
|
-
const byPid = new Map(rows.map((r) => [r.pid, r]));
|
|
367
|
-
const matched = [];
|
|
368
|
-
for (const row of rows) {
|
|
369
|
-
if (!argvMatchers.some((re) => re.test(row.args))) continue;
|
|
370
|
-
if (/\bclaude\b/.test(row.args) && row.args.includes(`--name agt-${codeName}`)) continue;
|
|
371
|
-
let cur = byPid.get(row.ppid);
|
|
372
|
-
let belongs = false;
|
|
373
|
-
for (let depth = 0; depth < maxDepth && cur; depth++) {
|
|
374
|
-
if (claudeMatcher.test(cur.args)) {
|
|
375
|
-
belongs = true;
|
|
376
|
-
break;
|
|
377
|
-
}
|
|
378
|
-
if (cur.pid === 1) break;
|
|
379
|
-
cur = byPid.get(cur.ppid);
|
|
380
|
-
}
|
|
381
|
-
if (belongs) matched.push(row.pid);
|
|
382
|
-
}
|
|
383
|
-
return matched;
|
|
384
|
-
}
|
|
385
|
-
function reapStaleMcpChildren(args) {
|
|
386
|
-
const { log: log2, codeName, serverKeys, mcpJson, graceMs = 5e3, isolated = false } = args;
|
|
387
|
-
if (serverKeys.length === 0) return [];
|
|
388
|
-
const runPs = args.runPs ?? (isolated ? () => execFileSync("docker", ["exec", `agt-${codeName}`, "ps", "-eo", "pid,ppid,args"], {
|
|
389
|
-
encoding: "utf-8",
|
|
390
|
-
timeout: 8e3
|
|
391
|
-
}) : () => execFileSync("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", timeout: 5e3 }));
|
|
392
|
-
const killProcess = args.killProcess ?? (isolated ? (pid, signal) => {
|
|
393
|
-
try {
|
|
394
|
-
execFileSync(
|
|
395
|
-
"docker",
|
|
396
|
-
["exec", `agt-${codeName}`, "kill", signal === "SIGKILL" ? "-KILL" : "-TERM", String(pid)],
|
|
397
|
-
{ timeout: 8e3, stdio: "ignore" }
|
|
398
|
-
);
|
|
399
|
-
} catch {
|
|
400
|
-
}
|
|
401
|
-
} : (pid, signal) => {
|
|
402
|
-
try {
|
|
403
|
-
process.kill(pid, signal);
|
|
404
|
-
} catch {
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
const isAlive = args.isAlive ?? (isolated ? (pid) => {
|
|
408
|
-
try {
|
|
409
|
-
execFileSync("docker", ["exec", `agt-${codeName}`, "kill", "-0", String(pid)], {
|
|
410
|
-
timeout: 8e3,
|
|
411
|
-
stdio: "ignore"
|
|
412
|
-
});
|
|
413
|
-
return true;
|
|
414
|
-
} catch {
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
} : (pid) => {
|
|
418
|
-
try {
|
|
419
|
-
process.kill(pid, 0);
|
|
420
|
-
return true;
|
|
421
|
-
} catch {
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
let psOutput;
|
|
426
|
-
try {
|
|
427
|
-
psOutput = runPs();
|
|
428
|
-
} catch (err) {
|
|
429
|
-
log2(`[stale-mcp-reaper] ps invocation failed for '${codeName}': ${err.message} \u2014 skipping reap`);
|
|
430
|
-
return [];
|
|
431
|
-
}
|
|
432
|
-
const rows = parsePsRows(psOutput);
|
|
433
|
-
const targets = findMcpChildrenForAgent({ rows, codeName, serverKeys, mcpJson, inContainer: isolated });
|
|
434
|
-
if (targets.length === 0) return [];
|
|
435
|
-
const byPid = new Map(rows.map((r) => [r.pid, r]));
|
|
436
|
-
const describe = (pid) => {
|
|
437
|
-
const argv = byPid.get(pid)?.args ?? "";
|
|
438
|
-
const pkgMatch = argv.match(/(@[a-z0-9_-]+\/[a-z0-9_-]+|[a-z0-9_-]+-mcp-server|[a-z0-9_-]+-mcp\b)/i);
|
|
439
|
-
return pkgMatch ? `${pkgMatch[1]} (pid ${pid})` : `pid ${pid}`;
|
|
440
|
-
};
|
|
441
|
-
log2(
|
|
442
|
-
`[stale-mcp-reaper] '${codeName}': rotating ${targets.length} stale MCP child(ren) for [${serverKeys.join(", ")}]: ${targets.map(describe).join(", ")}`
|
|
443
|
-
);
|
|
444
|
-
for (const pid of targets) {
|
|
445
|
-
killProcess(pid, "SIGTERM");
|
|
446
|
-
}
|
|
447
|
-
const killedKeys = /* @__PURE__ */ new Set();
|
|
448
|
-
for (const pid of targets) {
|
|
449
|
-
const argv = byPid.get(pid)?.args ?? "";
|
|
450
|
-
for (const key of serverKeys) {
|
|
451
|
-
const entry = mcpJson?.mcpServers?.[key];
|
|
452
|
-
const matchers = buildArgvMatchersForEntry(key, entry);
|
|
453
|
-
if (matchers.some((re) => re.test(argv))) killedKeys.add(key);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
if (killedKeys.size > 0) recordStaleRotation(codeName, killedKeys);
|
|
457
|
-
setTimeout(() => {
|
|
458
|
-
try {
|
|
459
|
-
let freshPsOutput;
|
|
460
|
-
try {
|
|
461
|
-
freshPsOutput = runPs();
|
|
462
|
-
} catch (err) {
|
|
463
|
-
log2(`[stale-mcp-reaper] '${codeName}': fresh ps for SIGKILL re-verify failed: ${err.message} \u2014 skipping SIGKILL pass`);
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
const stillOwned = new Set(
|
|
467
|
-
findMcpChildrenForAgent({
|
|
468
|
-
rows: parsePsRows(freshPsOutput),
|
|
469
|
-
codeName,
|
|
470
|
-
serverKeys,
|
|
471
|
-
mcpJson,
|
|
472
|
-
inContainer: isolated
|
|
473
|
-
})
|
|
474
|
-
);
|
|
475
|
-
const stragglers = targets.filter((pid) => isAlive(pid) && stillOwned.has(pid));
|
|
476
|
-
if (stragglers.length === 0) return;
|
|
477
|
-
log2(
|
|
478
|
-
`[stale-mcp-reaper] '${codeName}': ${stragglers.length} child(ren) survived SIGTERM; sending SIGKILL: ${stragglers.map(describe).join(", ")}`
|
|
479
|
-
);
|
|
480
|
-
for (const pid of stragglers) {
|
|
481
|
-
killProcess(pid, "SIGKILL");
|
|
482
|
-
}
|
|
483
|
-
} catch (err) {
|
|
484
|
-
log2(`[stale-mcp-reaper] '${codeName}': error in SIGKILL pass: ${err.message}`);
|
|
485
|
-
}
|
|
486
|
-
}, graceMs).unref();
|
|
487
|
-
return targets;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// src/lib/mcp-presence-reaper.ts
|
|
491
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
492
|
-
var DEFAULT_COLD_START_GRACE_MS = 9e4;
|
|
493
|
-
var MAX_PRESENCE_RESTART_ATTEMPTS = 3;
|
|
494
|
-
var BACKOFF_AFTER_RESTARTS = MAX_PRESENCE_RESTART_ATTEMPTS;
|
|
495
|
-
var BACKOFF_BASE_MS = 6e4;
|
|
496
|
-
var BACKOFF_MAX_MS = 30 * 6e4;
|
|
497
|
-
var BACKOFF_WINDOW_MS = 60 * 6e4;
|
|
498
|
-
var BACKOFF_GIVE_UP_RESTARTS = 2 * MAX_PRESENCE_RESTART_ATTEMPTS;
|
|
499
|
-
var BACKOFF_STABLE_RESET_MS = 5 * 6e4;
|
|
500
|
-
var presenceReaperState = /* @__PURE__ */ new Map();
|
|
501
|
-
function stateKey(codeName, serverKey) {
|
|
502
|
-
return `${codeName}\0${serverKey}`;
|
|
503
|
-
}
|
|
504
|
-
function clearPresenceReaperState(codeName) {
|
|
505
|
-
const prefix = `${codeName}\0`;
|
|
506
|
-
for (const key of presenceReaperState.keys()) {
|
|
507
|
-
if (key.startsWith(prefix)) presenceReaperState.delete(key);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
function clearPresenceReaperStateForKeys(codeName, keys) {
|
|
511
|
-
for (const key of keys) {
|
|
512
|
-
presenceReaperState.delete(stateKey(codeName, key));
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
function givenUpMcpServerKeys(codeName) {
|
|
516
|
-
const prefix = `${codeName}\0`;
|
|
517
|
-
const out = /* @__PURE__ */ new Set();
|
|
518
|
-
for (const [key, st] of presenceReaperState) {
|
|
519
|
-
if (key.startsWith(prefix) && (st.gaveUp || st.attempts >= MAX_PRESENCE_RESTART_ATTEMPTS)) {
|
|
520
|
-
out.add(key.slice(prefix.length));
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return out;
|
|
524
|
-
}
|
|
525
|
-
function findMissingMcpServers(args) {
|
|
526
|
-
const { rows, codeName, mcpJson, inContainer } = args;
|
|
527
|
-
const servers = mcpJson?.mcpServers ?? {};
|
|
528
|
-
const declared = Object.keys(servers);
|
|
529
|
-
if (declared.length === 0) return [];
|
|
530
|
-
const missing = [];
|
|
531
|
-
for (const key of declared) {
|
|
532
|
-
const entry = servers[key];
|
|
533
|
-
const isStdio = typeof entry?.command === "string";
|
|
534
|
-
if (!isStdio) continue;
|
|
535
|
-
const live = findMcpChildrenForAgent({
|
|
536
|
-
rows,
|
|
537
|
-
codeName,
|
|
538
|
-
serverKeys: [key],
|
|
539
|
-
mcpJson,
|
|
540
|
-
inContainer
|
|
541
|
-
});
|
|
542
|
-
if (live.length === 0) missing.push(key);
|
|
543
|
-
}
|
|
544
|
-
return missing;
|
|
545
|
-
}
|
|
546
|
-
function reapMissingMcpSessions(args) {
|
|
547
|
-
const {
|
|
548
|
-
log: log2,
|
|
549
|
-
codeName,
|
|
550
|
-
mcpJson,
|
|
551
|
-
sessionStartedAt,
|
|
552
|
-
restartPending = false,
|
|
553
|
-
stopSession,
|
|
554
|
-
graceMs = DEFAULT_COLD_START_GRACE_MS,
|
|
555
|
-
rotationGraceMs = DEFAULT_ROTATION_GRACE_MS,
|
|
556
|
-
onGiveUp,
|
|
557
|
-
onRestart,
|
|
558
|
-
// ENG-5932: defaults make quarantine a strict no-op (classify everything
|
|
559
|
-
// essential, mode off) so legacy callers/tests are unaffected.
|
|
560
|
-
classifyKey = () => "essential",
|
|
561
|
-
quarantineDwellMs = 0,
|
|
562
|
-
quarantineMode = "off",
|
|
563
|
-
onQuarantine,
|
|
564
|
-
isolated = false
|
|
565
|
-
} = args;
|
|
566
|
-
const now = args.now ?? Date.now;
|
|
567
|
-
const runPs = args.runPs ?? (isolated ? () => execFileSync2("docker", ["exec", `agt-${codeName}`, "ps", "-eo", "pid,ppid,args"], {
|
|
568
|
-
encoding: "utf-8",
|
|
569
|
-
timeout: 8e3
|
|
570
|
-
}) : () => execFileSync2("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", timeout: 5e3 }));
|
|
571
|
-
if (!mcpJson?.mcpServers) {
|
|
572
|
-
return { missing: [], restarted: false, reason: "no-mcp-json" };
|
|
573
|
-
}
|
|
574
|
-
const declaredCount = Object.keys(mcpJson.mcpServers).length;
|
|
575
|
-
if (declaredCount === 0) {
|
|
576
|
-
return { missing: [], restarted: false, reason: "no-declared-servers" };
|
|
577
|
-
}
|
|
578
|
-
if (restartPending) {
|
|
579
|
-
return { missing: [], restarted: false, reason: "restart-pending" };
|
|
580
|
-
}
|
|
581
|
-
if (sessionStartedAt === null) {
|
|
582
|
-
return { missing: [], restarted: false, reason: "session-start-unknown" };
|
|
583
|
-
}
|
|
584
|
-
if (now() - sessionStartedAt < graceMs) {
|
|
585
|
-
return { missing: [], restarted: false, reason: "cold-start" };
|
|
586
|
-
}
|
|
587
|
-
let psOutput;
|
|
588
|
-
try {
|
|
589
|
-
psOutput = runPs();
|
|
590
|
-
} catch (err) {
|
|
591
|
-
log2(`[mcp-presence-reaper] ps invocation failed for '${codeName}': ${err.message} \u2014 skipping`);
|
|
592
|
-
return { missing: [], restarted: false, reason: "healthy" };
|
|
593
|
-
}
|
|
594
|
-
const rows = parsePsRows(psOutput);
|
|
595
|
-
const missingRaw = findMissingMcpServers({ rows, codeName, mcpJson, inContainer: isolated });
|
|
596
|
-
const rotationGraced = [];
|
|
597
|
-
const missing = [];
|
|
598
|
-
for (const key of missingRaw) {
|
|
599
|
-
if (wasRecentlyRotated(codeName, key, rotationGraceMs, now)) {
|
|
600
|
-
rotationGraced.push(key);
|
|
601
|
-
} else {
|
|
602
|
-
missing.push(key);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
if (rotationGraced.length > 0) {
|
|
606
|
-
log2(
|
|
607
|
-
`[mcp-presence-reaper] '${codeName}': skipping [${rotationGraced.join(", ")}] within rotation grace window (${rotationGraceMs}ms) \u2014 child(ren) just SIGTERM'd by stale-mcp-reaper, awaiting respawn on next tool call (ENG-5344)`
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
const declared = mcpJson.mcpServers ?? {};
|
|
611
|
-
const liveKeys = /* @__PURE__ */ new Set();
|
|
612
|
-
for (const key of Object.keys(declared)) {
|
|
613
|
-
const entry = declared[key];
|
|
614
|
-
if (typeof entry?.command !== "string") continue;
|
|
615
|
-
if (!missingRaw.includes(key)) liveKeys.add(key);
|
|
616
|
-
}
|
|
617
|
-
for (const key of liveKeys) {
|
|
618
|
-
const state7 = presenceReaperState.get(stateKey(codeName, key));
|
|
619
|
-
if (state7) {
|
|
620
|
-
state7.attempts = 0;
|
|
621
|
-
state7.lastSeenLiveAt = now();
|
|
622
|
-
state7.lastAttemptedSessionStartedAt = null;
|
|
623
|
-
state7.gaveUpLogged = false;
|
|
624
|
-
state7.firstMissingAt = null;
|
|
625
|
-
state7.quarantineLogged = false;
|
|
626
|
-
state7.gaveUp = false;
|
|
627
|
-
if (state7.stableLiveSince === null) state7.stableLiveSince = now();
|
|
628
|
-
if (now() - state7.stableLiveSince >= BACKOFF_STABLE_RESET_MS) {
|
|
629
|
-
state7.restartHistory = [];
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
if (missing.length === 0) {
|
|
634
|
-
return {
|
|
635
|
-
missing,
|
|
636
|
-
restarted: false,
|
|
637
|
-
reason: "healthy",
|
|
638
|
-
rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
const nowMs = now();
|
|
642
|
-
const givenUp = [];
|
|
643
|
-
const active = [];
|
|
644
|
-
const backingOff = [];
|
|
645
|
-
const newGenerationKeys = /* @__PURE__ */ new Set();
|
|
646
|
-
const quarantined = [];
|
|
647
|
-
const wouldQuarantine = [];
|
|
648
|
-
for (const key of missing) {
|
|
649
|
-
const sk = stateKey(codeName, key);
|
|
650
|
-
const state7 = presenceReaperState.get(sk) ?? {
|
|
651
|
-
attempts: 0,
|
|
652
|
-
lastSeenLiveAt: null,
|
|
653
|
-
lastAttemptedSessionStartedAt: null,
|
|
654
|
-
gaveUpLogged: false,
|
|
655
|
-
firstMissingAt: null,
|
|
656
|
-
quarantineLogged: false,
|
|
657
|
-
restartHistory: [],
|
|
658
|
-
stableLiveSince: null,
|
|
659
|
-
gaveUp: false
|
|
660
|
-
};
|
|
661
|
-
if (state7.firstMissingAt === null) state7.firstMissingAt = nowMs;
|
|
662
|
-
state7.stableLiveSince = null;
|
|
663
|
-
if (state7.lastAttemptedSessionStartedAt !== sessionStartedAt) {
|
|
664
|
-
state7.attempts += 1;
|
|
665
|
-
state7.lastAttemptedSessionStartedAt = sessionStartedAt;
|
|
666
|
-
newGenerationKeys.add(key);
|
|
667
|
-
}
|
|
668
|
-
state7.restartHistory = state7.restartHistory.filter((t) => nowMs - t < BACKOFF_WINDOW_MS);
|
|
669
|
-
const restartsInWindow = state7.restartHistory.length;
|
|
670
|
-
presenceReaperState.set(sk, state7);
|
|
671
|
-
const overAttemptsCap = state7.attempts > MAX_PRESENCE_RESTART_ATTEMPTS;
|
|
672
|
-
const overRateCap = restartsInWindow >= BACKOFF_GIVE_UP_RESTARTS;
|
|
673
|
-
if (overAttemptsCap || overRateCap) {
|
|
674
|
-
givenUp.push(key);
|
|
675
|
-
state7.gaveUp = true;
|
|
676
|
-
if (!state7.gaveUpLogged) {
|
|
677
|
-
const cause = overAttemptsCap ? `${MAX_PRESENCE_RESTART_ATTEMPTS} consecutive failed restarts \u2014 declared but never recovers (likely orphaned config; see ENG-5279)` : `${restartsInWindow} restarts within ${Math.round(BACKOFF_WINDOW_MS / 6e4)}m \u2014 persistently flapping (crash-loop; ENG-6480)`;
|
|
678
|
-
log2(
|
|
679
|
-
`[mcp-presence-reaper] giving up on '${codeName}:${key}' after ${cause}`
|
|
680
|
-
);
|
|
681
|
-
state7.gaveUpLogged = true;
|
|
682
|
-
if (onGiveUp) {
|
|
683
|
-
try {
|
|
684
|
-
onGiveUp(codeName, key);
|
|
685
|
-
} catch (err) {
|
|
686
|
-
log2(
|
|
687
|
-
`[mcp-presence-reaper] onGiveUp callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
if (quarantineMode !== "off" && !state7.quarantineLogged && classifyKey(key) === "optional") {
|
|
693
|
-
const dwellElapsed = quarantineDwellMs <= 0 || state7.firstMissingAt !== null && nowMs - state7.firstMissingAt >= quarantineDwellMs;
|
|
694
|
-
if (dwellElapsed) {
|
|
695
|
-
state7.quarantineLogged = true;
|
|
696
|
-
if (quarantineMode === "enforce") {
|
|
697
|
-
quarantined.push(key);
|
|
698
|
-
log2(
|
|
699
|
-
`[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)`
|
|
700
|
-
);
|
|
701
|
-
if (onQuarantine) {
|
|
702
|
-
try {
|
|
703
|
-
onQuarantine(codeName, key);
|
|
704
|
-
} catch (err) {
|
|
705
|
-
log2(
|
|
706
|
-
`[mcp-presence-reaper] onQuarantine callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
|
|
707
|
-
);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
} else {
|
|
711
|
-
wouldQuarantine.push(key);
|
|
712
|
-
log2(
|
|
713
|
-
`[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)`
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
} else {
|
|
719
|
-
const backoffMs = restartsInWindow < BACKOFF_AFTER_RESTARTS ? 0 : Math.min(
|
|
720
|
-
BACKOFF_BASE_MS * 2 ** (restartsInWindow - BACKOFF_AFTER_RESTARTS),
|
|
721
|
-
BACKOFF_MAX_MS
|
|
722
|
-
);
|
|
723
|
-
const lastRestartAt = state7.restartHistory[state7.restartHistory.length - 1] ?? null;
|
|
724
|
-
if (lastRestartAt !== null && nowMs - lastRestartAt < backoffMs) {
|
|
725
|
-
backingOff.push(key);
|
|
726
|
-
} else {
|
|
727
|
-
active.push(key);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
if (active.length === 0) {
|
|
732
|
-
const reason = backingOff.length > 0 ? "backoff" : "all-keys-over-cap";
|
|
733
|
-
if (backingOff.length > 0) {
|
|
734
|
-
log2(
|
|
735
|
-
`[mcp-presence-reaper] '${codeName}': holding off restart for [${backingOff.join(", ")}] \u2014 escalating backoff interval not yet elapsed (ENG-6480)${givenUp.length > 0 ? ` (given up: [${givenUp.join(", ")}])` : ""}`
|
|
736
|
-
);
|
|
737
|
-
}
|
|
738
|
-
return {
|
|
739
|
-
missing,
|
|
740
|
-
restarted: false,
|
|
741
|
-
reason,
|
|
742
|
-
givenUp,
|
|
743
|
-
backingOff: backingOff.length > 0 ? backingOff : void 0,
|
|
744
|
-
rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
|
|
745
|
-
quarantined: quarantined.length > 0 ? quarantined : void 0,
|
|
746
|
-
wouldQuarantine: wouldQuarantine.length > 0 ? wouldQuarantine : void 0
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
const givenUpSuffix = givenUp.length > 0 ? ` (skipping over-cap: [${givenUp.join(", ")}])` : "";
|
|
750
|
-
log2(
|
|
751
|
-
`[mcp-presence-reaper] '${codeName}': declared MCP(s) [${active.join(", ")}] have no live children \u2014 restarting session${givenUpSuffix}`
|
|
752
|
-
);
|
|
753
|
-
if (onRestart) {
|
|
754
|
-
try {
|
|
755
|
-
const maybePromise = onRestart(codeName, { activeKeys: active, givenUpKeys: givenUp });
|
|
756
|
-
if (maybePromise && typeof maybePromise.then === "function") {
|
|
757
|
-
void maybePromise.then(void 0, (err) => {
|
|
758
|
-
log2(
|
|
759
|
-
`[mcp-presence-reaper] onRestart callback rejected for '${codeName}' (suppressed; restart proceeds): ${err.message}`
|
|
760
|
-
);
|
|
761
|
-
});
|
|
762
|
-
}
|
|
763
|
-
} catch (err) {
|
|
764
|
-
log2(
|
|
765
|
-
`[mcp-presence-reaper] onRestart callback threw for '${codeName}' (suppressed; restart proceeds): ${err.message}`
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
stopSession(codeName, { activeKeys: active });
|
|
770
|
-
for (const key of active) {
|
|
771
|
-
if (!newGenerationKeys.has(key)) continue;
|
|
772
|
-
const st = presenceReaperState.get(stateKey(codeName, key));
|
|
773
|
-
if (st) st.restartHistory.push(nowMs);
|
|
774
|
-
}
|
|
775
|
-
return {
|
|
776
|
-
missing,
|
|
777
|
-
restarted: true,
|
|
778
|
-
givenUp: givenUp.length > 0 ? givenUp : void 0,
|
|
779
|
-
backingOff: backingOff.length > 0 ? backingOff : void 0,
|
|
780
|
-
rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
|
|
781
|
-
quarantined: quarantined.length > 0 ? quarantined : void 0,
|
|
782
|
-
wouldQuarantine: wouldQuarantine.length > 0 ? wouldQuarantine : void 0
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
|
|
786
236
|
// src/lib/channel-restart-decision.ts
|
|
787
237
|
function launchableChannelIds(channelConfigs) {
|
|
788
238
|
if (!channelConfigs) return /* @__PURE__ */ new Set();
|
|
@@ -1809,116 +1259,6 @@ function isDirectChatMessageExpired(createdAt, nowMs, maxAgeMs) {
|
|
|
1809
1259
|
return nowMs - created > maxAgeMs;
|
|
1810
1260
|
}
|
|
1811
1261
|
|
|
1812
|
-
// src/lib/session-tool-probe.ts
|
|
1813
|
-
function classifyMcpServer(entry) {
|
|
1814
|
-
if (!entry || typeof entry !== "object") return "unknown";
|
|
1815
|
-
const e = entry;
|
|
1816
|
-
if (typeof e.command === "string") return "stdio";
|
|
1817
|
-
if (typeof e.url === "string") return "http";
|
|
1818
|
-
return "unknown";
|
|
1819
|
-
}
|
|
1820
|
-
function splitServerKeysByKind(mcpJson, serverKeys) {
|
|
1821
|
-
const servers = mcpJson?.mcpServers ?? {};
|
|
1822
|
-
const stdio = [];
|
|
1823
|
-
const http = [];
|
|
1824
|
-
for (const key of serverKeys) {
|
|
1825
|
-
const kind = classifyMcpServer(servers[key]);
|
|
1826
|
-
if (kind === "stdio") stdio.push(key);
|
|
1827
|
-
else if (kind === "http") http.push(key);
|
|
1828
|
-
}
|
|
1829
|
-
return { stdio, http };
|
|
1830
|
-
}
|
|
1831
|
-
function computeSessionToolBindStatus(input) {
|
|
1832
|
-
const { stdioServerKeys, httpServerKeys, missingStdioKeys, httpReachable, httpSessionLoaded } = input;
|
|
1833
|
-
if (stdioServerKeys.length === 0 && httpServerKeys.length === 0) return "unknown";
|
|
1834
|
-
if (stdioServerKeys.some((k) => missingStdioKeys.has(k))) return "missing";
|
|
1835
|
-
let anyHttpUnconfirmed = false;
|
|
1836
|
-
for (const k of httpServerKeys) {
|
|
1837
|
-
const reachable = httpReachable.get(k);
|
|
1838
|
-
if (reachable === false) return "unreachable";
|
|
1839
|
-
if (reachable === void 0) {
|
|
1840
|
-
anyHttpUnconfirmed = true;
|
|
1841
|
-
} else if (!httpSessionLoaded) {
|
|
1842
|
-
anyHttpUnconfirmed = true;
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1845
|
-
if (anyHttpUnconfirmed && stdioServerKeys.length === 0) return "unknown";
|
|
1846
|
-
return "bound";
|
|
1847
|
-
}
|
|
1848
|
-
|
|
1849
|
-
// src/lib/session-tool-bind-runner.ts
|
|
1850
|
-
var DEFAULT_INTERVAL_MS2 = 60 * 60 * 1e3;
|
|
1851
|
-
var DEFAULT_MAX_PER_RUN2 = 25;
|
|
1852
|
-
function sanitizeServerKey(definitionId) {
|
|
1853
|
-
return definitionId.replace(/[^a-z0-9]/gi, "_").toLowerCase();
|
|
1854
|
-
}
|
|
1855
|
-
function resolveIntegrationServerKeys(definitionId, declaredKeys, hint) {
|
|
1856
|
-
const declared = new Set(declaredKeys);
|
|
1857
|
-
const candidates = [hint, definitionId, sanitizeServerKey(definitionId)].filter(
|
|
1858
|
-
(k) => typeof k === "string" && k.length > 0
|
|
1859
|
-
);
|
|
1860
|
-
const out = /* @__PURE__ */ new Set();
|
|
1861
|
-
for (const c of candidates) if (declared.has(c)) out.add(c);
|
|
1862
|
-
return [...out];
|
|
1863
|
-
}
|
|
1864
|
-
function isDue2(last, now, intervalMs) {
|
|
1865
|
-
if (!last) return true;
|
|
1866
|
-
const t = Date.parse(last);
|
|
1867
|
-
if (Number.isNaN(t)) return true;
|
|
1868
|
-
return now - t >= intervalMs;
|
|
1869
|
-
}
|
|
1870
|
-
async function runSessionToolBindProbes(integrations, options) {
|
|
1871
|
-
const now = (options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
|
|
1872
|
-
const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS2;
|
|
1873
|
-
const maxPerRun = options.maxPerRun ?? DEFAULT_MAX_PER_RUN2;
|
|
1874
|
-
const due = integrations.filter((i) => isDue2(i.last_session_tool_bind_at, now, intervalMs)).sort((a, b) => {
|
|
1875
|
-
const ta = a.last_session_tool_bind_at ? Date.parse(a.last_session_tool_bind_at) : 0;
|
|
1876
|
-
const tb = b.last_session_tool_bind_at ? Date.parse(b.last_session_tool_bind_at) : 0;
|
|
1877
|
-
return ta - tb;
|
|
1878
|
-
});
|
|
1879
|
-
const batch = due.slice(0, maxPerRun);
|
|
1880
|
-
const declaredKeys = Object.keys(options.mcpJson?.mcpServers ?? {});
|
|
1881
|
-
const reports = [];
|
|
1882
|
-
let skipped = 0;
|
|
1883
|
-
let probed = 0;
|
|
1884
|
-
for (const integ of batch) {
|
|
1885
|
-
const keys = resolveIntegrationServerKeys(
|
|
1886
|
-
integ.definition_id,
|
|
1887
|
-
declaredKeys,
|
|
1888
|
-
integ.mcp_server_key
|
|
1889
|
-
);
|
|
1890
|
-
if (keys.length === 0) {
|
|
1891
|
-
skipped += 1;
|
|
1892
|
-
continue;
|
|
1893
|
-
}
|
|
1894
|
-
probed += 1;
|
|
1895
|
-
const { stdio, http } = splitServerKeysByKind(options.mcpJson, keys);
|
|
1896
|
-
const httpReachable = /* @__PURE__ */ new Map();
|
|
1897
|
-
for (const key of http) {
|
|
1898
|
-
let reachable;
|
|
1899
|
-
try {
|
|
1900
|
-
reachable = await options.probeHttp(key);
|
|
1901
|
-
} catch {
|
|
1902
|
-
reachable = void 0;
|
|
1903
|
-
}
|
|
1904
|
-
if (reachable !== void 0) httpReachable.set(key, reachable);
|
|
1905
|
-
}
|
|
1906
|
-
const status = computeSessionToolBindStatus({
|
|
1907
|
-
stdioServerKeys: stdio,
|
|
1908
|
-
httpServerKeys: http,
|
|
1909
|
-
missingStdioKeys: options.missingStdioKeys,
|
|
1910
|
-
httpReachable,
|
|
1911
|
-
httpSessionLoaded: options.httpSessionLoaded ?? false
|
|
1912
|
-
});
|
|
1913
|
-
if (status === "unknown") {
|
|
1914
|
-
skipped += 1;
|
|
1915
|
-
continue;
|
|
1916
|
-
}
|
|
1917
|
-
reports.push({ integration_id: integ.id, scope: integ.scope, status });
|
|
1918
|
-
}
|
|
1919
|
-
return { reports, due: due.length, probed, skipped };
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
1262
|
// ../../packages/core/dist/host-config/capture.js
|
|
1923
1263
|
import { createHash as createHash3 } from "crypto";
|
|
1924
1264
|
var NON_SECRET_ENV_GATES = [
|
|
@@ -3435,7 +2775,7 @@ function saveChannelHashCache(source, configDir) {
|
|
|
3435
2775
|
}
|
|
3436
2776
|
|
|
3437
2777
|
// src/lib/channel-sweep.ts
|
|
3438
|
-
import { execFileSync
|
|
2778
|
+
import { execFileSync } from "child_process";
|
|
3439
2779
|
var CHANNEL_BASENAMES = [
|
|
3440
2780
|
"slack-channel",
|
|
3441
2781
|
"direct-chat-channel",
|
|
@@ -3598,7 +2938,7 @@ function resolveLiveAnchorPids(agentCodeNames) {
|
|
|
3598
2938
|
for (const codeName of agentCodeNames) {
|
|
3599
2939
|
const pids = /* @__PURE__ */ new Set();
|
|
3600
2940
|
try {
|
|
3601
|
-
const out =
|
|
2941
|
+
const out = execFileSync("tmux", ["list-panes", "-t", `agt-${codeName}`, "-F", "#{pane_pid}"], {
|
|
3602
2942
|
encoding: "utf-8",
|
|
3603
2943
|
timeout: 2e3,
|
|
3604
2944
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -3618,7 +2958,7 @@ async function sweepChannelProcesses(opts) {
|
|
|
3618
2958
|
const kill = opts.killFn ?? defaultKill;
|
|
3619
2959
|
let psOutput = "";
|
|
3620
2960
|
try {
|
|
3621
|
-
psOutput =
|
|
2961
|
+
psOutput = execFileSync("ps", ["eww", "-o", "pid=,ppid=,etime=,command="], {
|
|
3622
2962
|
encoding: "utf-8",
|
|
3623
2963
|
timeout: 5e3,
|
|
3624
2964
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -3655,7 +2995,7 @@ function defaultKillSignal(pid, signal) {
|
|
|
3655
2995
|
}
|
|
3656
2996
|
}
|
|
3657
2997
|
function defaultPs() {
|
|
3658
|
-
return
|
|
2998
|
+
return execFileSync("ps", ["eww", "-o", "pid=,ppid=,etime=,command="], {
|
|
3659
2999
|
encoding: "utf-8",
|
|
3660
3000
|
timeout: 5e3,
|
|
3661
3001
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -6621,65 +5961,13 @@ async function runAgentConnectivityProbes(agent, integrations, projectDir) {
|
|
|
6621
5961
|
}
|
|
6622
5962
|
async function runAgentSessionToolBindProbes(agent, integrations, projectDir) {
|
|
6623
5963
|
if (integrations.length === 0) return;
|
|
6624
|
-
let mcpJson = null;
|
|
6625
|
-
try {
|
|
6626
|
-
mcpJson = JSON.parse(readFileSync14(join16(projectDir, ".mcp.json"), "utf-8"));
|
|
6627
|
-
} catch {
|
|
6628
|
-
return;
|
|
6629
|
-
}
|
|
6630
|
-
if (!mcpJson?.mcpServers || Object.keys(mcpJson.mcpServers).length === 0) return;
|
|
6631
|
-
const isolated = isolationMode(agent.code_name) === "docker";
|
|
6632
|
-
let missingStdioKeys = /* @__PURE__ */ new Set();
|
|
6633
|
-
try {
|
|
6634
|
-
const psOutput = isolated ? syncExecFile("docker", ["exec", `agt-${agent.code_name}`, "ps", "-eo", "pid,ppid,args"], {
|
|
6635
|
-
encoding: "utf-8",
|
|
6636
|
-
timeout: 8e3
|
|
6637
|
-
}) : syncExecFile("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", timeout: 5e3 });
|
|
6638
|
-
missingStdioKeys = new Set(
|
|
6639
|
-
findMissingMcpServers({
|
|
6640
|
-
rows: parsePsRows(psOutput),
|
|
6641
|
-
codeName: agent.code_name,
|
|
6642
|
-
mcpJson,
|
|
6643
|
-
inContainer: isolated
|
|
6644
|
-
})
|
|
6645
|
-
);
|
|
6646
|
-
} catch {
|
|
6647
|
-
}
|
|
6648
|
-
const probeEnv = buildProbeEnv(projectDir);
|
|
6649
|
-
const probeDeps = buildConnectivityProbeDeps(projectDir, probeEnv);
|
|
6650
|
-
const probeHttp = async (serverKey) => {
|
|
6651
|
-
if (!probeDeps.mcpProbe) return void 0;
|
|
6652
|
-
const outcome = await probeDeps.mcpProbe({ serverKey, definitionId: serverKey });
|
|
6653
|
-
if (outcome.status === "ok") return true;
|
|
6654
|
-
if (outcome.status === "down") return false;
|
|
6655
|
-
return void 0;
|
|
6656
|
-
};
|
|
6657
|
-
let httpSessionLoaded = false;
|
|
6658
|
-
try {
|
|
6659
|
-
const sessionStartedMs = getSessionState(agent.code_name)?.startedAt ?? null;
|
|
6660
|
-
const mcpMtimeMs = statSync4(join16(projectDir, ".mcp.json")).mtimeMs;
|
|
6661
|
-
httpSessionLoaded = sessionStartedMs != null && sessionStartedMs >= mcpMtimeMs;
|
|
6662
|
-
} catch {
|
|
6663
|
-
}
|
|
6664
5964
|
const intervalSec = Number(process.env.AGT_CONNECTIVITY_PROBE_INTERVAL_SECONDS) || 3600;
|
|
6665
|
-
const
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
// Server-key hint for managed/remote-MCP kinds; the runner also tries the
|
|
6672
|
-
// raw + sanitised definition_id against the real `.mcp.json` keys.
|
|
6673
|
-
mcp_server_key: deriveMcpServerKey({
|
|
6674
|
-
definitionId: i.definition_id,
|
|
6675
|
-
sourceType: i.source_type ?? null,
|
|
6676
|
-
authType: i.auth_type,
|
|
6677
|
-
connectivityTest: i.connectivity_test ?? null
|
|
6678
|
-
})
|
|
6679
|
-
})),
|
|
6680
|
-
{ mcpJson, missingStdioKeys, probeHttp, httpSessionLoaded, intervalMs: intervalSec * 1e3 }
|
|
6681
|
-
);
|
|
6682
|
-
if (result.reports.length > 0) {
|
|
5965
|
+
const sessionStartedMs = getSessionState(agent.code_name)?.startedAt ?? null;
|
|
5966
|
+
const result = await gatherSessionToolBindProbe(agent, integrations, projectDir, {
|
|
5967
|
+
sessionStartedMs,
|
|
5968
|
+
intervalMs: intervalSec * 1e3
|
|
5969
|
+
});
|
|
5970
|
+
if (result && result.reports.length > 0) {
|
|
6683
5971
|
await api.post("/host/session-tool-bind", {
|
|
6684
5972
|
agent_id: agent.agent_id,
|
|
6685
5973
|
results: result.reports
|
|
@@ -6871,10 +6159,10 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
6871
6159
|
var lastVersionCheckAt = 0;
|
|
6872
6160
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
6873
6161
|
var lastResponsivenessProbeAt = 0;
|
|
6874
|
-
var agtCliVersion = true ? "0.28.
|
|
6875
|
-
function resolveBrewPath(
|
|
6162
|
+
var agtCliVersion = true ? "0.28.209" : "dev";
|
|
6163
|
+
function resolveBrewPath(execFileSync2) {
|
|
6876
6164
|
try {
|
|
6877
|
-
const out =
|
|
6165
|
+
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
6878
6166
|
if (out) return out;
|
|
6879
6167
|
} catch {
|
|
6880
6168
|
}
|
|
@@ -6888,7 +6176,7 @@ function resolveBrewPath(execFileSync4) {
|
|
|
6888
6176
|
}
|
|
6889
6177
|
return null;
|
|
6890
6178
|
}
|
|
6891
|
-
function claudeBinaryInstalled(
|
|
6179
|
+
function claudeBinaryInstalled(execFileSync2) {
|
|
6892
6180
|
const canonical = [
|
|
6893
6181
|
"/home/linuxbrew/.linuxbrew/bin/claude",
|
|
6894
6182
|
"/opt/homebrew/bin/claude",
|
|
@@ -6896,7 +6184,7 @@ function claudeBinaryInstalled(execFileSync4) {
|
|
|
6896
6184
|
];
|
|
6897
6185
|
if (canonical.some((path) => existsSync8(path))) return true;
|
|
6898
6186
|
try {
|
|
6899
|
-
|
|
6187
|
+
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
6900
6188
|
return true;
|
|
6901
6189
|
} catch {
|
|
6902
6190
|
return false;
|
|
@@ -6930,9 +6218,9 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
6930
6218
|
}
|
|
6931
6219
|
const { binary, installer, package: pkg, script } = integration.cli_tool;
|
|
6932
6220
|
const resolvedInstaller = installer ?? "manual";
|
|
6933
|
-
const { execFileSync:
|
|
6221
|
+
const { execFileSync: execFileSync2, execSync } = await import("child_process");
|
|
6934
6222
|
try {
|
|
6935
|
-
|
|
6223
|
+
execFileSync2("which", [binary], { timeout: 5e3, stdio: "pipe" });
|
|
6936
6224
|
toolkitCliEnsured.add(toolkitSlug);
|
|
6937
6225
|
toolkitCliRetryAfter.delete(toolkitSlug);
|
|
6938
6226
|
toolkitCliFailureCount.delete(toolkitSlug);
|
|
@@ -6953,14 +6241,14 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
6953
6241
|
return;
|
|
6954
6242
|
}
|
|
6955
6243
|
log(`[toolkit-install] ${toolkitSlug}: installing via npm (${pkg})\u2026`);
|
|
6956
|
-
|
|
6244
|
+
execFileSync2("npm", ["install", "-g", pkg], { timeout: 18e4, stdio: "pipe" });
|
|
6957
6245
|
} else if (resolvedInstaller === "brew") {
|
|
6958
6246
|
if (!pkg) {
|
|
6959
6247
|
log(`[toolkit-install] ${toolkitSlug}: installer=brew but no package declared`);
|
|
6960
6248
|
toolkitCliEnsured.add(toolkitSlug);
|
|
6961
6249
|
return;
|
|
6962
6250
|
}
|
|
6963
|
-
const brewPath = resolveBrewPath(
|
|
6251
|
+
const brewPath = resolveBrewPath(execFileSync2);
|
|
6964
6252
|
if (!brewPath) {
|
|
6965
6253
|
log(`[toolkit-install] ${toolkitSlug}: installer=brew but Homebrew not available \u2014 install manually: brew install ${pkg}`);
|
|
6966
6254
|
toolkitCliEnsured.add(toolkitSlug);
|
|
@@ -6970,9 +6258,9 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
6970
6258
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
6971
6259
|
log(`[toolkit-install] ${toolkitSlug}: installing via brew (${pkg})\u2026`);
|
|
6972
6260
|
if (isRoot) {
|
|
6973
|
-
|
|
6261
|
+
execFileSync2("sudo", ["-u", "ec2-user", "-H", brewPath, "install", pkg], { timeout: 18e4, stdio: "pipe", cwd: "/tmp" });
|
|
6974
6262
|
} else {
|
|
6975
|
-
|
|
6263
|
+
execFileSync2(brewPath, ["install", pkg], { timeout: 18e4, stdio: "pipe" });
|
|
6976
6264
|
}
|
|
6977
6265
|
} else if (resolvedInstaller === "script") {
|
|
6978
6266
|
if (!script) {
|
|
@@ -6992,7 +6280,7 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
6992
6280
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
6993
6281
|
}
|
|
6994
6282
|
try {
|
|
6995
|
-
|
|
6283
|
+
execFileSync2("which", [binary], { timeout: 5e3, stdio: "pipe" });
|
|
6996
6284
|
log(`[toolkit-install] ${toolkitSlug}: installed \u2014 ${binary} now on PATH`);
|
|
6997
6285
|
toolkitCliEnsured.add(toolkitSlug);
|
|
6998
6286
|
toolkitCliRetryAfter.delete(toolkitSlug);
|
|
@@ -7079,8 +6367,8 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
7079
6367
|
if (frameworkBinaryChecked.has(frameworkId)) return;
|
|
7080
6368
|
frameworkBinaryChecked.add(frameworkId);
|
|
7081
6369
|
lastManagedSettingsStatus = ensureClaudeManagedSettings();
|
|
7082
|
-
const { execFileSync:
|
|
7083
|
-
const brewPath = resolveBrewPath(
|
|
6370
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
6371
|
+
const brewPath = resolveBrewPath(execFileSync2);
|
|
7084
6372
|
if (!brewPath) {
|
|
7085
6373
|
log("Homebrew not found (no `brew` on PATH, no /home/linuxbrew/.linuxbrew/bin/brew). Cannot auto-install Claude Code. Install manually: https://claude.ai/download");
|
|
7086
6374
|
return;
|
|
@@ -7093,7 +6381,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
7093
6381
|
}
|
|
7094
6382
|
return runAsync(brewPath, args, opts);
|
|
7095
6383
|
};
|
|
7096
|
-
const claudeExists = claudeBinaryInstalled(
|
|
6384
|
+
const claudeExists = claudeBinaryInstalled(execFileSync2);
|
|
7097
6385
|
if (!claudeExists) {
|
|
7098
6386
|
log(`Claude Code binary not found \u2014 installing via Homebrew${isRoot ? " (as ec2-user via sudo)" : ""}...`);
|
|
7099
6387
|
try {
|
|
@@ -7128,13 +6416,13 @@ async function maybeUpgradeClaudeCode() {
|
|
|
7128
6416
|
}
|
|
7129
6417
|
claudeCodeUpgradeInFlight = true;
|
|
7130
6418
|
stampClaudeCodeUpgradeMarker();
|
|
7131
|
-
const { execFileSync:
|
|
7132
|
-
const brewPath = resolveBrewPath(
|
|
6419
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
6420
|
+
const brewPath = resolveBrewPath(execFileSync2);
|
|
7133
6421
|
if (!brewPath) {
|
|
7134
6422
|
claudeCodeUpgradeInFlight = false;
|
|
7135
6423
|
return;
|
|
7136
6424
|
}
|
|
7137
|
-
if (!claudeBinaryInstalled(
|
|
6425
|
+
if (!claudeBinaryInstalled(execFileSync2)) {
|
|
7138
6426
|
claudeCodeUpgradeInFlight = false;
|
|
7139
6427
|
return;
|
|
7140
6428
|
}
|
|
@@ -7213,18 +6501,18 @@ async function checkAndUpdateCli(opts) {
|
|
|
7213
6501
|
}
|
|
7214
6502
|
}
|
|
7215
6503
|
async function checkAndUpdateCliViaBrew(force = false) {
|
|
7216
|
-
const { execFileSync:
|
|
7217
|
-
const brewPath = resolveBrewPath(
|
|
6504
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
6505
|
+
const brewPath = resolveBrewPath(execFileSync2);
|
|
7218
6506
|
if (!brewPath) return "noop";
|
|
7219
6507
|
let metadataFresh = true;
|
|
7220
6508
|
try {
|
|
7221
|
-
|
|
6509
|
+
execFileSync2(brewPath, ["update", "--quiet"], { timeout: 6e4, stdio: "pipe" });
|
|
7222
6510
|
} catch (err) {
|
|
7223
6511
|
metadataFresh = false;
|
|
7224
6512
|
log(`[self-update] brew update failed (continuing with stale cache): ${err.message}`);
|
|
7225
6513
|
}
|
|
7226
6514
|
try {
|
|
7227
|
-
const outdated =
|
|
6515
|
+
const outdated = execFileSync2(brewPath, ["outdated", "--json=v2"], {
|
|
7228
6516
|
timeout: 3e4,
|
|
7229
6517
|
encoding: "utf-8"
|
|
7230
6518
|
});
|
|
@@ -7257,7 +6545,7 @@ async function checkAndUpdateCliViaBrew(force = false) {
|
|
|
7257
6545
|
}
|
|
7258
6546
|
log(`[self-update] agt CLI update available: ${installed} \u2192 ${latest}. Upgrading via brew${force ? " (forced \u2014 Update CLI now)" : ""}...`);
|
|
7259
6547
|
try {
|
|
7260
|
-
|
|
6548
|
+
execFileSync2(brewPath, ["upgrade", "integrity-labs/tap/agt"], {
|
|
7261
6549
|
timeout: 12e4,
|
|
7262
6550
|
stdio: "pipe"
|
|
7263
6551
|
});
|
|
@@ -7376,7 +6664,7 @@ async function fetchUrgentDistTagVersion() {
|
|
|
7376
6664
|
}
|
|
7377
6665
|
}
|
|
7378
6666
|
async function installAgtCliViaNpm(version, reasonLabel) {
|
|
7379
|
-
const { execFileSync:
|
|
6667
|
+
const { execFileSync: execFileSync2 } = await import("child_process");
|
|
7380
6668
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
7381
6669
|
const cmd = isRoot ? "npm" : "sudo";
|
|
7382
6670
|
const installArgs = [
|
|
@@ -7387,14 +6675,14 @@ async function installAgtCliViaNpm(version, reasonLabel) {
|
|
|
7387
6675
|
];
|
|
7388
6676
|
const args = isRoot ? installArgs : ["-n", "npm", ...installArgs];
|
|
7389
6677
|
try {
|
|
7390
|
-
|
|
6678
|
+
execFileSync2(cmd, args, { timeout: 18e4, stdio: "pipe" });
|
|
7391
6679
|
} catch (err) {
|
|
7392
6680
|
log(`[self-update] npm upgrade failed: ${err.message}`);
|
|
7393
6681
|
return "failed";
|
|
7394
6682
|
}
|
|
7395
6683
|
let smokeOk = false;
|
|
7396
6684
|
try {
|
|
7397
|
-
const out =
|
|
6685
|
+
const out = execFileSync2("agt", ["--version"], { timeout: 15e3, stdio: "pipe" }).toString();
|
|
7398
6686
|
smokeOk = isAgtVersionSmokeOk(out, version);
|
|
7399
6687
|
if (!smokeOk) {
|
|
7400
6688
|
log(
|
|
@@ -7780,7 +7068,7 @@ async function pollCycle() {
|
|
|
7780
7068
|
}
|
|
7781
7069
|
try {
|
|
7782
7070
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
7783
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
7071
|
+
const { collectDiagnostics } = await import("../persistent-session-Z46SCCQM.js");
|
|
7784
7072
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
7785
7073
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
7786
7074
|
let tailscaleHostname;
|
|
@@ -7928,7 +7216,7 @@ async function pollCycle() {
|
|
|
7928
7216
|
const {
|
|
7929
7217
|
collectResponsivenessProbes,
|
|
7930
7218
|
getResponsivenessIntervalMs
|
|
7931
|
-
} = await import("../responsiveness-probe-
|
|
7219
|
+
} = await import("../responsiveness-probe-4MUX7FBH.js");
|
|
7932
7220
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
7933
7221
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
7934
7222
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -7960,7 +7248,7 @@ async function pollCycle() {
|
|
|
7960
7248
|
collectResponsivenessProbes,
|
|
7961
7249
|
livePendingInboundOldestAgeSeconds,
|
|
7962
7250
|
parkPendingInbound
|
|
7963
|
-
} = await import("../responsiveness-probe-
|
|
7251
|
+
} = await import("../responsiveness-probe-4MUX7FBH.js");
|
|
7964
7252
|
const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
|
|
7965
7253
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
7966
7254
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -10987,7 +10275,7 @@ async function processClaudePairSessions(agents) {
|
|
|
10987
10275
|
killPairSession,
|
|
10988
10276
|
pairTmuxSession,
|
|
10989
10277
|
finalizeClaudePairOnboarding
|
|
10990
|
-
} = await import("../claude-pair-runtime-
|
|
10278
|
+
} = await import("../claude-pair-runtime-UUTPSE2V.js");
|
|
10991
10279
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
10992
10280
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
10993
10281
|
const killed = await killPairSession(pairTmuxSession(pairId));
|