@songsid/agend 2.1.4-beta.9 → 2.1.4

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 (166) hide show
  1. package/README.md +1 -1
  2. package/README.zh-TW.md +1 -1
  3. package/dist/agent-cli-instructions.md +2 -2
  4. package/dist/agent-endpoint.js +1 -1
  5. package/dist/agent-endpoint.js.map +1 -1
  6. package/dist/backend/antigravity.d.ts +12 -8
  7. package/dist/backend/antigravity.js +189 -42
  8. package/dist/backend/antigravity.js.map +1 -1
  9. package/dist/backend/claude-code.d.ts +87 -23
  10. package/dist/backend/claude-code.js +361 -46
  11. package/dist/backend/claude-code.js.map +1 -1
  12. package/dist/backend/codex.d.ts +18 -0
  13. package/dist/backend/codex.js +139 -5
  14. package/dist/backend/codex.js.map +1 -1
  15. package/dist/backend/gemini-cli.js +2 -2
  16. package/dist/backend/gemini-cli.js.map +1 -1
  17. package/dist/backend/grok.js +2 -2
  18. package/dist/backend/grok.js.map +1 -1
  19. package/dist/backend/kiro.d.ts +52 -2
  20. package/dist/backend/kiro.js +267 -12
  21. package/dist/backend/kiro.js.map +1 -1
  22. package/dist/backend/opencode.js +17 -4
  23. package/dist/backend/opencode.js.map +1 -1
  24. package/dist/backend/types.d.ts +122 -3
  25. package/dist/backend/types.js +5 -1
  26. package/dist/backend/types.js.map +1 -1
  27. package/dist/backend-outage.d.ts +61 -0
  28. package/dist/backend-outage.js +71 -0
  29. package/dist/backend-outage.js.map +1 -0
  30. package/dist/channel/adapters/discord.d.ts +44 -2
  31. package/dist/channel/adapters/discord.js +523 -90
  32. package/dist/channel/adapters/discord.js.map +1 -1
  33. package/dist/channel/adapters/telegram.d.ts +30 -0
  34. package/dist/channel/adapters/telegram.js +221 -19
  35. package/dist/channel/adapters/telegram.js.map +1 -1
  36. package/dist/channel/agy-mcp-launcher.d.ts +2 -0
  37. package/dist/channel/agy-mcp-launcher.js +28 -0
  38. package/dist/channel/agy-mcp-launcher.js.map +1 -0
  39. package/dist/channel/ipc-bridge.d.ts +2 -1
  40. package/dist/channel/ipc-bridge.js +33 -4
  41. package/dist/channel/ipc-bridge.js.map +1 -1
  42. package/dist/channel/markdown-chunk.d.ts +42 -0
  43. package/dist/channel/markdown-chunk.js +205 -0
  44. package/dist/channel/markdown-chunk.js.map +1 -0
  45. package/dist/channel/mcp-server.js +34 -5
  46. package/dist/channel/mcp-server.js.map +1 -1
  47. package/dist/channel/mcp-tools.js +5 -3
  48. package/dist/channel/mcp-tools.js.map +1 -1
  49. package/dist/channel/message-queue.js +64 -8
  50. package/dist/channel/message-queue.js.map +1 -1
  51. package/dist/channel/types.d.ts +31 -1
  52. package/dist/classic-channel-manager.d.ts +65 -0
  53. package/dist/classic-channel-manager.js +172 -3
  54. package/dist/classic-channel-manager.js.map +1 -1
  55. package/dist/cli.js +124 -83
  56. package/dist/cli.js.map +1 -1
  57. package/dist/config-validator.js +13 -0
  58. package/dist/config-validator.js.map +1 -1
  59. package/dist/config.d.ts +4 -0
  60. package/dist/config.js +12 -1
  61. package/dist/config.js.map +1 -1
  62. package/dist/cross-instance-envelope.d.ts +6 -0
  63. package/dist/cross-instance-envelope.js +30 -0
  64. package/dist/cross-instance-envelope.js.map +1 -0
  65. package/dist/daemon.d.ts +491 -14
  66. package/dist/daemon.js +2121 -276
  67. package/dist/daemon.js.map +1 -1
  68. package/dist/doctor.d.ts +41 -0
  69. package/dist/doctor.js +267 -0
  70. package/dist/doctor.js.map +1 -0
  71. package/dist/fleet-context.d.ts +63 -0
  72. package/dist/fleet-context.js.map +1 -1
  73. package/dist/fleet-manager.d.ts +402 -11
  74. package/dist/fleet-manager.js +2594 -422
  75. package/dist/fleet-manager.js.map +1 -1
  76. package/dist/fleet-yaml-slim.d.ts +10 -0
  77. package/dist/fleet-yaml-slim.js +59 -0
  78. package/dist/fleet-yaml-slim.js.map +1 -0
  79. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +1 -0
  80. package/dist/general-knowledge/skills/model-discovery/SKILL.md +63 -16
  81. package/dist/general-knowledge/skills/session-management/SKILL.md +171 -8
  82. package/dist/instance-lifecycle.d.ts +114 -1
  83. package/dist/instance-lifecycle.js +404 -14
  84. package/dist/instance-lifecycle.js.map +1 -1
  85. package/dist/instructions.js +42 -4
  86. package/dist/instructions.js.map +1 -1
  87. package/dist/locale.js +1024 -320
  88. package/dist/locale.js.map +1 -1
  89. package/dist/login-flows.d.ts +91 -0
  90. package/dist/login-flows.js +170 -0
  91. package/dist/login-flows.js.map +1 -0
  92. package/dist/login-manager.d.ts +63 -0
  93. package/dist/login-manager.js +134 -0
  94. package/dist/login-manager.js.map +1 -0
  95. package/dist/network-family.d.ts +18 -0
  96. package/dist/network-family.js +20 -0
  97. package/dist/network-family.js.map +1 -0
  98. package/dist/outbound-handlers.d.ts +16 -1
  99. package/dist/outbound-handlers.js +174 -40
  100. package/dist/outbound-handlers.js.map +1 -1
  101. package/dist/outbound-schemas.d.ts +10 -3
  102. package/dist/outbound-schemas.js +10 -0
  103. package/dist/outbound-schemas.js.map +1 -1
  104. package/dist/pane-input-residue.d.ts +52 -0
  105. package/dist/pane-input-residue.js +107 -0
  106. package/dist/pane-input-residue.js.map +1 -0
  107. package/dist/process-memory.d.ts +6 -0
  108. package/dist/process-memory.js +45 -0
  109. package/dist/process-memory.js.map +1 -1
  110. package/dist/reply-dedup.d.ts +7 -8
  111. package/dist/reply-dedup.js +0 -0
  112. package/dist/reply-dedup.js.map +1 -1
  113. package/dist/restart-progress.d.ts +2 -0
  114. package/dist/restart-progress.js +3 -0
  115. package/dist/restart-progress.js.map +1 -1
  116. package/dist/scheduler/db.d.ts +12 -0
  117. package/dist/scheduler/db.js +59 -0
  118. package/dist/scheduler/db.js.map +1 -1
  119. package/dist/service-installer.d.ts +11 -0
  120. package/dist/service-installer.js +84 -18
  121. package/dist/service-installer.js.map +1 -1
  122. package/dist/settings-api.js +1 -1
  123. package/dist/settings-api.js.map +1 -1
  124. package/dist/setup-wizard.js +2 -2
  125. package/dist/setup-wizard.js.map +1 -1
  126. package/dist/spawn-gate.d.ts +30 -0
  127. package/dist/spawn-gate.js +79 -0
  128. package/dist/spawn-gate.js.map +1 -0
  129. package/dist/steer-capability.d.ts +1 -0
  130. package/dist/steer-capability.js +6 -0
  131. package/dist/steer-capability.js.map +1 -0
  132. package/dist/storm-window.d.ts +83 -0
  133. package/dist/storm-window.js +251 -0
  134. package/dist/storm-window.js.map +1 -0
  135. package/dist/tips.d.ts +44 -0
  136. package/dist/tips.js +355 -0
  137. package/dist/tips.js.map +1 -0
  138. package/dist/tmux-manager.d.ts +16 -0
  139. package/dist/tmux-manager.js +110 -25
  140. package/dist/tmux-manager.js.map +1 -1
  141. package/dist/topic-commands.d.ts +31 -7
  142. package/dist/topic-commands.js +342 -70
  143. package/dist/topic-commands.js.map +1 -1
  144. package/dist/transcript-sources.d.ts +35 -10
  145. package/dist/transcript-sources.js +190 -38
  146. package/dist/transcript-sources.js.map +1 -1
  147. package/dist/types.d.ts +6 -1
  148. package/dist/ui/dashboard.html +55 -32
  149. package/dist/ui/settings.html +200 -60
  150. package/dist/ui/view.html +147 -30
  151. package/dist/usage/format-rich.d.ts +1 -1
  152. package/dist/usage/format-rich.js +28 -24
  153. package/dist/usage/format-rich.js.map +1 -1
  154. package/dist/usage/i18n-keys.d.ts +7 -0
  155. package/dist/usage/i18n-keys.js +34 -0
  156. package/dist/usage/i18n-keys.js.map +1 -0
  157. package/dist/usage/i18n.d.ts +5 -0
  158. package/dist/usage/i18n.js +27 -0
  159. package/dist/usage/i18n.js.map +1 -0
  160. package/dist/usage/providers.d.ts +21 -0
  161. package/dist/usage/providers.js +153 -75
  162. package/dist/usage/providers.js.map +1 -1
  163. package/dist/usage/usage-api.d.ts +11 -3
  164. package/dist/usage/usage-api.js +61 -24
  165. package/dist/usage/usage-api.js.map +1 -1
  166. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- // Force IPv4 for all DNS lookups (fixes IPv6 timeout issues in corporate/WSL environments)
