@triflux/core 10.48.1 → 10.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/hooks/agy-session-hook.mjs +1 -1
  2. package/hooks/claude-cwd-projection-refresh.mjs +7 -79
  3. package/hooks/codex-session-hook.mjs +3 -8
  4. package/hooks/hook-orchestrator.mjs +8 -648
  5. package/hooks/lib/resolve-root.mjs +1 -1
  6. package/hub/bridge.mjs +290 -51
  7. package/hub/team/claude-daemon-control.mjs +54 -6
  8. package/hub/team/claude-transcript.mjs +240 -0
  9. package/hub/team/session-context.mjs +203 -0
  10. package/hud/constants.mjs +10 -0
  11. package/hud/hud-qos-status.mjs +23 -4
  12. package/hud/providers/gemini.mjs +4 -1
  13. package/hud/renderers.mjs +10 -6
  14. package/package.json +2 -2
  15. package/scripts/lib/doctor-env-checks.mjs +0 -85
  16. package/scripts/lib/legacy-hook-cleanup.mjs +230 -0
  17. package/scripts/lib/legacy-mcp-cleanup.mjs +772 -0
  18. package/scripts/lib/mcp-guard-engine.mjs +44 -271
  19. package/scripts/lib/mcp-manifest.mjs +2 -2
  20. package/scripts/lib/session-presence.mjs +249 -0
  21. package/hooks/agent-route-guard.mjs +0 -113
  22. package/hooks/cross-review-tracker.mjs +0 -135
  23. package/hooks/cto-north-star-brief.mjs +0 -133
  24. package/hooks/error-context.mjs +0 -185
  25. package/hooks/hook-adaptive-collector.mjs +0 -88
  26. package/hooks/hook-manager.mjs +0 -426
  27. package/hooks/hook-registry.json +0 -367
  28. package/hooks/hooks.json +0 -149
  29. package/hooks/keyword-rules.json +0 -878
  30. package/hooks/mcp-config-watcher.mjs +0 -84
  31. package/hooks/permission-safe-allow.mjs +0 -267
  32. package/hooks/pipeline-stop.mjs +0 -435
  33. package/hooks/post-tool-tips.mjs +0 -306
  34. package/hooks/pre-compact-snapshot.mjs +0 -181
  35. package/hooks/safety-guard.mjs +0 -466
  36. package/hooks/session-end-cleanup.mjs +0 -360
  37. package/hooks/session-start-fast.mjs +0 -579
  38. package/hooks/session-start-lake.mjs +0 -81
  39. package/hooks/subagent-tracker.mjs +0 -281
  40. package/hooks/subagent-verifier.mjs +0 -85
  41. package/scripts/lib/cross-review-utils.mjs +0 -59
  42. package/scripts/lib/hook-utils.mjs +0 -16
  43. package/scripts/lib/keyword-rules.mjs +0 -219
  44. package/scripts/lib/mcp-gateway-health-check.mjs +0 -224
  45. package/scripts/lib/mcp-gateway-servers.mjs +0 -70
  46. package/scripts/lib/mcp-gateway-wrapper-check.mjs +0 -35
package/hub/bridge.mjs CHANGED
@@ -1317,7 +1317,8 @@ function daemonAttachErrorResult(error) {
1317
1317
  matchedCompletion: false,
1318
1318
  timedOut: false,
1319
1319
  closed: false,
1320
- inputSent: error?.inputSent === true,
1320
+ inputSent: error?.inputSent ?? null,
1321
+ status: error?.inputSent === false ? "failed" : "unknown",
1321
1322
  error: error?.message || String(error),
1322
1323
  };
1323
1324
  }
@@ -1388,87 +1389,323 @@ async function cmdDaemonProbe(args) {
1388
1389
  try {
1389
1390
  const payload = readBridgePayload(args);
1390
1391
  const { probeClaudeDaemonCandidates } = await loadDaemonControl();
1391
- const probe = await probeClaudeDaemonCandidates({
1392
- configDir: payload.configDir,
1393
- env: process.env,
1394
- short: payload.short,
1395
- sessionId: payload.sessionId,
1396
- timeoutMs: numericOption(payload.timeoutMs, 6000),
1397
- tmpRoot: payload.tmpRoot,
1398
- });
1399
- return emitJson(probe);
1392
+ const { probe, recoveredFrom } = await probeDaemonTarget(
1393
+ payload,
1394
+ probeClaudeDaemonCandidates,
1395
+ payload.includeContext === true,
1396
+ );
1397
+ return emitJson({ ...probe, ...(recoveredFrom ? { recoveredFrom } : {}) });
1400
1398
  } catch (error) {
1401
1399
  return emitJson(daemonErrorResult(error));
1402
1400
  }
1403
1401
  }
