@integrity-labs/agt-cli 0.28.207 → 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.
@@ -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-TPFTAFG3.js";
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
- sumTranscriptUsageInWindow
117
- } from "../chunk-ELBYWACS.js";
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
 
@@ -223,559 +228,9 @@ function computeIntegrationsHash(integrations) {
223
228
  return createHash2("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 16);
224
229
  }
225
230
 
226
- // src/lib/stale-mcp-reaper.ts
227
- import { execFileSync } from "child_process";
228
- var rotationTimestamps = /* @__PURE__ */ new Map();
229
- var DEFAULT_ROTATION_GRACE_MS = 15e3;
230
- function rotationKey(codeName, serverKey) {
231
- return `${codeName}\0${serverKey}`;
232
- }
233
- function recordStaleRotation(codeName, serverKeys, now = Date.now) {
234
- const ts = now();
235
- for (const key of serverKeys) {
236
- rotationTimestamps.set(rotationKey(codeName, key), ts);
237
- }
238
- }
239
- function wasRecentlyRotated(codeName, serverKey, withinMs, now = Date.now) {
240
- const k = rotationKey(codeName, serverKey);
241
- const ts = rotationTimestamps.get(k);
242
- if (ts === void 0) return false;
243
- if (now() - ts < withinMs) return true;
244
- rotationTimestamps.delete(k);
245
- return false;
246
- }
247
- function parseEnvIntegrationsEntries(content) {
248
- const entries = /* @__PURE__ */ new Map();
249
- for (const raw of content.split(/\r?\n/)) {
250
- const line = raw.trim();
251
- if (!line || line.startsWith("#")) continue;
252
- const eq = line.indexOf("=");
253
- if (eq <= 0) continue;
254
- const name = line.slice(0, eq).trim();
255
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) continue;
256
- const value = line.slice(eq + 1);
257
- entries.set(name, value);
258
- }
259
- return entries;
260
- }
261
- function diffEnvIntegrations(oldContent, newContent) {
262
- const oldEntries = oldContent === void 0 ? /* @__PURE__ */ new Map() : parseEnvIntegrationsEntries(oldContent);
263
- const newEntries = parseEnvIntegrationsEntries(newContent);
264
- const changed = /* @__PURE__ */ new Set();
265
- for (const [name, value] of newEntries) {
266
- if (oldEntries.get(name) !== value) changed.add(name);
267
- }
268
- for (const name of oldEntries.keys()) {
269
- if (!newEntries.has(name)) changed.add(name);
270
- }
271
- return [...changed];
272
- }
273
- function findMcpServersUsingVars(mcp, changedVars) {
274
- const changedSet = new Set(changedVars);
275
- if (!mcp?.mcpServers || changedSet.size === 0) return [];
276
- const result = [];
277
- for (const [serverKey, entry] of Object.entries(mcp.mcpServers)) {
278
- if (!entry || typeof entry !== "object") continue;
279
- const env = entry.env;
280
- if (!env || typeof env !== "object") continue;
281
- let matches = false;
282
- for (const value of Object.values(env)) {
283
- if (typeof value !== "string") continue;
284
- const placeholderMatches = value.match(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g);
285
- if (placeholderMatches) {
286
- for (const ph of placeholderMatches) {
287
- const name = ph.slice(2, -1);
288
- if (changedSet.has(name)) {
289
- matches = true;
290
- break;
291
- }
292
- }
293
- }
294
- if (matches) break;
295
- }
296
- if (matches) result.push(serverKey);
297
- }
298
- return result;
299
- }
300
- function buildArgvMatchersForEntry(key, entry) {
301
- const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
302
- const patterns = [];
303
- const args = entry?.args ?? [];
304
- for (let i = args.length - 1; i >= 0; i--) {
305
- const arg = args[i];
306
- if (typeof arg !== "string" || !arg) continue;
307
- if (arg.startsWith("-")) continue;
308
- const stripped = arg.replace(
309
- /@[^/@]*$/,
310
- (m) => (
311
- // @latest, @1.2.3, etc. — drop. But @scope at the START of a
312
- // package spec must NOT be stripped, so we only strip a tail
313
- // `@...` if it doesn't itself start with a slash inside.
314
- // The regex above only matches a single trailing `@...`
315
- // segment after the last `/`, so this is safe for `@scope/pkg`.
316
- m.includes("/") ? m : ""
317
- )
318
- );
319
- const tail = "(?![A-Za-z0-9_-])";
320
- if (/^@?[a-z0-9]([a-z0-9._-]*\/)?[a-z0-9._-]+$/i.test(stripped)) {
321
- patterns.push(new RegExp(`${escapeRe(stripped)}${tail}`));
322
- const basename = stripped.split("/").pop();
323
- if (basename && basename !== stripped) {
324
- patterns.push(new RegExp(`${escapeRe(basename)}${tail}`));
325
- }
326
- break;
327
- }
328
- if (stripped.includes("/")) {
329
- const basename = stripped.split("/").pop();
330
- if (basename) {
331
- patterns.push(new RegExp(`${escapeRe(basename)}${tail}`));
332
- break;
333
- }
334
- }
335
- }
336
- if (patterns.length === 0) {
337
- const safe = escapeRe(key);
338
- patterns.push(new RegExp(`(?<![A-Za-z0-9_])${safe}(?![A-Za-z0-9_])`));
339
- if (safe.includes("_")) {
340
- const dashed = safe.replace(/_/g, "-");
341
- patterns.push(new RegExp(`(?<![A-Za-z0-9_])${dashed}(?![A-Za-z0-9_])`));
342
- }
343
- }
344
- return patterns;
345
- }
346
- function buildClaudeAgentMatcher(codeName) {
347
- const safe = codeName.replace(/[^A-Za-z0-9_-]/g, "");
348
- return new RegExp(`\\bclaude\\b.*--name\\s+agt-${safe}(?=\\s|$)`);
349
- }
350
- function findMcpChildrenForAgent(args) {
351
- const { rows, codeName, serverKeys, mcpJson } = args;
352
- const maxDepth = args.maxDepth ?? 8;
353
- const inContainer = args.inContainer ?? false;
354
- if (serverKeys.length === 0 || rows.length === 0) return [];
355
- const claudeMatcher = inContainer ? /\bclaude\b/ : buildClaudeAgentMatcher(codeName);
356
- const argvMatchers = [];
357
- for (const key of serverKeys) {
358
- const entry = mcpJson?.mcpServers?.[key];
359
- argvMatchers.push(...buildArgvMatchersForEntry(key, entry));
360
- }
361
- const byPid = new Map(rows.map((r) => [r.pid, r]));
362
- const matched = [];
363
- for (const row of rows) {
364
- if (!argvMatchers.some((re) => re.test(row.args))) continue;
365
- if (/\bclaude\b/.test(row.args) && row.args.includes(`--name agt-${codeName}`)) continue;
366
- let cur = byPid.get(row.ppid);
367
- let belongs = false;
368
- for (let depth = 0; depth < maxDepth && cur; depth++) {
369
- if (claudeMatcher.test(cur.args)) {
370
- belongs = true;
371
- break;
372
- }
373
- if (cur.pid === 1) break;
374
- cur = byPid.get(cur.ppid);
375
- }
376
- if (belongs) matched.push(row.pid);
377
- }
378
- return matched;
379
- }
380
- function reapStaleMcpChildren(args) {
381
- const { log: log2, codeName, serverKeys, mcpJson, graceMs = 5e3, isolated = false } = args;
382
- if (serverKeys.length === 0) return [];
383
- const runPs = args.runPs ?? (isolated ? () => execFileSync("docker", ["exec", `agt-${codeName}`, "ps", "-eo", "pid,ppid,args"], {
384
- encoding: "utf-8",
385
- timeout: 8e3
386
- }) : () => execFileSync("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", timeout: 5e3 }));
387
- const killProcess = args.killProcess ?? (isolated ? (pid, signal) => {
388
- try {
389
- execFileSync(
390
- "docker",
391
- ["exec", `agt-${codeName}`, "kill", signal === "SIGKILL" ? "-KILL" : "-TERM", String(pid)],
392
- { timeout: 8e3, stdio: "ignore" }
393
- );
394
- } catch {
395
- }
396
- } : (pid, signal) => {
397
- try {
398
- process.kill(pid, signal);
399
- } catch {
400
- }
401
- });
402
- const isAlive = args.isAlive ?? (isolated ? (pid) => {
403
- try {
404
- execFileSync("docker", ["exec", `agt-${codeName}`, "kill", "-0", String(pid)], {
405
- timeout: 8e3,
406
- stdio: "ignore"
407
- });
408
- return true;
409
- } catch {
410
- return false;
411
- }
412
- } : (pid) => {
413
- try {
414
- process.kill(pid, 0);
415
- return true;
416
- } catch {
417
- return false;
418
- }
419
- });
420
- let psOutput;
421
- try {
422
- psOutput = runPs();
423
- } catch (err) {
424
- log2(`[stale-mcp-reaper] ps invocation failed for '${codeName}': ${err.message} \u2014 skipping reap`);
425
- return [];
426
- }
427
- const rows = parsePsRows(psOutput);
428
- const targets = findMcpChildrenForAgent({ rows, codeName, serverKeys, mcpJson, inContainer: isolated });
429
- if (targets.length === 0) return [];
430
- const byPid = new Map(rows.map((r) => [r.pid, r]));
431
- const describe = (pid) => {
432
- const argv = byPid.get(pid)?.args ?? "";
433
- const pkgMatch = argv.match(/(@[a-z0-9_-]+\/[a-z0-9_-]+|[a-z0-9_-]+-mcp-server|[a-z0-9_-]+-mcp\b)/i);
434
- return pkgMatch ? `${pkgMatch[1]} (pid ${pid})` : `pid ${pid}`;
435
- };
436
- log2(
437
- `[stale-mcp-reaper] '${codeName}': rotating ${targets.length} stale MCP child(ren) for [${serverKeys.join(", ")}]: ${targets.map(describe).join(", ")}`
438
- );
439
- for (const pid of targets) {
440
- killProcess(pid, "SIGTERM");
441
- }
442
- const killedKeys = /* @__PURE__ */ new Set();
443
- for (const pid of targets) {
444
- const argv = byPid.get(pid)?.args ?? "";
445
- for (const key of serverKeys) {
446
- const entry = mcpJson?.mcpServers?.[key];
447
- const matchers = buildArgvMatchersForEntry(key, entry);
448
- if (matchers.some((re) => re.test(argv))) killedKeys.add(key);
449
- }
450
- }
451
- if (killedKeys.size > 0) recordStaleRotation(codeName, killedKeys);
452
- setTimeout(() => {
453
- try {
454
- let freshPsOutput;
455
- try {
456
- freshPsOutput = runPs();
457
- } catch (err) {
458
- log2(`[stale-mcp-reaper] '${codeName}': fresh ps for SIGKILL re-verify failed: ${err.message} \u2014 skipping SIGKILL pass`);
459
- return;
460
- }
461
- const stillOwned = new Set(
462
- findMcpChildrenForAgent({
463
- rows: parsePsRows(freshPsOutput),
464
- codeName,
465
- serverKeys,
466
- mcpJson,
467
- inContainer: isolated
468
- })
469
- );
470
- const stragglers = targets.filter((pid) => isAlive(pid) && stillOwned.has(pid));
471
- if (stragglers.length === 0) return;
472
- log2(
473
- `[stale-mcp-reaper] '${codeName}': ${stragglers.length} child(ren) survived SIGTERM; sending SIGKILL: ${stragglers.map(describe).join(", ")}`
474
- );
475
- for (const pid of stragglers) {
476
- killProcess(pid, "SIGKILL");
477
- }
478
- } catch (err) {
479
- log2(`[stale-mcp-reaper] '${codeName}': error in SIGKILL pass: ${err.message}`);
480
- }
481
- }, graceMs).unref();
482
- return targets;
483
- }
484
-
485
- // src/lib/mcp-presence-reaper.ts
486
- import { execFileSync as execFileSync2 } from "child_process";
487
- var DEFAULT_COLD_START_GRACE_MS = 9e4;
488
- var MAX_PRESENCE_RESTART_ATTEMPTS = 3;
489
- var BACKOFF_AFTER_RESTARTS = MAX_PRESENCE_RESTART_ATTEMPTS;
490
- var BACKOFF_BASE_MS = 6e4;
491
- var BACKOFF_MAX_MS = 30 * 6e4;
492
- var BACKOFF_WINDOW_MS = 60 * 6e4;
493
- var BACKOFF_GIVE_UP_RESTARTS = 2 * MAX_PRESENCE_RESTART_ATTEMPTS;
494
- var BACKOFF_STABLE_RESET_MS = 5 * 6e4;
495
- var presenceReaperState = /* @__PURE__ */ new Map();
496
- function stateKey(codeName, serverKey) {
497
- return `${codeName}\0${serverKey}`;
498
- }
499
- function clearPresenceReaperState(codeName) {
500
- const prefix = `${codeName}\0`;
501
- for (const key of presenceReaperState.keys()) {
502
- if (key.startsWith(prefix)) presenceReaperState.delete(key);
503
- }
504
- }
505
- function clearPresenceReaperStateForKeys(codeName, keys) {
506
- for (const key of keys) {
507
- presenceReaperState.delete(stateKey(codeName, key));
508
- }
509
- }
510
- function givenUpMcpServerKeys(codeName) {
511
- const prefix = `${codeName}\0`;
512
- const out = /* @__PURE__ */ new Set();
513
- for (const [key, st] of presenceReaperState) {
514
- if (key.startsWith(prefix) && (st.gaveUp || st.attempts >= MAX_PRESENCE_RESTART_ATTEMPTS)) {
515
- out.add(key.slice(prefix.length));
516
- }
517
- }
518
- return out;
519
- }
520
- function findMissingMcpServers(args) {
521
- const { rows, codeName, mcpJson, inContainer } = args;
522
- const servers = mcpJson?.mcpServers ?? {};
523
- const declared = Object.keys(servers);
524
- if (declared.length === 0) return [];
525
- const missing = [];
526
- for (const key of declared) {
527
- const entry = servers[key];
528
- const isStdio = typeof entry?.command === "string";
529
- if (!isStdio) continue;
530
- const live = findMcpChildrenForAgent({
531
- rows,
532
- codeName,
533
- serverKeys: [key],
534
- mcpJson,
535
- inContainer
536
- });
537
- if (live.length === 0) missing.push(key);
538
- }
539
- return missing;
540
- }
541
- function reapMissingMcpSessions(args) {
542
- const {
543
- log: log2,
544
- codeName,
545
- mcpJson,
546
- sessionStartedAt,
547
- restartPending = false,
548
- stopSession,
549
- graceMs = DEFAULT_COLD_START_GRACE_MS,
550
- rotationGraceMs = DEFAULT_ROTATION_GRACE_MS,
551
- onGiveUp,
552
- onRestart,
553
- // ENG-5932: defaults make quarantine a strict no-op (classify everything
554
- // essential, mode off) so legacy callers/tests are unaffected.
555
- classifyKey = () => "essential",
556
- quarantineDwellMs = 0,
557
- quarantineMode = "off",
558
- onQuarantine,
559
- isolated = false
560
- } = args;
561
- const now = args.now ?? Date.now;
562
- const runPs = args.runPs ?? (isolated ? () => execFileSync2("docker", ["exec", `agt-${codeName}`, "ps", "-eo", "pid,ppid,args"], {
563
- encoding: "utf-8",
564
- timeout: 8e3
565
- }) : () => execFileSync2("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", timeout: 5e3 }));
566
- if (!mcpJson?.mcpServers) {
567
- return { missing: [], restarted: false, reason: "no-mcp-json" };
568
- }
569
- const declaredCount = Object.keys(mcpJson.mcpServers).length;
570
- if (declaredCount === 0) {
571
- return { missing: [], restarted: false, reason: "no-declared-servers" };
572
- }
573
- if (restartPending) {
574
- return { missing: [], restarted: false, reason: "restart-pending" };
575
- }
576
- if (sessionStartedAt === null) {
577
- return { missing: [], restarted: false, reason: "session-start-unknown" };
578
- }
579
- if (now() - sessionStartedAt < graceMs) {
580
- return { missing: [], restarted: false, reason: "cold-start" };
581
- }
582
- let psOutput;
583
- try {
584
- psOutput = runPs();
585
- } catch (err) {
586
- log2(`[mcp-presence-reaper] ps invocation failed for '${codeName}': ${err.message} \u2014 skipping`);
587
- return { missing: [], restarted: false, reason: "healthy" };
588
- }
589
- const rows = parsePsRows(psOutput);
590
- const missingRaw = findMissingMcpServers({ rows, codeName, mcpJson, inContainer: isolated });
591
- const rotationGraced = [];
592
- const missing = [];
593
- for (const key of missingRaw) {
594
- if (wasRecentlyRotated(codeName, key, rotationGraceMs, now)) {
595
- rotationGraced.push(key);
596
- } else {
597
- missing.push(key);
598
- }
599
- }
600
- if (rotationGraced.length > 0) {
601
- log2(
602
- `[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)`
603
- );
604
- }
605
- const declared = mcpJson.mcpServers ?? {};
606
- const liveKeys = /* @__PURE__ */ new Set();
607
- for (const key of Object.keys(declared)) {
608
- const entry = declared[key];
609
- if (typeof entry?.command !== "string") continue;
610
- if (!missingRaw.includes(key)) liveKeys.add(key);
611
- }
612
- for (const key of liveKeys) {
613
- const state7 = presenceReaperState.get(stateKey(codeName, key));
614
- if (state7) {
615
- state7.attempts = 0;
616
- state7.lastSeenLiveAt = now();
617
- state7.lastAttemptedSessionStartedAt = null;
618
- state7.gaveUpLogged = false;
619
- state7.firstMissingAt = null;
620
- state7.quarantineLogged = false;
621
- state7.gaveUp = false;
622
- if (state7.stableLiveSince === null) state7.stableLiveSince = now();
623
- if (now() - state7.stableLiveSince >= BACKOFF_STABLE_RESET_MS) {
624
- state7.restartHistory = [];
625
- }
626
- }
627
- }
628
- if (missing.length === 0) {
629
- return {
630
- missing,
631
- restarted: false,
632
- reason: "healthy",
633
- rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0
634
- };
635
- }
636
- const nowMs = now();
637
- const givenUp = [];
638
- const active = [];
639
- const backingOff = [];
640
- const newGenerationKeys = /* @__PURE__ */ new Set();
641
- const quarantined = [];
642
- const wouldQuarantine = [];
643
- for (const key of missing) {
644
- const sk = stateKey(codeName, key);
645
- const state7 = presenceReaperState.get(sk) ?? {
646
- attempts: 0,
647
- lastSeenLiveAt: null,
648
- lastAttemptedSessionStartedAt: null,
649
- gaveUpLogged: false,
650
- firstMissingAt: null,
651
- quarantineLogged: false,
652
- restartHistory: [],
653
- stableLiveSince: null,
654
- gaveUp: false
655
- };
656
- if (state7.firstMissingAt === null) state7.firstMissingAt = nowMs;
657
- state7.stableLiveSince = null;
658
- if (state7.lastAttemptedSessionStartedAt !== sessionStartedAt) {
659
- state7.attempts += 1;
660
- state7.lastAttemptedSessionStartedAt = sessionStartedAt;
661
- newGenerationKeys.add(key);
662
- }
663
- state7.restartHistory = state7.restartHistory.filter((t) => nowMs - t < BACKOFF_WINDOW_MS);
664
- const restartsInWindow = state7.restartHistory.length;
665
- presenceReaperState.set(sk, state7);
666
- const overAttemptsCap = state7.attempts > MAX_PRESENCE_RESTART_ATTEMPTS;
667
- const overRateCap = restartsInWindow >= BACKOFF_GIVE_UP_RESTARTS;
668
- if (overAttemptsCap || overRateCap) {
669
- givenUp.push(key);
670
- state7.gaveUp = true;
671
- if (!state7.gaveUpLogged) {
672
- 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)`;
673
- log2(
674
- `[mcp-presence-reaper] giving up on '${codeName}:${key}' after ${cause}`
675
- );
676
- state7.gaveUpLogged = true;
677
- if (onGiveUp) {
678
- try {
679
- onGiveUp(codeName, key);
680
- } catch (err) {
681
- log2(
682
- `[mcp-presence-reaper] onGiveUp callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
683
- );
684
- }
685
- }
686
- }
687
- if (quarantineMode !== "off" && !state7.quarantineLogged && classifyKey(key) === "optional") {
688
- const dwellElapsed = quarantineDwellMs <= 0 || state7.firstMissingAt !== null && nowMs - state7.firstMissingAt >= quarantineDwellMs;
689
- if (dwellElapsed) {
690
- state7.quarantineLogged = true;
691
- if (quarantineMode === "enforce") {
692
- quarantined.push(key);
693
- log2(
694
- `[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)`
695
- );
696
- if (onQuarantine) {
697
- try {
698
- onQuarantine(codeName, key);
699
- } catch (err) {
700
- log2(
701
- `[mcp-presence-reaper] onQuarantine callback threw for '${codeName}:${key}' (suppressed; reaper continues): ${err.message}`
702
- );
703
- }
704
- }
705
- } else {
706
- wouldQuarantine.push(key);
707
- log2(
708
- `[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)`
709
- );
710
- }
711
- }
712
- }
713
- } else {
714
- const backoffMs = restartsInWindow < BACKOFF_AFTER_RESTARTS ? 0 : Math.min(
715
- BACKOFF_BASE_MS * 2 ** (restartsInWindow - BACKOFF_AFTER_RESTARTS),
716
- BACKOFF_MAX_MS
717
- );
718
- const lastRestartAt = state7.restartHistory[state7.restartHistory.length - 1] ?? null;
719
- if (lastRestartAt !== null && nowMs - lastRestartAt < backoffMs) {
720
- backingOff.push(key);
721
- } else {
722
- active.push(key);
723
- }
724
- }
725
- }
726
- if (active.length === 0) {
727
- const reason = backingOff.length > 0 ? "backoff" : "all-keys-over-cap";
728
- if (backingOff.length > 0) {
729
- log2(
730
- `[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(", ")}])` : ""}`
731
- );
732
- }
733
- return {
734
- missing,
735
- restarted: false,
736
- reason,
737
- givenUp,
738
- backingOff: backingOff.length > 0 ? backingOff : void 0,
739
- rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
740
- quarantined: quarantined.length > 0 ? quarantined : void 0,
741
- wouldQuarantine: wouldQuarantine.length > 0 ? wouldQuarantine : void 0
742
- };
743
- }
744
- const givenUpSuffix = givenUp.length > 0 ? ` (skipping over-cap: [${givenUp.join(", ")}])` : "";
745
- log2(
746
- `[mcp-presence-reaper] '${codeName}': declared MCP(s) [${active.join(", ")}] have no live children \u2014 restarting session${givenUpSuffix}`
747
- );
748
- if (onRestart) {
749
- try {
750
- const maybePromise = onRestart(codeName, { activeKeys: active, givenUpKeys: givenUp });
751
- if (maybePromise && typeof maybePromise.then === "function") {
752
- void maybePromise.then(void 0, (err) => {
753
- log2(
754
- `[mcp-presence-reaper] onRestart callback rejected for '${codeName}' (suppressed; restart proceeds): ${err.message}`
755
- );
756
- });
757
- }
758
- } catch (err) {
759
- log2(
760
- `[mcp-presence-reaper] onRestart callback threw for '${codeName}' (suppressed; restart proceeds): ${err.message}`
761
- );
762
- }
763
- }
764
- stopSession(codeName, { activeKeys: active });
765
- for (const key of active) {
766
- if (!newGenerationKeys.has(key)) continue;
767
- const st = presenceReaperState.get(stateKey(codeName, key));
768
- if (st) st.restartHistory.push(nowMs);
769
- }
770
- return {
771
- missing,
772
- restarted: true,
773
- givenUp: givenUp.length > 0 ? givenUp : void 0,
774
- backingOff: backingOff.length > 0 ? backingOff : void 0,
775
- rotationGraced: rotationGraced.length > 0 ? rotationGraced : void 0,
776
- quarantined: quarantined.length > 0 ? quarantined : void 0,
777
- wouldQuarantine: wouldQuarantine.length > 0 ? wouldQuarantine : void 0
778
- };
231
+ // src/lib/host-integration-mapping.ts
232
+ function withResolvedRemoteMcp(integration) {
233
+ return { ...integration, remoteMcp: integration.remote_mcp ?? void 0 };
779
234
  }
780
235
 
781
236
  // src/lib/channel-restart-decision.ts
@@ -1804,116 +1259,6 @@ function isDirectChatMessageExpired(createdAt, nowMs, maxAgeMs) {
1804
1259
  return nowMs - created > maxAgeMs;
1805
1260
  }
1806
1261
 
1807
- // src/lib/session-tool-probe.ts
1808
- function classifyMcpServer(entry) {
1809
- if (!entry || typeof entry !== "object") return "unknown";
1810
- const e = entry;
1811
- if (typeof e.command === "string") return "stdio";
1812
- if (typeof e.url === "string") return "http";
1813
- return "unknown";
1814
- }
1815
- function splitServerKeysByKind(mcpJson, serverKeys) {
1816
- const servers = mcpJson?.mcpServers ?? {};
1817
- const stdio = [];
1818
- const http = [];
1819
- for (const key of serverKeys) {
1820
- const kind = classifyMcpServer(servers[key]);
1821
- if (kind === "stdio") stdio.push(key);
1822
- else if (kind === "http") http.push(key);
1823
- }
1824
- return { stdio, http };
1825
- }
1826
- function computeSessionToolBindStatus(input) {
1827
- const { stdioServerKeys, httpServerKeys, missingStdioKeys, httpReachable, httpSessionLoaded } = input;
1828
- if (stdioServerKeys.length === 0 && httpServerKeys.length === 0) return "unknown";
1829
- if (stdioServerKeys.some((k) => missingStdioKeys.has(k))) return "missing";
1830
- let anyHttpUnconfirmed = false;
1831
- for (const k of httpServerKeys) {
1832
- const reachable = httpReachable.get(k);
1833
- if (reachable === false) return "unreachable";
1834
- if (reachable === void 0) {
1835
- anyHttpUnconfirmed = true;
1836
- } else if (!httpSessionLoaded) {
1837
- anyHttpUnconfirmed = true;
1838
- }
1839
- }
1840
- if (anyHttpUnconfirmed && stdioServerKeys.length === 0) return "unknown";
1841
- return "bound";
1842
- }
1843
-
1844
- // src/lib/session-tool-bind-runner.ts
1845
- var DEFAULT_INTERVAL_MS2 = 60 * 60 * 1e3;
1846
- var DEFAULT_MAX_PER_RUN2 = 25;
1847
- function sanitizeServerKey(definitionId) {
1848
- return definitionId.replace(/[^a-z0-9]/gi, "_").toLowerCase();
1849
- }
1850
- function resolveIntegrationServerKeys(definitionId, declaredKeys, hint) {
1851
- const declared = new Set(declaredKeys);
1852
- const candidates = [hint, definitionId, sanitizeServerKey(definitionId)].filter(
1853
- (k) => typeof k === "string" && k.length > 0
1854
- );
1855
- const out = /* @__PURE__ */ new Set();
1856
- for (const c of candidates) if (declared.has(c)) out.add(c);
1857
- return [...out];
1858
- }
1859
- function isDue2(last, now, intervalMs) {
1860
- if (!last) return true;
1861
- const t = Date.parse(last);
1862
- if (Number.isNaN(t)) return true;
1863
- return now - t >= intervalMs;
1864
- }
1865
- async function runSessionToolBindProbes(integrations, options) {
1866
- const now = (options.now?.() ?? /* @__PURE__ */ new Date()).getTime();
1867
- const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS2;
1868
- const maxPerRun = options.maxPerRun ?? DEFAULT_MAX_PER_RUN2;
1869
- const due = integrations.filter((i) => isDue2(i.last_session_tool_bind_at, now, intervalMs)).sort((a, b) => {
1870
- const ta = a.last_session_tool_bind_at ? Date.parse(a.last_session_tool_bind_at) : 0;
1871
- const tb = b.last_session_tool_bind_at ? Date.parse(b.last_session_tool_bind_at) : 0;
1872
- return ta - tb;
1873
- });
1874
- const batch = due.slice(0, maxPerRun);
1875
- const declaredKeys = Object.keys(options.mcpJson?.mcpServers ?? {});
1876
- const reports = [];
1877
- let skipped = 0;
1878
- let probed = 0;
1879
- for (const integ of batch) {
1880
- const keys = resolveIntegrationServerKeys(
1881
- integ.definition_id,
1882
- declaredKeys,
1883
- integ.mcp_server_key
1884
- );
1885
- if (keys.length === 0) {
1886
- skipped += 1;
1887
- continue;
1888
- }
1889
- probed += 1;
1890
- const { stdio, http } = splitServerKeysByKind(options.mcpJson, keys);
1891
- const httpReachable = /* @__PURE__ */ new Map();
1892
- for (const key of http) {
1893
- let reachable;
1894
- try {
1895
- reachable = await options.probeHttp(key);
1896
- } catch {
1897
- reachable = void 0;
1898
- }
1899
- if (reachable !== void 0) httpReachable.set(key, reachable);
1900
- }
1901
- const status = computeSessionToolBindStatus({
1902
- stdioServerKeys: stdio,
1903
- httpServerKeys: http,
1904
- missingStdioKeys: options.missingStdioKeys,
1905
- httpReachable,
1906
- httpSessionLoaded: options.httpSessionLoaded ?? false
1907
- });
1908
- if (status === "unknown") {
1909
- skipped += 1;
1910
- continue;
1911
- }
1912
- reports.push({ integration_id: integ.id, scope: integ.scope, status });
1913
- }
1914
- return { reports, due: due.length, probed, skipped };
1915
- }
1916
-
1917
1262
  // ../../packages/core/dist/host-config/capture.js
1918
1263
  import { createHash as createHash3 } from "crypto";
1919
1264
  var NON_SECRET_ENV_GATES = [
@@ -3430,7 +2775,7 @@ function saveChannelHashCache(source, configDir) {
3430
2775
  }
3431
2776
 
3432
2777
  // src/lib/channel-sweep.ts
3433
- import { execFileSync as execFileSync3 } from "child_process";
2778
+ import { execFileSync } from "child_process";
3434
2779
  var CHANNEL_BASENAMES = [
3435
2780
  "slack-channel",
3436
2781
  "direct-chat-channel",
@@ -3593,7 +2938,7 @@ function resolveLiveAnchorPids(agentCodeNames) {
3593
2938
  for (const codeName of agentCodeNames) {
3594
2939
  const pids = /* @__PURE__ */ new Set();
3595
2940
  try {
3596
- const out = execFileSync3("tmux", ["list-panes", "-t", `agt-${codeName}`, "-F", "#{pane_pid}"], {
2941
+ const out = execFileSync("tmux", ["list-panes", "-t", `agt-${codeName}`, "-F", "#{pane_pid}"], {
3597
2942
  encoding: "utf-8",
3598
2943
  timeout: 2e3,
3599
2944
  stdio: ["ignore", "pipe", "ignore"]
@@ -3613,7 +2958,7 @@ async function sweepChannelProcesses(opts) {
3613
2958
  const kill = opts.killFn ?? defaultKill;
3614
2959
  let psOutput = "";
3615
2960
  try {
3616
- psOutput = execFileSync3("ps", ["eww", "-o", "pid=,ppid=,etime=,command="], {
2961
+ psOutput = execFileSync("ps", ["eww", "-o", "pid=,ppid=,etime=,command="], {
3617
2962
  encoding: "utf-8",
3618
2963
  timeout: 5e3,
3619
2964
  maxBuffer: 10 * 1024 * 1024
@@ -3650,7 +2995,7 @@ function defaultKillSignal(pid, signal) {
3650
2995
  }
3651
2996
  }
3652
2997
  function defaultPs() {
3653
- return execFileSync3("ps", ["eww", "-o", "pid=,ppid=,etime=,command="], {
2998
+ return execFileSync("ps", ["eww", "-o", "pid=,ppid=,etime=,command="], {
3654
2999
  encoding: "utf-8",
3655
3000
  timeout: 5e3,
3656
3001
  maxBuffer: 10 * 1024 * 1024
@@ -6616,65 +5961,13 @@ async function runAgentConnectivityProbes(agent, integrations, projectDir) {
6616
5961
  }
6617
5962
  async function runAgentSessionToolBindProbes(agent, integrations, projectDir) {
6618
5963
  if (integrations.length === 0) return;
6619
- let mcpJson = null;
6620
- try {
6621
- mcpJson = JSON.parse(readFileSync14(join16(projectDir, ".mcp.json"), "utf-8"));
6622
- } catch {
6623
- return;
6624
- }
6625
- if (!mcpJson?.mcpServers || Object.keys(mcpJson.mcpServers).length === 0) return;
6626
- const isolated = isolationMode(agent.code_name) === "docker";
6627
- let missingStdioKeys = /* @__PURE__ */ new Set();
6628
- try {
6629
- const psOutput = isolated ? syncExecFile("docker", ["exec", `agt-${agent.code_name}`, "ps", "-eo", "pid,ppid,args"], {
6630
- encoding: "utf-8",
6631
- timeout: 8e3
6632
- }) : syncExecFile("ps", ["-eo", "pid,ppid,args"], { encoding: "utf-8", timeout: 5e3 });
6633
- missingStdioKeys = new Set(
6634
- findMissingMcpServers({
6635
- rows: parsePsRows(psOutput),
6636
- codeName: agent.code_name,
6637
- mcpJson,
6638
- inContainer: isolated
6639
- })
6640
- );
6641
- } catch {
6642
- }
6643
- const probeEnv = buildProbeEnv(projectDir);
6644
- const probeDeps = buildConnectivityProbeDeps(projectDir, probeEnv);
6645
- const probeHttp = async (serverKey) => {
6646
- if (!probeDeps.mcpProbe) return void 0;
6647
- const outcome = await probeDeps.mcpProbe({ serverKey, definitionId: serverKey });
6648
- if (outcome.status === "ok") return true;
6649
- if (outcome.status === "down") return false;
6650
- return void 0;
6651
- };
6652
- let httpSessionLoaded = false;
6653
- try {
6654
- const sessionStartedMs = getSessionState(agent.code_name)?.startedAt ?? null;
6655
- const mcpMtimeMs = statSync4(join16(projectDir, ".mcp.json")).mtimeMs;
6656
- httpSessionLoaded = sessionStartedMs != null && sessionStartedMs >= mcpMtimeMs;
6657
- } catch {
6658
- }
6659
5964
  const intervalSec = Number(process.env.AGT_CONNECTIVITY_PROBE_INTERVAL_SECONDS) || 3600;
6660
- const result = await runSessionToolBindProbes(
6661
- integrations.map((i) => ({
6662
- id: i.id,
6663
- definition_id: i.definition_id,
6664
- scope: i.scope,
6665
- last_session_tool_bind_at: i.last_session_tool_bind_at ?? null,
6666
- // Server-key hint for managed/remote-MCP kinds; the runner also tries the
6667
- // raw + sanitised definition_id against the real `.mcp.json` keys.
6668
- mcp_server_key: deriveMcpServerKey({
6669
- definitionId: i.definition_id,
6670
- sourceType: i.source_type ?? null,
6671
- authType: i.auth_type,
6672
- connectivityTest: i.connectivity_test ?? null
6673
- })
6674
- })),
6675
- { mcpJson, missingStdioKeys, probeHttp, httpSessionLoaded, intervalMs: intervalSec * 1e3 }
6676
- );
6677
- 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) {
6678
5971
  await api.post("/host/session-tool-bind", {
6679
5972
  agent_id: agent.agent_id,
6680
5973
  results: result.reports
@@ -6866,10 +6159,10 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
6866
6159
  var lastVersionCheckAt = 0;
6867
6160
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
6868
6161
  var lastResponsivenessProbeAt = 0;
6869
- var agtCliVersion = true ? "0.28.207" : "dev";
6870
- function resolveBrewPath(execFileSync4) {
6162
+ var agtCliVersion = true ? "0.28.209" : "dev";
6163
+ function resolveBrewPath(execFileSync2) {
6871
6164
  try {
6872
- const out = execFileSync4("which", ["brew"], { timeout: 5e3 }).toString().trim();
6165
+ const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
6873
6166
  if (out) return out;
6874
6167
  } catch {
6875
6168
  }
@@ -6883,7 +6176,7 @@ function resolveBrewPath(execFileSync4) {
6883
6176
  }
6884
6177
  return null;
6885
6178
  }
6886
- function claudeBinaryInstalled(execFileSync4) {
6179
+ function claudeBinaryInstalled(execFileSync2) {
6887
6180
  const canonical = [
6888
6181
  "/home/linuxbrew/.linuxbrew/bin/claude",
6889
6182
  "/opt/homebrew/bin/claude",
@@ -6891,7 +6184,7 @@ function claudeBinaryInstalled(execFileSync4) {
6891
6184
  ];
6892
6185
  if (canonical.some((path) => existsSync8(path))) return true;
6893
6186
  try {
6894
- execFileSync4("which", ["claude"], { timeout: 5e3 });
6187
+ execFileSync2("which", ["claude"], { timeout: 5e3 });
6895
6188
  return true;
6896
6189
  } catch {
6897
6190
  return false;
@@ -6925,9 +6218,9 @@ async function ensureToolkitCli(toolkitSlug) {
6925
6218
  }
6926
6219
  const { binary, installer, package: pkg, script } = integration.cli_tool;
6927
6220
  const resolvedInstaller = installer ?? "manual";
6928
- const { execFileSync: execFileSync4, execSync } = await import("child_process");
6221
+ const { execFileSync: execFileSync2, execSync } = await import("child_process");
6929
6222
  try {
6930
- execFileSync4("which", [binary], { timeout: 5e3, stdio: "pipe" });
6223
+ execFileSync2("which", [binary], { timeout: 5e3, stdio: "pipe" });
6931
6224
  toolkitCliEnsured.add(toolkitSlug);
6932
6225
  toolkitCliRetryAfter.delete(toolkitSlug);
6933
6226
  toolkitCliFailureCount.delete(toolkitSlug);
@@ -6948,14 +6241,14 @@ async function ensureToolkitCli(toolkitSlug) {
6948
6241
  return;
6949
6242
  }
6950
6243
  log(`[toolkit-install] ${toolkitSlug}: installing via npm (${pkg})\u2026`);
6951
- execFileSync4("npm", ["install", "-g", pkg], { timeout: 18e4, stdio: "pipe" });
6244
+ execFileSync2("npm", ["install", "-g", pkg], { timeout: 18e4, stdio: "pipe" });
6952
6245
  } else if (resolvedInstaller === "brew") {
6953
6246
  if (!pkg) {
6954
6247
  log(`[toolkit-install] ${toolkitSlug}: installer=brew but no package declared`);
6955
6248
  toolkitCliEnsured.add(toolkitSlug);
6956
6249
  return;
6957
6250
  }
6958
- const brewPath = resolveBrewPath(execFileSync4);
6251
+ const brewPath = resolveBrewPath(execFileSync2);
6959
6252
  if (!brewPath) {
6960
6253
  log(`[toolkit-install] ${toolkitSlug}: installer=brew but Homebrew not available \u2014 install manually: brew install ${pkg}`);
6961
6254
  toolkitCliEnsured.add(toolkitSlug);
@@ -6965,9 +6258,9 @@ async function ensureToolkitCli(toolkitSlug) {
6965
6258
  const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
6966
6259
  log(`[toolkit-install] ${toolkitSlug}: installing via brew (${pkg})\u2026`);
6967
6260
  if (isRoot) {
6968
- execFileSync4("sudo", ["-u", "ec2-user", "-H", brewPath, "install", pkg], { timeout: 18e4, stdio: "pipe", cwd: "/tmp" });
6261
+ execFileSync2("sudo", ["-u", "ec2-user", "-H", brewPath, "install", pkg], { timeout: 18e4, stdio: "pipe", cwd: "/tmp" });
6969
6262
  } else {
6970
- execFileSync4(brewPath, ["install", pkg], { timeout: 18e4, stdio: "pipe" });
6263
+ execFileSync2(brewPath, ["install", pkg], { timeout: 18e4, stdio: "pipe" });
6971
6264
  }
6972
6265
  } else if (resolvedInstaller === "script") {
6973
6266
  if (!script) {
@@ -6987,7 +6280,7 @@ async function ensureToolkitCli(toolkitSlug) {
6987
6280
  process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
6988
6281
  }
6989
6282
  try {
6990
- execFileSync4("which", [binary], { timeout: 5e3, stdio: "pipe" });
6283
+ execFileSync2("which", [binary], { timeout: 5e3, stdio: "pipe" });
6991
6284
  log(`[toolkit-install] ${toolkitSlug}: installed \u2014 ${binary} now on PATH`);
6992
6285
  toolkitCliEnsured.add(toolkitSlug);
6993
6286
  toolkitCliRetryAfter.delete(toolkitSlug);
@@ -7074,8 +6367,8 @@ async function ensureFrameworkBinary(frameworkId) {
7074
6367
  if (frameworkBinaryChecked.has(frameworkId)) return;
7075
6368
  frameworkBinaryChecked.add(frameworkId);
7076
6369
  lastManagedSettingsStatus = ensureClaudeManagedSettings();
7077
- const { execFileSync: execFileSync4 } = await import("child_process");
7078
- const brewPath = resolveBrewPath(execFileSync4);
6370
+ const { execFileSync: execFileSync2 } = await import("child_process");
6371
+ const brewPath = resolveBrewPath(execFileSync2);
7079
6372
  if (!brewPath) {
7080
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");
7081
6374
  return;
@@ -7088,7 +6381,7 @@ async function ensureFrameworkBinary(frameworkId) {
7088
6381
  }
7089
6382
  return runAsync(brewPath, args, opts);
7090
6383
  };
7091
- const claudeExists = claudeBinaryInstalled(execFileSync4);
6384
+ const claudeExists = claudeBinaryInstalled(execFileSync2);
7092
6385
  if (!claudeExists) {
7093
6386
  log(`Claude Code binary not found \u2014 installing via Homebrew${isRoot ? " (as ec2-user via sudo)" : ""}...`);
7094
6387
  try {
@@ -7123,13 +6416,13 @@ async function maybeUpgradeClaudeCode() {
7123
6416
  }
7124
6417
  claudeCodeUpgradeInFlight = true;
7125
6418
  stampClaudeCodeUpgradeMarker();
7126
- const { execFileSync: execFileSync4 } = await import("child_process");
7127
- const brewPath = resolveBrewPath(execFileSync4);
6419
+ const { execFileSync: execFileSync2 } = await import("child_process");
6420
+ const brewPath = resolveBrewPath(execFileSync2);
7128
6421
  if (!brewPath) {
7129
6422
  claudeCodeUpgradeInFlight = false;
7130
6423
  return;
7131
6424
  }
7132
- if (!claudeBinaryInstalled(execFileSync4)) {
6425
+ if (!claudeBinaryInstalled(execFileSync2)) {
7133
6426
  claudeCodeUpgradeInFlight = false;
7134
6427
  return;
7135
6428
  }
@@ -7208,18 +6501,18 @@ async function checkAndUpdateCli(opts) {
7208
6501
  }
7209
6502
  }
7210
6503
  async function checkAndUpdateCliViaBrew(force = false) {
7211
- const { execFileSync: execFileSync4 } = await import("child_process");
7212
- const brewPath = resolveBrewPath(execFileSync4);
6504
+ const { execFileSync: execFileSync2 } = await import("child_process");
6505
+ const brewPath = resolveBrewPath(execFileSync2);
7213
6506
  if (!brewPath) return "noop";
7214
6507
  let metadataFresh = true;
7215
6508
  try {
7216
- execFileSync4(brewPath, ["update", "--quiet"], { timeout: 6e4, stdio: "pipe" });
6509
+ execFileSync2(brewPath, ["update", "--quiet"], { timeout: 6e4, stdio: "pipe" });
7217
6510
  } catch (err) {
7218
6511
  metadataFresh = false;
7219
6512
  log(`[self-update] brew update failed (continuing with stale cache): ${err.message}`);
7220
6513
  }
7221
6514
  try {
7222
- const outdated = execFileSync4(brewPath, ["outdated", "--json=v2"], {
6515
+ const outdated = execFileSync2(brewPath, ["outdated", "--json=v2"], {
7223
6516
  timeout: 3e4,
7224
6517
  encoding: "utf-8"
7225
6518
  });
@@ -7252,7 +6545,7 @@ async function checkAndUpdateCliViaBrew(force = false) {
7252
6545
  }
7253
6546
  log(`[self-update] agt CLI update available: ${installed} \u2192 ${latest}. Upgrading via brew${force ? " (forced \u2014 Update CLI now)" : ""}...`);
7254
6547
  try {
7255
- execFileSync4(brewPath, ["upgrade", "integrity-labs/tap/agt"], {
6548
+ execFileSync2(brewPath, ["upgrade", "integrity-labs/tap/agt"], {
7256
6549
  timeout: 12e4,
7257
6550
  stdio: "pipe"
7258
6551
  });
@@ -7371,7 +6664,7 @@ async function fetchUrgentDistTagVersion() {
7371
6664
  }
7372
6665
  }
7373
6666
  async function installAgtCliViaNpm(version, reasonLabel) {
7374
- const { execFileSync: execFileSync4 } = await import("child_process");
6667
+ const { execFileSync: execFileSync2 } = await import("child_process");
7375
6668
  const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
7376
6669
  const cmd = isRoot ? "npm" : "sudo";
7377
6670
  const installArgs = [
@@ -7382,14 +6675,14 @@ async function installAgtCliViaNpm(version, reasonLabel) {
7382
6675
  ];
7383
6676
  const args = isRoot ? installArgs : ["-n", "npm", ...installArgs];
7384
6677
  try {
7385
- execFileSync4(cmd, args, { timeout: 18e4, stdio: "pipe" });
6678
+ execFileSync2(cmd, args, { timeout: 18e4, stdio: "pipe" });
7386
6679
  } catch (err) {
7387
6680
  log(`[self-update] npm upgrade failed: ${err.message}`);
7388
6681
  return "failed";
7389
6682
  }
7390
6683
  let smokeOk = false;
7391
6684
  try {
7392
- const out = execFileSync4("agt", ["--version"], { timeout: 15e3, stdio: "pipe" }).toString();
6685
+ const out = execFileSync2("agt", ["--version"], { timeout: 15e3, stdio: "pipe" }).toString();
7393
6686
  smokeOk = isAgtVersionSmokeOk(out, version);
7394
6687
  if (!smokeOk) {
7395
6688
  log(
@@ -7775,7 +7068,7 @@ async function pollCycle() {
7775
7068
  }
7776
7069
  try {
7777
7070
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
7778
- const { collectDiagnostics } = await import("../persistent-session-6662WBSH.js");
7071
+ const { collectDiagnostics } = await import("../persistent-session-Z46SCCQM.js");
7779
7072
  const diagCodeNames = [...agentState.persistentSessionAgents];
7780
7073
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
7781
7074
  let tailscaleHostname;
@@ -7923,7 +7216,7 @@ async function pollCycle() {
7923
7216
  const {
7924
7217
  collectResponsivenessProbes,
7925
7218
  getResponsivenessIntervalMs
7926
- } = await import("../responsiveness-probe-TOLTTG6I.js");
7219
+ } = await import("../responsiveness-probe-4MUX7FBH.js");
7927
7220
  const probeIntervalMs = getResponsivenessIntervalMs();
7928
7221
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
7929
7222
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -7955,7 +7248,7 @@ async function pollCycle() {
7955
7248
  collectResponsivenessProbes,
7956
7249
  livePendingInboundOldestAgeSeconds,
7957
7250
  parkPendingInbound
7958
- } = await import("../responsiveness-probe-TOLTTG6I.js");
7251
+ } = await import("../responsiveness-probe-4MUX7FBH.js");
7959
7252
  const { getProjectDir: wedgeProjectDir } = await import("../claude-scheduler-FATCLHDM.js");
7960
7253
  const wedgeNow = /* @__PURE__ */ new Date();
7961
7254
  const liveAgents = agentState.persistentSessionAgents;
@@ -9283,7 +8576,7 @@ async function processAgent(agent, agentStates) {
9283
8576
  }
9284
8577
  try {
9285
8578
  const integrationsData = await api.post("/host/agent-integrations", { agent_id: agent.agent_id });
9286
- const integrations = integrationsData.integrations ?? [];
8579
+ const integrations = (integrationsData.integrations ?? []).map(withResolvedRemoteMcp);
9287
8580
  for (const integration of integrations) {
9288
8581
  if (integration.auth_type !== "oauth2") continue;
9289
8582
  const expiresAt = integration.credentials?.token_expires_at;
@@ -10982,7 +10275,7 @@ async function processClaudePairSessions(agents) {
10982
10275
  killPairSession,
10983
10276
  pairTmuxSession,
10984
10277
  finalizeClaudePairOnboarding
10985
- } = await import("../claude-pair-runtime-Q4Q2OTTV.js");
10278
+ } = await import("../claude-pair-runtime-UUTPSE2V.js");
10986
10279
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
10987
10280
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
10988
10281
  const killed = await killPairSession(pairTmuxSession(pairId));