3
- import dns from "node:dns";
4
- dns.setDefaultResultOrder("ipv4first");
2
+ import { applyNetworkReliabilityDefaults } from "./network-family.js";
3
+ // Preserve Node's dual-stack Happy Eyeballs behavior, but avoid its 250ms
4
+ // premature-connect failure on WSL/VPN/high-load hosts (#658).
5
+ applyNetworkReliabilityDefaults();
5
6
  import { Command } from "commander";
6
7
  import { join, dirname } from "node:path";
7
8
  import { SchedulerDb } from "./scheduler/db.js";
@@ -19,6 +20,8 @@ import { getUpdateSelector, lookupTargetVersion, reportUpdateRestart, shouldSkip
19
20
  import { clearUpdateMarker, markUpdateInProgress, setUpdateProgressStage } from "./update-marker.js";
20
21
  import { acquireFleetLock, releaseProcessFleetLock, setProcessFleetLock } from "./fleet-lock.js";
21
22
  import { SYSTEMD_RESTART_TIMEOUT_MS } from "./service-installer.js";
23
+ import { loadRawFleetConfig } from "./config.js";
24
+ import { setLocale, t } from "./locale.js";
22
25
  /** Prefix tmux args with -L when socket isolation is active. */
23
26
  function tmuxArgs(args) {
24
27
  const socket = getTmuxSocketName();
@@ -28,6 +31,14 @@ const __filename = fileURLToPath(import.meta.url);
28
31
  const __dirname = dirname(__filename);
29
32
  const DATA_DIR = getAgendHome();
30
33
  const FLEET_CONFIG_PATH = join(DATA_DIR, "fleet.yaml");
34
+ // CLI commands run outside FleetManager. Honor an explicit fleet locale while
35
+ // keeping the historical English CLI default when config is missing/unset.
36
+ try {
37
+ const configuredLocale = loadRawFleetConfig(FLEET_CONFIG_PATH).defaults?.locale;
38
+ if (configuredLocale === "en" || configuredLocale === "zh-TW")
39
+ setLocale(configuredLocale);
40
+ }
41
+ catch { /* malformed or missing config: keep the English CLI default */ }
31
42
  const program = new Command();
32
43
  function claimFleetSingleton() {
33
44
  try {
@@ -549,7 +560,7 @@ function generateMermaid(rows) {
549
560
  }
550
561
  fleet
551
562
  .command("cleanup")
552
- .description("Remove orphaned instance directories not in fleet.yaml")
563
+ .description("Remove orphaned instance directories and stale instance files")
553
564
  .option("--dry-run", "List orphans without deleting")
554
565
  .action(async (opts) => {
555
566
  const { FleetManager } = await import("./fleet-manager.js");
@@ -818,15 +829,24 @@ backend
818
829
  // ── doctor mcp ──
819
830
  program
820
831
  .command("doctor")
821
- .description("Run fleet health diagnostics")
822
- .argument("[check]", "Specific check to run (mcp)")
832
+ .description("Diagnose backends, tmux, service, D-Bus, fleet, IPC, and network; use `mcp` for a deep MCP check")
833
+ .argument("[check]", "Optional deep check: mcp (omit for the full fleet diagnostic)")
823
834
  .action(async (check) => {
824
835
  if (check === "mcp") {
825
836
  await doctorMcp();
826
837
  }
827
- else {
838
+ else if (check) {
828
839
  console.log("Usage: agend doctor mcp");
829
840
  console.log(" mcp — Check fleet MCP health (IPC sockets, MCP configs, backends)");
841
+ process.exitCode = 2;
842
+ }
843
+ else {
844
+ const { inspectService } = await import("./service-installer.js");
845
+ const { collectDoctorReport, formatDoctorReport } = await import("./doctor.js");
846
+ const report = await collectDoctorReport(DATA_DIR, inspectService());
847
+ console.log(formatDoctorReport(report));
848
+ if (report.errors > 0)
849
+ process.exitCode = 1;
830
850
  }
831
851
  });
832
852
  async function doctorMcp() {
@@ -959,8 +979,8 @@ async function doctorMcp() {
959
979
  }
960
980
  backend
961
981
  .command("trust")
962
- .description("Pre-trust working directories for a backend (prevents trust dialogs)")
963
- .argument("<backend>", "Backend (gemini-cli)")
982
+ .description("Pre-trust Gemini CLI working directories (prevents trust dialogs)")
983
+ .argument("<backend>", "Backend (gemini-cli only)")
964
984
  .argument("[directories...]", "Directories to trust (defaults to all fleet instance dirs)")
965
985
  .action(async (backendName, directories) => {
966
986
  if (backendName !== "gemini-cli") {
@@ -1146,7 +1166,7 @@ access
1146
1166
  // === Update + Reload ===
1147
1167
  program
1148
1168
  .command("update")
1149
- .description("Update AgEnD to latest version and restart service")
1169
+ .description("Update AgEnD to the selected release and restart the fleet service")
1150
1170
  .option("--version <ver>", "Specific version to install")
1151
1171
  .option("--beta", "Install beta version")
1152
1172
  .option("--force", "Force reinstall and restart even when already up to date")
@@ -1273,7 +1293,10 @@ program
1273
1293
  if (agendPath) {
1274
1294
  try {
1275
1295
  // Use the NEW binary to install service (old binary's templates may be deleted)
1276
- const installResult = spawnSync(agendPath, ["install"], { encoding: "utf-8", timeout: 15000 });
1296
+ // `agend install` activates by default. During update the existing
1297
+ // restart stage owns stop/start and progress reporting, so only refresh
1298
+ // the service file here to avoid restarting the fleet twice.
1299
+ const installResult = spawnSync(agendPath, ["install", "--no-activate"], { encoding: "utf-8", timeout: 15000 });
1277
1300
  if (installResult.status === 0) {
1278
1301
  console.log(` ✓ Service updated`);
1279
1302
  }
@@ -1326,7 +1349,7 @@ program
1326
1349
  });
1327
1350
  program
1328
1351
  .command("reload")
1329
- .description("Hot-reload fleet config (re-read fleet.yaml, start new instances)")
1352
+ .description("Hot-reload fleet.yaml and reconcile instance configuration")
1330
1353
  .action(async () => {
1331
1354
  const pidPath = join(DATA_DIR, "fleet.pid");
1332
1355
  if (!existsSync(pidPath)) {
@@ -1346,8 +1369,9 @@ program
1346
1369
  // === Install/Uninstall ===
1347
1370
  program
1348
1371
  .command("install")
1349
- .description("Install as system service")
1350
- .option("--activate", "Stop manual fleet and load the service immediately")
1372
+ .description("Install the AgEnD system service and start it by default")
1373
+ .option("--activate", "Deprecated; service activation is now the default")
1374
+ .option("--no-activate", "Write the service file without activating it")
1351
1375
  .action(async (opts) => {
1352
1376
  const { installService, activateService, detectPlatform } = await import("./service-installer.js");
1353
1377
  const execPath = process.argv[1];
@@ -1358,38 +1382,86 @@ program
1358
1382
  workingDirectory: DATA_DIR,
1359
1383
  logPath: join(DATA_DIR, "fleet.log"),
1360
1384
  });
1361
- console.log(`Service installed at: ${svcPath}`);
1362
- if (opts.activate) {
1385
+ console.log(t("install.path", svcPath));
1386
+ if (opts.activate !== false) {
1363
1387
  const pidPath = join(DATA_DIR, "fleet.pid");
1364
- activateService(svcPath, pidPath);
1365
- console.log("Service activated.");
1366
- }
1367
- else {
1368
- const plat = detectPlatform();
1369
- if (plat === "macos") {
1370
- console.log(`Run: launchctl load ${svcPath}`);
1388
+ try {
1389
+ activateService(svcPath, pidPath);
1390
+ console.log(t("install.running"));
1371
1391
  }
1372
- else {
1373
- console.log(`Run: systemctl --user enable --now com.agend.fleet`);
1392
+ catch (err) {
1393
+ console.error(t("install.start_failed", err.message));
1394
+ const plat = detectPlatform();
1395
+ if (plat === "macos") {
1396
+ console.error(`Run manually: launchctl load ${svcPath}`);
1397
+ }
1398
+ else {
1399
+ console.error("Run manually: systemctl --user daemon-reload && systemctl --user enable --now com.agend.fleet");
1400
+ }
1401
+ process.exitCode = 1;
1374
1402
  }
1375
1403
  }
1404
+ else {
1405
+ console.log(t("install.updated"));
1406
+ }
1376
1407
  });
1377
1408
  program
1378
1409
  .command("uninstall")
1379
- .description("Remove system service")
1380
- .action(async () => {
1381
- const { uninstallService } = await import("./service-installer.js");
1382
- const removed = uninstallService("com.agend.fleet");
1383
- if (removed) {
1384
- console.log("Service uninstalled");
1410
+ .description("Remove the AgEnD system service")
1411
+ .option("-f, --force", "Skip confirmation (for CI/automation)")
1412
+ .action(async (opts) => {
1413
+ const { inspectService, isServiceRemovalConfirmed, uninstallService } = await import("./service-installer.js");
1414
+ const service = inspectService("com.agend.fleet");
1415
+ if (!service.installed) {
1416
+ console.log(t("uninstall.none"));
1417
+ return;
1385
1418
  }
1386
- else {
1387
- console.log("No service found to uninstall");
1419
+ const state = (value, yes, no) => value == null ? t("uninstall.unknown") : value ? yes : no;
1420
+ console.log(t("uninstall.found"));
1421
+ console.log(` ${t("uninstall.manager", service.manager ?? t("uninstall.unknown"))}`);
1422
+ console.log(` ${t("uninstall.path", service.path ?? t("uninstall.unknown"))}`);
1423
+ console.log(` ${t("uninstall.enabled", state(service.enabled, t("uninstall.yes"), t("uninstall.no")))}`);
1424
+ console.log(` ${t("uninstall.active", state(service.active, t("uninstall.running"), t("uninstall.inactive")))}`);
1425
+ if (!opts.force && !process.stdin.isTTY) {
1426
+ console.log(t("uninstall.non_interactive"));
1427
+ process.exitCode = 1;
1428
+ return;
1429
+ }
1430
+ if (!opts.force) {
1431
+ const { createInterface } = await import("node:readline/promises");
1432
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1433
+ let answer = "";
1434
+ try {
1435
+ answer = await rl.question(t("uninstall.confirm"));
1436
+ }
1437
+ catch {
1438
+ console.log(t("uninstall.cancelled_force"));
1439
+ return;
1440
+ }
1441
+ finally {
1442
+ rl.close();
1443
+ }
1444
+ if (!isServiceRemovalConfirmed(answer)) {
1445
+ console.log(t("uninstall.cancelled"));
1446
+ return;
1447
+ }
1448
+ }
1449
+ try {
1450
+ if (uninstallService("com.agend.fleet")) {
1451
+ console.log(t("uninstall.removed"));
1452
+ }
1453
+ else {
1454
+ console.log(t("uninstall.none"));
1455
+ }
1456
+ }
1457
+ catch (err) {
1458
+ console.error(t("uninstall.failed", err.message));
1459
+ process.exitCode = 1;
1388
1460
  }
1389
1461
  });
1390
1462
  program
1391
1463
  .command("stop")
1392
- .description("Stop the AgEnD service")
1464
+ .description("Stop the AgEnD service or detached fleet")
1393
1465
  .action(async () => {
1394
1466
  const { getServicePath, stopService } = await import("./service-installer.js");
1395
1467
  if (!getServicePath()) {
@@ -1433,7 +1505,7 @@ program
1433
1505
  });
1434
1506
  program
1435
1507
  .command("start")
1436
- .description("Start the AgEnD service (must be installed first)")
1508
+ .description("Start the AgEnD service, or start a detached fleet if no service is installed")
1437
1509
  .action(async () => {
1438
1510
  const { getServicePath, getSystemServicePath, startService, startSystemService } = await import("./service-installer.js");
1439
1511
  const userServicePath = getServicePath();
@@ -1589,7 +1661,7 @@ program
1589
1661
  });
1590
1662
  program
1591
1663
  .command("quickstart")
1592
- .description("Quick 3-step setup: detect backend, create bot, connect group")
1664
+ .description("Guided setup for a Backend, chat channel, projects, and fleet service")
1593
1665
  .action(async () => {
1594
1666
  const { runQuickstart } = await import("./quickstart.js");
1595
1667
  await runQuickstart();
@@ -1862,7 +1934,7 @@ schedule
1862
1934
  });
1863
1935
  schedule
1864
1936
  .command("trigger")
1865
- .description("Manually trigger a schedule")
1937
+ .description("Show how to manually trigger a schedule")
1866
1938
  .argument("<id>", "Schedule ID")
1867
1939
  .action((id) => {
1868
1940
  console.log("Manual trigger requires fleet manager running. Use the Telegram interface instead.");
@@ -1959,46 +2031,8 @@ async function resolveInstance(query, config) {
1959
2031
  }
1960
2032
  return match;
1961
2033
  }
1962
- /** Get total RSS (KB) for a process and all its descendants. */
1963
- function getTreeRssKb(pid, depth = 0) {
1964
- if (depth > 10)
1965
- return 0;
1966
- if (!Number.isInteger(pid) || pid <= 0)
1967
- return 0;
1968
- let total = 0;
1969
- try {
1970
- // Try PSS from smaps_rollup (more accurate — excludes shared page double-counting)
1971
- const smaps = readFileSync(`/proc/${pid}/smaps_rollup`, "utf-8");
1972
- const match = smaps.match(/^Pss:\s+(\d+)/m);
1973
- if (match) {
1974
- total += parseInt(match[1], 10);
1975
- }
1976
- else {
1977
- throw new Error("no Pss line");
1978
- }
1979
- }
1980
- catch {
1981
- // Fallback to RSS via ps
1982
- try {
1983
- const rss = parseInt(execFileSync("ps", ["-o", "rss=", "-p", String(pid)], { stdio: "pipe" }).toString().trim(), 10);
1984
- if (!isNaN(rss))
1985
- total += rss;
1986
- }
1987
- catch {
1988
- return 0;
1989
- }
1990
- }
1991
- try {
1992
- const children = execFileSync("pgrep", ["-P", String(pid)], { stdio: "pipe" }).toString().trim();
1993
- for (const line of children.split("\n")) {
1994
- const childPid = parseInt(line, 10);
1995
- if (!isNaN(childPid))
1996
- total += getTreeRssKb(childPid, depth + 1);
1997
- }
1998
- }
1999
- catch { /* no children */ }
2000
- return total;
2001
- }
2034
+ // Imported from process-memory.ts — see that module for implementation
2035
+ import { getTreeRssKb } from "./process-memory.js";
2002
2036
  function getInstanceStatusStandalone(name) {
2003
2037
  if (hasPausedMarker(join(DATA_DIR, "instances", name)))
2004
2038
  return "paused";
@@ -2521,6 +2555,10 @@ program
2521
2555
  if (procStatus === "stopped") {
2522
2556
  return { name, status: "stopped", issues: ["Not running"], general: isGeneral };
2523
2557
  }
2558
+ // Paused instances have no tmux window by design — skip running-only checks
2559
+ if (procStatus === "paused") {
2560
+ return { name, status: "paused", issues: [], general: isGeneral };
2561
+ }
2524
2562
  // Tmux window alive?
2525
2563
  if (!tmuxWindows.has(name))
2526
2564
  issues.push("Tmux window missing");
@@ -2552,10 +2590,11 @@ program
2552
2590
  });
2553
2591
  // Fleet classification
2554
2592
  const crashed = results.filter(r => r.status === "crash");
2555
- const problems = results.filter(r => r.status !== "ok" && r.status !== "idle" && r.status !== "stopped");
2593
+ const problems = results.filter(r => r.status !== "ok" && r.status !== "idle" && r.status !== "stopped" && r.status !== "paused");
2556
2594
  const healthy = results.filter(r => r.status === "ok" || r.status === "idle");
2557
2595
  const stopped = results.filter(r => r.status === "stopped");
2558
- const generalDown = results.some(r => r.general && r.status !== "ok" && r.status !== "idle");
2596
+ const paused = results.filter(r => r.status === "paused");
2597
+ const generalDown = results.some(r => r.general && r.status !== "ok" && r.status !== "idle" && r.status !== "paused");
2559
2598
  let classification;
2560
2599
  if (generalDown || crashed.length > 0)
2561
2600
  classification = "unhealthy";
@@ -2570,7 +2609,8 @@ program
2570
2609
  }
2571
2610
  if (opts.quiet) {
2572
2611
  const icon = classification === "healthy" ? "✓" : classification === "degraded" ? "⚠" : "✗";
2573
- console.log(`${icon} ${classification}: ${healthy.length}/${names.length} healthy${problems.length > 0 ? `, ${problems.length} issues` : ""}`);
2612
+ const pausedPart = paused.length > 0 ? `, ${paused.length} paused` : "";
2613
+ console.log(`${icon} ${classification}: ${healthy.length}/${names.length} healthy${problems.length > 0 ? `, ${problems.length} issues` : ""}${pausedPart}`);
2574
2614
  process.exit(exitCode);
2575
2615
  }
2576
2616
  // Full output
@@ -2578,9 +2618,10 @@ program
2578
2618
  const upH = Math.floor(uptime / 3600);
2579
2619
  const upM = Math.floor((uptime % 3600) / 60);
2580
2620
  console.log(`Fleet: ${fleetIcon} ${fleetUp ? `running (uptime ${upH}h ${upM}m, PID ${fleetPid})` : "not running"}`);
2581
- console.log(`Instances: ${healthy.length} healthy, ${problems.length + crashed.length} issues, ${stopped.length} stopped\n`);
2582
- // Only show instances with problems
2583
- const unhealthy = results.filter(r => r.issues.length > 0 && r.status !== "stopped");
2621
+ const pausedPart = paused.length > 0 ? `, ${paused.length} paused` : "";
2622
+ console.log(`Instances: ${healthy.length} healthy, ${problems.length + crashed.length} issues, ${stopped.length} stopped${pausedPart}\n`);
2623
+ // Only show instances with problems (paused is not a problem)
2624
+ const unhealthy = results.filter(r => r.issues.length > 0 && r.status !== "stopped" && r.status !== "paused");
2584
2625
  if (unhealthy.length === 0) {
2585
2626
  console.log("\x1b[32m✓ All instances healthy\x1b[0m");
2586
2627
  }