1404
1402
 
1403
+ function staleCandidate(probe) {
1404
+ return probe?.candidateResults?.find((candidate) =>
1405
+ ["daemon-dir-missing", "stale-control-socket"].includes(
1406
+ candidate.errorCode,
1407
+ ),
1408
+ );
1409
+ }
1410
+
1411
+ function daemonProbeOptions(payload, includeContext = false) {
1412
+ return {
1413
+ configDir: payload.configDir,
1414
+ env: process.env,
1415
+ short: payload.short,
1416
+ sessionId: payload.sessionId,
1417
+ timeoutMs: numericOption(payload.timeoutMs, 6000),
1418
+ tmpRoot: payload.tmpRoot,
1419
+ includeContext,
1420
+ };
1421
+ }
1422
+
1423
+ async function probeDaemonTarget(
1424
+ payload,
1425
+ probeCandidates,
1426
+ includeContext = false,
1427
+ ) {
1428
+ const options = daemonProbeOptions(payload, includeContext);
1429
+ const probe = await probeCandidates(options);
1430
+ const stale = staleCandidate(probe);
1431
+ if (probe.ok || !(payload.short || payload.sessionId) || !stale) {
1432
+ return { probe, recoveredFrom: null };
1433
+ }
1434
+ const retry = await probeCandidates({
1435
+ ...options,
1436
+ candidateSourceConfigDir: stale.sourceConfigDir ?? stale.configDir,
1437
+ });
1438
+ return {
1439
+ probe: retry,
1440
+ recoveredFrom: retry.ok ? stale : null,
1441
+ };
1442
+ }
1443
+
1405
1444
  async function cmdDaemonAttach(args) {
1406
1445
  try {
1407
1446
  const payload = readBridgePayload(args);
1408
1447
  if (!payload.prompt) throw new Error("prompt is required");
1409
-
1410
1448
  const {
1411
1449
  attachClaudeDaemonSession,
1412
1450
  buildDaemonControlAuth,
1413
1451
  probeClaudeDaemonCandidates,
1414
1452
  } = await loadDaemonControl();
1415
- const probe = await probeClaudeDaemonCandidates({
1416
- configDir: payload.configDir,
1417
- env: process.env,
1418
- short: payload.short,
1419
- sessionId: payload.sessionId,
1420
- timeoutMs: numericOption(payload.timeoutMs, 6000),
1421
- tmpRoot: payload.tmpRoot,
1422
- });
1453
+ let { probe, recoveredFrom } = await probeDaemonTarget(
1454
+ payload,
1455
+ probeClaudeDaemonCandidates,
1456
+ false,
1457
+ );
1423
1458
  if (!probe.ok) return emitJson(daemonAttachProbeFailureResult(probe));
1424
- const short = payload.short ?? probe.target?.short;
1425
- if (!short) {
1426
- return emitJson(
1427
- daemonAttachProbeFailureResult({
1428
- ...probe,
1429
- ok: false,
1430
- reason: "target-not-found",
1431
- error: "short or resolvable sessionId is required",
1432
- }),
1433
- );
1434
- }
1435
- const controlAuth = await buildDaemonControlAuth(
1436
- probe.daemon?.configDir ?? payload.configDir,
1459
+ const { findClaudeTranscript, readClaudeTranscript } = await import(
1460
+ "./team/claude-transcript.mjs"
1461
+ );
1462
+ const { contextGuard, modelContext } = await import(
1463
+ "./team/session-context.mjs"
1437
1464
  );
1438
-
1439
1465
  let result;
1440
- try {
1441
- result = await attachClaudeDaemonSession({
1442
- controlSock: probe.controlSock,
1443
- short,
1444
- input: payload.prompt,
1445
- ...controlAuth,
1446
- cols: numericOption(payload.cols, undefined),
1447
- rows: numericOption(payload.rows, undefined),
1448
- timeoutMs: numericOption(payload.timeoutMs, 30_000),
1466
+ let context = modelContext("claude", null);
1467
+ let guard = {};
1468
+ for (let attempt = 0; attempt < 2; attempt += 1) {
1469
+ const short = payload.short ?? probe.target?.short;
1470
+ if (!short) {
1471
+ return emitJson(
1472
+ daemonAttachProbeFailureResult({
1473
+ ...probe,
1474
+ reason: "target-not-found",
1475
+ error: "short or resolvable sessionId is required",
1476
+ }),
1477
+ );
1478
+ }
1479
+ const transcript = await findClaudeTranscript({
1480
+ configDir: probe.daemon?.configDir,
1481
+ sourceConfigDir: probe.daemon?.sourceConfigDir,
1482
+ sessionId: probe.target?.sessionId,
1483
+ cwd: probe.target?.cwd,
1449
1484
  });
1450
- } catch (error) {
1451
- return emitJson({
1452
- ...daemonAttachErrorResult(error),
1453
- ...daemonProbeMetadata(probe),
1485
+ context =
1486
+ (await readClaudeTranscript(transcript).catch(() => null))?.context ??
1487
+ modelContext("claude", null);
1488
+ guard = contextGuard("claude", context, {
1489
+ warnContextPct: payload.warnContextPct,
1490
+ maxContextPct: payload.maxContextPct,
1454
1491
  });
1492
+ if (guard.ok === false) {
1493
+ return emitJson({
1494
+ ...guard,
1495
+ ...(recoveredFrom ? { recoveredFrom } : {}),
1496
+ ...daemonProbeMetadata(probe),
1497
+ });
1498
+ }
1499
+ const controlAuth = await buildDaemonControlAuth(
1500
+ probe.daemon?.configDir ?? payload.configDir,
1501
+ );
1502
+ try {
1503
+ result = await attachClaudeDaemonSession({
1504
+ controlSock: probe.controlSock,
1505
+ short,
1506
+ input: payload.prompt,
1507
+ ...controlAuth,
1508
+ cols: numericOption(payload.cols, undefined),
1509
+ rows: numericOption(payload.rows, undefined),
1510
+ timeoutMs: numericOption(payload.timeoutMs, 30_000),
1511
+ noWait: payload.noWait === true,
1512
+ });
1513
+ break;
1514
+ } catch (error) {
1515
+ if (
1516
+ attempt === 1 ||
1517
+ error?.code !== "ENOENT" ||
1518
+ error?.inputSent !== false
1519
+ ) {
1520
+ return emitJson({
1521
+ ...daemonAttachErrorResult(error),
1522
+ ...(recoveredFrom ? { recoveredFrom } : {}),
1523
+ ...daemonProbeMetadata(probe),
1524
+ });
1525
+ }
1526
+ const stale = probe.daemon;
1527
+ const recovered = await probeClaudeDaemonCandidates({
1528
+ ...daemonProbeOptions(payload),
1529
+ candidateSourceConfigDir: stale.sourceConfigDir ?? stale.configDir,
1530
+ });
1531
+ if (!recovered.ok) {
1532
+ return emitJson({
1533
+ ...daemonAttachErrorResult(error),
1534
+ ...daemonProbeMetadata(recovered),
1535
+ });
1536
+ }
1537
+ probe = recovered;
1538
+ recoveredFrom = stale;
1539
+ }
1455
1540
  }
1456
1541
 
1457
1542
  return emitJson({
1458
- ok: result.matchedCompletion === true,
1543
+ ok:
1544
+ payload.noWait === true
1545
+ ? result.inputSent === true
1546
+ : result.matchedCompletion === true,
1547
+ ...(payload.noWait === true
1548
+ ? {
1549
+ status:
1550
+ result.inputSent === true
1551
+ ? "submitted"
1552
+ : result.inputSent === false
1553
+ ? "failed"
1554
+ : "unknown",
1555
+ done: false,
1556
+ ...(result.inputSent === true
1557
+ ? { submittedAt: new Date().toISOString() }
1558
+ : {}),
1559
+ target: probe.target,
1560
+ }
1561
+ : {}),
1459
1562
  text: result.text,
1460
1563
  raw: result.streamText,
1461
1564
  responseRaw: result.responseStreamText,
1462
1565
  matchedCompletion: result.matchedCompletion === true,
1463
- timedOut: result.timedOut === true,
1566
+ ...(payload.noWait === true
1567
+ ? {}
1568
+ : { timedOut: result.timedOut === true }),
1464
1569
  closed: result.closed === true,
1465
- inputSent: result.inputSent === true,
1570
+ inputSent: result.inputSent ?? null,
1466
1571
  error:
1467
1572
  result.handshake?.ok === false ? result.handshake?.error : undefined,
1573
+ ...context,
1574
+ ...guard,
1575
+ ...(recoveredFrom ? { recoveredFrom } : {}),
1468
1576
  ...daemonProbeMetadata(probe),
1469
1577
  });
1470
1578
  } catch (error) {
1471
- return emitJson(daemonAttachErrorResult(error));
1579
+ return emitJson(
1580
+ daemonAttachErrorResult({
1581
+ inputSent: false,
1582
+ message: error?.message || String(error),
1583
+ }),
1584
+ );
1585
+ }
1586
+ }
1587
+
1588
+ async function cmdDaemonWait(args) {
1589
+ let requestId = null;
1590
+ const emptyContext = {
1591
+ estimatedContextTokens: null,
1592
+ contextLimitTokens: null,
1593
+ contextLimitSource: null,
1594
+ contextPct: null,
1595
+ };
1596
+ try {
1597
+ const payload = readBridgePayload(args);
1598
+ requestId = payload.requestId ?? null;
1599
+ if (!payload.short && !payload.sessionId)
1600
+ throw new Error("short or sessionId is required");
1601
+ const { probeClaudeDaemonCandidates } = await loadDaemonControl();
1602
+ const { findClaudeTranscript, readClaudeTranscript } = await import(
1603
+ "./team/claude-transcript.mjs"
1604
+ );
1605
+ const timeoutMs = numericOption(payload.timeoutMs, 30_000);
1606
+ const pollIntervalMs = Math.max(
1607
+ 10,
1608
+ numericOption(payload.pollIntervalMs, 500),
1609
+ );
1610
+ const deadline = Date.now() + timeoutMs;
1611
+ let last = null;
1612
+ do {
1613
+ const probe = await probeClaudeDaemonCandidates({
1614
+ configDir: payload.configDir,
1615
+ env: process.env,
1616
+ short: payload.short,
1617
+ sessionId: payload.sessionId,
1618
+ timeoutMs: Math.max(1, Math.min(6000, deadline - Date.now())),
1619
+ tmpRoot: payload.tmpRoot,
1620
+ includeContext: false,
1621
+ });
1622
+ if (!probe.ok) {
1623
+ if (Date.now() >= deadline) {
1624
+ return emitJson({
1625
+ ...(last ?? emptyContext),
1626
+ ok: true,
1627
+ status: "working",
1628
+ done: false,
1629
+ timedOut: true,
1630
+ requestId,
1631
+ });
1632
+ }
1633
+ return emitJson({
1634
+ ok: false,
1635
+ status: "unknown",
1636
+ done: false,
1637
+ timedOut: false,
1638
+ ...emptyContext,
1639
+ requestId,
1640
+ ...daemonProbeMetadata(probe),
1641
+ error: probe.error || probe.reason,
1642
+ });
1643
+ }
1644
+ const transcriptPath = await findClaudeTranscript({
1645
+ configDir: probe.daemon?.configDir,
1646
+ sourceConfigDir: probe.daemon?.sourceConfigDir,
1647
+ sessionId: probe.target?.sessionId,
1648
+ cwd: probe.target?.cwd,
1649
+ });
1650
+ const transcript = await readClaudeTranscript(transcriptPath, {
1651
+ requestId: payload.requestId,
1652
+ });
1653
+ const context = transcript?.context ?? emptyContext;
1654
+ const idle = [
1655
+ probe.target?.state,
1656
+ probe.target?.status,
1657
+ probe.target?.tempo,
1658
+ ].some((value) =>
1659
+ ["idle", "done", "ready"].includes(String(value || "").toLowerCase()),
1660
+ );
1661
+ last = {
1662
+ ok: true,
1663
+ status: transcript?.userSeen ? "working" : "submitted",
1664
+ done: false,
1665
+ timedOut: false,
1666
+ response: transcript?.response || "",
1667
+ ...context,
1668
+ requestId,
1669
+ target: probe.target,
1670
+ ...daemonProbeMetadata(probe),
1671
+ };
1672
+ if (transcript?.error) {
1673
+ return emitJson({
1674
+ ...last,
1675
+ ok: false,
1676
+ status: "failed",
1677
+ error: transcript.error,
1678
+ });
1679
+ }
1680
+ if (
1681
+ (idle || transcript?.sectionClosed) &&
1682
+ transcript?.userSeen &&
1683
+ transcript.turnEnded
1684
+ ) {
1685
+ if (!transcript.response.trim())
1686
+ return emitJson({
1687
+ ...last,
1688
+ ok: false,
1689
+ status: "failed",
1690
+ error: "turn ended without assistant text",
1691
+ });
1692
+ return emitJson({ ...last, status: "completed", done: true });
1693
+ }
1694
+ if (Date.now() >= deadline) break;
1695
+ await new Promise((resolve) =>
1696
+ setTimeout(resolve, Math.min(pollIntervalMs, deadline - Date.now())),
1697
+ );
1698
+ } while (Date.now() < deadline);
1699
+ return emitJson({ ...last, status: "working", timedOut: true });
1700
+ } catch (error) {
1701
+ return emitJson({
1702
+ ...daemonErrorResult(error),
1703
+ status: "unknown",
1704
+ done: false,
1705
+ timedOut: false,
1706
+ ...emptyContext,
1707
+ requestId,
1708
+ });
1472
1709
  }
1473
1710
  }
1474
1711
 
@@ -1807,6 +2044,8 @@ export async function main(argv = process.argv.slice(2)) {
1807
2044
  return await cmdDaemonProbe(args);
1808
2045
  case "daemon-attach":
1809
2046
  return await cmdDaemonAttach(args);
2047
+ case "daemon-wait":
2048
+ return await cmdDaemonWait(args);
1810
2049
  case "daemon-interrupt":
1811
2050
  return await cmdDaemonInterrupt(args);
1812
2051
  case "retry-run":
@@ -1817,7 +2056,7 @@ export async function main(argv = process.argv.slice(2)) {
1817
2056
  return await cmdInterveneRun(args);
1818
2057
  default:
1819
2058
  console.error(
1820
- "사용법: bridge.mjs <register|heartbeat|result|control|handoff|publish|takeover-role|send-input|context|deregister|assign-async|assign-result|assign-status|assign-retry|team-info|team-task-list|team-task-update|team-send-message|pipeline-state|pipeline-advance|pipeline-init|pipeline-list|ping|delegator-delegate|delegator-reply|delegator-status|hitl-request|hitl-submit|hitl-pending|daemon-probe|daemon-attach|daemon-interrupt|retry-run|retry-status|intervene-run> [--옵션]",
2059
+ "사용법: bridge.mjs <register|heartbeat|result|control|handoff|publish|takeover-role|send-input|context|deregister|assign-async|assign-result|assign-status|assign-retry|team-info|team-task-list|team-task-update|team-send-message|pipeline-state|pipeline-advance|pipeline-init|pipeline-list|ping|delegator-delegate|delegator-reply|delegator-status|hitl-request|hitl-submit|hitl-pending|daemon-probe|daemon-attach|daemon-wait|daemon-interrupt|retry-run|retry-status|intervene-run> [--옵션]",
1821
2060
  );
1822
2061
  process.exit(1);
1823
2062
  }
@@ -13,6 +13,10 @@ import {
13
13
  removeClaudeSessionProjection,
14
14
  writeClaudeSessionProjection,
15
15
  } from "./claude-session-projection.mjs";
16
+ import {
17
+ findClaudeTranscript,
18
+ readClaudeTranscript,
19
+ } from "./claude-transcript.mjs";
16
20
 
17
21
  export function resolveClaudeConfigDir(env = process.env) {
18
22
  if (env.CLAUDE_CONFIG_DIR) return path.resolve(env.CLAUDE_CONFIG_DIR);
@@ -223,6 +227,21 @@ function sessionsFromDaemonList(listResponse) {
223
227
  return extractClaudeAgentSessions(listResponse);
224
228
  }
225
229
 
230
+ async function addSessionContexts(sessions, candidate) {
231
+ return await Promise.all(
232
+ sessions.map(async (session) => {
233
+ const transcript = await findClaudeTranscript({
234
+ configDir: candidate.configDir,
235
+ sourceConfigDir: candidate.sourceConfigDir,
236
+ sessionId: session.sessionId,
237
+ cwd: session.cwd,
238
+ });
239
+ const snapshot = await readClaudeTranscript(transcript).catch(() => null);
240
+ return { ...session, context: snapshot?.context ?? null };
241
+ }),
242
+ );
243
+ }
244
+
226
245
  function errorMessage(error) {
227
246
  return error?.message || String(error);
228
247
  }
@@ -322,12 +341,21 @@ export async function probeClaudeDaemonCandidates({
322
341
  sessionId,
323
342
  timeoutMs = 6000,
324
343
  tmpRoot = "/tmp",
344
+ includeContext = false,
345
+ candidateSourceConfigDir,
325
346
  } = {}) {
326
- const candidates = await buildClaudeDaemonDiscoveryCandidates({
347
+ const discovered = await buildClaudeDaemonDiscoveryCandidates({
327
348
  configDir,
328
349
  env,
329
350
  tmpRoot,
330
351
  });
352
+ const candidates = candidateSourceConfigDir
353
+ ? discovered.filter(
354
+ (candidate) =>
355
+ (candidate.sourceConfigDir ?? candidate.configDir) ===
356
+ candidateSourceConfigDir,
357
+ )
358
+ : discovered;
331
359
  const callerProvenance = detectCallerProvenance(env);
332
360
  const targetRequested = Boolean(short || sessionId);
333
361
  const results = [];
@@ -339,7 +367,10 @@ export async function probeClaudeDaemonCandidates({
339
367
  { proto: 1, op: "list" },
340
368
  { timeoutMs },
341
369
  );
342
- const sessions = sessionsFromDaemonList(list);
370
+ const normalized = sessionsFromDaemonList(list);
371
+ const sessions = includeContext
372
+ ? await addSessionContexts(normalized, candidate)
373
+ : normalized;
343
374
  const ok = list?.ok !== false;
344
375
  results.push({
345
376
  ok,
@@ -1038,10 +1069,10 @@ function defaultClaudeAttachCompletionMatched(
1038
1069
  );
1039
1070
  }
1040
1071
 
1041
- function writeClaudeAttachInput(socket, input) {
1072
+ function writeClaudeAttachInput(socket, input, onSubmitted) {
1042
1073
  socket.write("\x15");
1043
1074
  socket.write(`\x1b[200~${String(input)}\x1b[201~`);
1044
- socket.write("\r");
1075
+ socket.write("\r", onSubmitted);
1045
1076
  }
1046
1077
 
1047
1078
  function requiresPostInputTransition(handshake) {
@@ -1066,6 +1097,7 @@ export function attachClaudeDaemonSession({
1066
1097
  timeoutMs = 30_000,
1067
1098
  completionQuiescenceMs = DEFAULT_ATTACH_COMPLETION_QUIESCENCE_MS,
1068
1099
  completionMatched = defaultClaudeAttachCompletionMatched,
1100
+ noWait = false,
1069
1101
  } = {}) {
1070
1102
  if (!controlSock) throw new Error("controlSock is required");
1071
1103
  return new Promise((resolve, reject) => {
@@ -1198,8 +1230,24 @@ export function attachClaudeDaemonSession({
1198
1230
  { cols },
1199
1231
  );
1200
1232
  responseStartOffset = stream.length;
1201
- writeClaudeAttachInput(socket, input);
1202
- inputSent = true;
1233
+ inputSent = noWait ? null : true;
1234
+ writeClaudeAttachInput(
1235
+ socket,
1236
+ input,
1237
+ noWait
1238
+ ? (error) => {
1239
+ if (error) fail(error);
1240
+ else {
1241
+ inputSent = true;
1242
+ finish({
1243
+ timedOut: false,
1244
+ matchedCompletion: false,
1245
+ closed: false,
1246
+ });
1247
+ }
1248
+ }
1249
+ : undefined,
1250
+ );
1203
1251
  }, initialDrainMs);
1204
1252
  }
1205
1253
  } else {